Files
smart-electronics-frontend/features/cart/components/OrderSummary.tsx
@jcarymuhammedow f32e7538e1 fixed some bugs
2026-02-05 19:36:12 +05:00

346 lines
10 KiB
TypeScript

"use client";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Textarea } from "@/components/ui/textarea";
import { Separator } from "@/components/ui/separator";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Input } from "@/components/ui/input";
import DeliveryTypeSelector from "./DeliveryTypeSelector";
import { useTranslations } from "next-intl";
import type { DeliveryType, PaymentType, Province } from "@/lib/types/api";
import { useState } from "react";
interface OrderBillingItem {
title: string;
value: string;
}
interface OrderBilling {
body: OrderBillingItem[];
footer: {
title: string;
value: string;
};
}
interface OrderSummaryProps {
order: {
id: number;
billing: OrderBilling;
};
paymentType: PaymentType | null;
deliveryType: DeliveryType;
selectedRegion: string;
selectedProvince: number | null;
note: string;
regionGroups: Record<string, Province[]>;
availableRegions: string[];
paymentTypes: PaymentType[];
phone: string;
name: string;
lastName: string;
onPhoneChange: (phone: string) => void;
onNameChange: (name: string) => void;
onLastNameChange: (lastName: string) => void;
onPaymentTypeChange: (type: PaymentType) => void;
onDeliveryTypeChange: (type: DeliveryType) => void;
onRegionChange: (regionCode: string) => void;
onProvinceChange: (provinceId: number) => void;
onNoteChange: (note: string) => void;
onCompleteOrder: () => void;
isLoading: boolean;
}
export default function OrderSummary({
order,
paymentType,
deliveryType,
selectedRegion,
selectedProvince,
note,
regionGroups,
availableRegions,
paymentTypes,
phone,
name,
lastName,
onPhoneChange,
onNameChange,
onLastNameChange,
onPaymentTypeChange,
onDeliveryTypeChange,
onRegionChange,
onProvinceChange,
onNoteChange,
onCompleteOrder,
isLoading,
}: OrderSummaryProps) {
const t = useTranslations();
const [showValidation, setShowValidation] = useState(false);
const provincesForSelectedRegion = selectedRegion
? regionGroups[selectedRegion] || []
: [];
const phoneDigits = phone.replace(/\D/g, "");
const isPhoneValid = phoneDigits.length === 11;
const isFormValid =
selectedRegion &&
selectedProvince &&
paymentType &&
isPhoneValid &&
name.trim() !== "" &&
lastName.trim() !== "";
const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const input = e.target.value;
const prefix = "+993 ";
if (input.length < prefix.length) {
onPhoneChange(prefix);
return;
}
const digitsOnly = input.substring(prefix.length).replace(/\D/g, "");
const limitedDigits = digitsOnly.substring(0, 8);
let formattedPhone = prefix;
if (limitedDigits.length > 0) {
formattedPhone += limitedDigits.substring(0, 2);
if (limitedDigits.length > 2) {
formattedPhone += " " + limitedDigits.substring(2);
}
}
onPhoneChange(formattedPhone);
};
const handleCompleteOrderClick = () => {
setShowValidation(true);
if (isFormValid) {
onCompleteOrder();
}
};
return (
<Card className="w-full md:w-[420px] p-8 rounded-lg border border-gray-200 shadow-lg h-fit sticky top-20">
{/* Customer Information */}
<div className="mb-8">
<h3 className="text-xl font-bold mb-5 text-gray-900">
{t("customer_information")}
</h3>
<div className="space-y-5">
<div>
<Label className="text-sm font-semibold mb-2 block text-gray-700">
{t("name")}
</Label>
<Input
type="text"
value={name}
onChange={(e) => onNameChange(e.target.value)}
placeholder={t("name")}
className={`rounded-[10px] h-12 border-2 transition-colors ${
showValidation && name.trim() === ""
? "border-red-500"
: "border-gray-200 focus:border-gray-900"
}`}
/>
{showValidation && name.trim() === "" && (
<p className="text-xs text-red-500 mt-2 font-medium">
{t("requiredField")}
</p>
)}
</div>
<div>
<Label className="text-sm font-semibold mb-2 block text-gray-700">
{t("last_name")}
</Label>
<Input
type="text"
value={lastName}
onChange={(e) => onLastNameChange(e.target.value)}
placeholder={t("last_name")}
className={`rounded-[10px] h-12 border-2 transition-colors ${
showValidation && lastName.trim() === ""
? "border-red-500"
: "border-gray-200 focus:border-gray-900"
}`}
/>
{showValidation && lastName.trim() === "" && (
<p className="text-xs text-red-500 mt-2 font-medium">
{t("requiredField")}
</p>
)}
</div>
<div>
<Label className="text-sm font-semibold mb-2 block text-gray-700">
{t("phone")}
</Label>
<Input
type="tel"
value={phone}
onChange={handlePhoneChange}
placeholder="+993 61 097651"
className={`rounded-[10px] h-12 border-2 transition-colors ${
showValidation && !isPhoneValid
? "border-red-500"
: "border-gray-200 focus:border-gray-900"
}`}
/>
{showValidation && !isPhoneValid && (
<p className="text-xs text-red-500 mt-2 font-medium">
{t("requiredField")}
</p>
)}
</div>
</div>
</div>
{/* Region Selection */}
<div className="mb-8">
<Label className="text-xl font-bold mb-4 block text-gray-900">
{t("choose_region")}
</Label>
<RadioGroup
value={selectedRegion}
onValueChange={(value) => {
onRegionChange(value);
onProvinceChange(null as any);
}}
className="flex flex-wrap gap-4"
>
{availableRegions.map((regionCode) => (
<div key={regionCode} className="flex items-center space-x-3">
<RadioGroupItem
value={regionCode}
id={`region-${regionCode}`}
className={`border-2 h-5 w-5 ${
showValidation && !selectedRegion
? "border-red-500"
: "border-gray-400"
} data-[state=checked]:border-gray-900 data-[state=checked]:bg-gray-900`}
/>
<Label
htmlFor={`region-${regionCode}`}
className="cursor-pointer uppercase font-semibold text-gray-700 hover:text-gray-900 transition-colors"
>
{regionCode}
</Label>
</div>
))}
</RadioGroup>
{showValidation && !selectedRegion && (
<p className="text-xs text-red-500 mt-3 font-medium">
{t("requiredField")}
</p>
)}
</div>
{/* Province Selection */}
{selectedRegion && provincesForSelectedRegion.length > 0 && (
<div className="mb-8">
<Label className="text-xl font-bold mb-4 block text-gray-900">
{t("choose_address")}
</Label>
<Select
value={selectedProvince?.toString() || ""}
onValueChange={(value) => onProvinceChange(parseInt(value))}
>
<SelectTrigger
className={`rounded-[10px] h-12 w-full border-2 font-medium ${
showValidation && !selectedProvince
? "border-red-500"
: "border-gray-200 hover:border-gray-900"
}`}
>
<SelectValue placeholder={t("choose_address")} />
</SelectTrigger>
<SelectContent className="rounded-lg">
{provincesForSelectedRegion.map((province) => (
<SelectItem
key={province.id}
value={province.id.toString()}
className="rounded-lg"
>
{province.name}
</SelectItem>
))}
</SelectContent>
</Select>
{showValidation && !selectedProvince && (
<p className="text-xs text-red-500 mt-3 font-medium">
{t("requiredField")}
</p>
)}
</div>
)}
{/* Note */}
<div className="mb-8">
<Label className="text-xl font-bold mb-4 block text-gray-900">
{t("note")}
</Label>
<Textarea
value={note}
onChange={(e) => onNoteChange(e.target.value)}
className="rounded-[10px] resize-none border-2 border-gray-200 focus:border-gray-900 transition-colors"
rows={3}
placeholder={t("note")}
/>
</div>
{/* Billing */}
<div className="space-y-3 mb-6">
{order.billing.body.map((item, index) => (
<div
key={index}
className="flex justify-between text-base font-semibold text-gray-700"
>
<span>{item.title}:</span>
<span>{item.value}</span>
</div>
))}
</div>
<Separator className="my-6 bg-gray-200" />
<div className="flex justify-between items-center mb-8">
<span className="text-xl font-bold text-gray-900">
{order.billing.footer.title}:
</span>
<span className="text-2xl font-bold text-emerald-600">
{order.billing.footer.value}
</span>
</div>
<Button
onClick={handleCompleteOrderClick}
disabled={isLoading}
className="w-full rounded-[10px] cursor-pointer bg-gradient-to-r from-gray-900 to-gray-800 hover:from-gray-800 hover:to-gray-700 h-14 text-lg font-bold disabled:opacity-50 shadow-md hover:shadow-lg transition-all duration-300"
size="lg"
>
{isLoading ? (
<div className="flex items-center gap-2">
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
<span>{t("order")}...</span>
</div>
) : (
t("order")
)}
</Button>
</Card>
);
}