diff --git a/package-lock.json b/package-lock.json index 26370c7..aa3faa0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-radio-group": "^1.3.7", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", "@tanstack/react-query": "^5.84.1", @@ -17,7 +18,7 @@ "clsx": "^2.1.1", "embla-carousel-react": "^8.6.0", "iconsax-react": "^0.0.8", - "lucide-react": "^0.536.0", + "lucide-react": "^0.539.0", "next": "15.4.5", "react": "19.1.0", "react-dom": "19.1.0", @@ -1183,6 +1184,69 @@ } } }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.7.tgz", + "integrity": "sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-separator": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", @@ -1224,6 +1288,21 @@ } } }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-controllable-state": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", @@ -1276,6 +1355,39 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -4949,9 +5061,9 @@ } }, "node_modules/lucide-react": { - "version": "0.536.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.536.0.tgz", - "integrity": "sha512-2PgvNa9v+qz4Jt/ni8vPLt4jwoFybXHuubQT8fv4iCW5TjDxkbZjNZZHa485ad73NSEn/jdsEtU57eE1g+ma8A==", + "version": "0.539.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.539.0.tgz", + "integrity": "sha512-VVISr+VF2krO91FeuCrm1rSOLACQUYVy7NQkzrOty52Y8TlTPcXcMdQFj9bYzBgXbWCiywlwSZ3Z8u6a+6bMlg==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" diff --git a/package.json b/package.json index 998aab8..eb9208b 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-radio-group": "^1.3.7", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", "@tanstack/react-query": "^5.84.1", @@ -18,7 +19,7 @@ "clsx": "^2.1.1", "embla-carousel-react": "^8.6.0", "iconsax-react": "^0.0.8", - "lucide-react": "^0.536.0", + "lucide-react": "^0.539.0", "next": "15.4.5", "react": "19.1.0", "react-dom": "19.1.0", diff --git a/src/app/cart/components/CartItem.tsx b/src/app/cart/components/CartItem.tsx new file mode 100644 index 0000000..6e40cbf --- /dev/null +++ b/src/app/cart/components/CartItem.tsx @@ -0,0 +1,83 @@ +"use client"; + +import CartControls from '@/components/CartControls' +import { clx } from '@/helpers/utils'; +import { ArchiveTick, Shop, TruckFast } from 'iconsax-react' +import Image from 'next/image' +import { FC } from 'react' + +type Props = { + noBorder?: boolean +} + +const CartItem: FC = (props) => { + + const { noBorder } = props + + return ( +
+ + +
+
+
+ گوشی موبایل سامسونگ مدل Galaxy S24 Ultra دو سیم کارت ظرفیت 1 ترابایت و رم 12 گیگابایت - ویتنام +
+
+ 70,400,000 تومان +
+
+
+
+
+
+
+
مشکی
+
+ +
+
+ +
+
فروشنده فروشگاه سندس
+
+ +
+
+ +
+
گارانتی اصالت و سلامت فیزیکی کالا
+
+ +
+
+
+ +
+
ارسال از سندس حداکثر پس از 3 روز کاری
+
+ + null} + onRemove={() => null} + quantity={1} + /> + +
+
+ +
+
+ ) +} + +export default CartItem \ No newline at end of file diff --git a/src/app/cart/components/DiscountCard.tsx b/src/app/cart/components/DiscountCard.tsx new file mode 100644 index 0000000..2388885 --- /dev/null +++ b/src/app/cart/components/DiscountCard.tsx @@ -0,0 +1,18 @@ +import { Button } from '@/components/ui/button' +import { FC } from 'react' + +const DiscountCard: FC = () => { + return ( +
+
+ کد تخفیف دارید؟ +
+ + +
+ ) +} + +export default DiscountCard \ No newline at end of file diff --git a/src/app/cart/components/TitleBack.tsx b/src/app/cart/components/TitleBack.tsx new file mode 100644 index 0000000..e13de42 --- /dev/null +++ b/src/app/cart/components/TitleBack.tsx @@ -0,0 +1,19 @@ +import { ArrowRight } from 'iconsax-react' +import { FC } from 'react' + +type Props = { + title: string +} + +const TitleBack: FC = (props) => { + const { title } = props + + return ( +
+ +
{title}
+
+ ) +} + +export default TitleBack \ No newline at end of file diff --git a/src/app/cart/page.tsx b/src/app/cart/page.tsx new file mode 100644 index 0000000..6bc019f --- /dev/null +++ b/src/app/cart/page.tsx @@ -0,0 +1,50 @@ +import { Button } from "@/components/ui/button" +import CartItem from "@/app/cart/components/CartItem" +import CartSummary from "@/components/CartSummary" +import withLayout from "@/hoc/withLayout" +import { Trash } from "iconsax-react" +import { NextPage } from "next" + +const Cart: NextPage = () => { + return ( +
+
+
+
+ سبد خرید +
+
+ 10 +
+
+ + +
+ +
+
+ + +
+ +
+
+ ) +} + +export default withLayout(Cart) \ No newline at end of file diff --git a/src/app/cart/payment/page.tsx b/src/app/cart/payment/page.tsx new file mode 100644 index 0000000..01012ef --- /dev/null +++ b/src/app/cart/payment/page.tsx @@ -0,0 +1,48 @@ +import CartSummary from '@/components/CartSummary' +import withLayout from '@/hoc/withLayout' +import { NextPage } from 'next' +import TitleBack from '../components/TitleBack' +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import DiscountCard from '../components/DiscountCard' + +const CartPayment: NextPage = () => { + return ( +
+ +
+
+ + +
+
انتخاب روش پرداخت
+ +
+ +
+ +
زین پال
+
+
+ +
زین پال
+
+
+
+
+
+ +
+
+ ) +} + +export default withLayout(CartPayment) \ No newline at end of file diff --git a/src/app/cart/shipping/page.tsx b/src/app/cart/shipping/page.tsx new file mode 100644 index 0000000..0aa20ae --- /dev/null +++ b/src/app/cart/shipping/page.tsx @@ -0,0 +1,49 @@ +import CartSummary from '@/components/CartSummary' +import withLayout from '@/hoc/withLayout' +import { NextPage } from 'next' +import CartItem from '../components/CartItem' +import TitleBack from '../components/TitleBack' +import { Location } from 'iconsax-react' + +const CartShipping: NextPage = () => { + return ( +
+ +
+
+ +
+
+ +
+
+ آدرس تحویل سفارش +
+
+ اراک خیابان امام حسن غریبی خیابان 123 +
+
حمید ضرغامی
+
+
+
ویرایش
+
+
+ + +
+
+ +
+
+ ) +} + +export default withLayout(CartShipping) \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 082f07f..f8f766b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -184,3 +184,6 @@ textarea.place-black::placeholder { .swiper-pagination { z-index: 1 !important; } +div { + direction: rtl; +} diff --git a/src/components/CartControls.tsx b/src/components/CartControls.tsx new file mode 100644 index 0000000..ba771e9 --- /dev/null +++ b/src/components/CartControls.tsx @@ -0,0 +1,64 @@ +"use client"; + +import { Add, Minus, Trash } from "iconsax-react"; +import React from "react"; + +type CartControlsProps = { + quantity: number; + onChange: (next: number) => void; + onRemove: () => void; + min?: number; + max?: number; + className?: string; +}; + +export default function CartControls(props: CartControlsProps) { + const { quantity, onChange, onRemove, min = 1, max, className } = props; + + const canDecrement = quantity > min; + const canIncrement = typeof max === "number" ? quantity < max : true; + + const orange = "blue"; + + return ( +
+ + +
+ + + {quantity} + + + +
+ + +
+ ); +} + + diff --git a/src/components/CartSummary.tsx b/src/components/CartSummary.tsx new file mode 100644 index 0000000..753ed7d --- /dev/null +++ b/src/components/CartSummary.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { NumberFormat } from "@/config/func"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; +import { FC } from "react"; + +type CartSummaryProps = { + itemsCount: number; + itemsPrice: number; + discount: number; + total: number; + onConfirm?: () => void; + confirmHref?: string; + confirmLabel?: string; + onDownloadInvoice?: () => void; + className?: string; +}; + +const Row: FC<{ label: string; value: string | number; emphasize?: boolean }> = ( + { label, value, emphasize } +) => { + return ( +
+
{label}
+
+ {typeof value === "number" ? NumberFormat(value) : value} +
+
+ ); +}; + +const CartSummary: FC = (props) => { + const { itemsCount, itemsPrice, discount, total, onConfirm, onDownloadInvoice, className, confirmHref, confirmLabel } = props; + + return ( +
+
+
تعداد کالاها
+
{NumberFormat(itemsCount)} محصول
+
+ +
+ + + +
+ +
+ + +
+
+ ); +}; + +export default CartSummary; + + diff --git a/src/components/ui/radio-group.tsx b/src/components/ui/radio-group.tsx new file mode 100644 index 0000000..5e6778c --- /dev/null +++ b/src/components/ui/radio-group.tsx @@ -0,0 +1,45 @@ +"use client" + +import * as React from "react" +import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" +import { CircleIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function RadioGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function RadioGroupItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ) +} + +export { RadioGroup, RadioGroupItem }