build and format

This commit is contained in:
2026-02-17 21:36:14 +03:30
parent 53484a07f8
commit 2d7b48b5b7
9 changed files with 13138 additions and 10312 deletions

View File

@@ -16,9 +16,9 @@
"main_title_line2": "سریع و مطمئن",
"contact_us": "تماس با ما"
},
"Categories" : {
"title" : "دسته بندی ها",
"description" : "از بین صدها مرکز زیبایی، دقیقا همونی که میخای رو پیدا کن."
"Categories": {
"title": "دسته بندی ها",
"description": "از بین صدها مرکز زیبایی، دقیقا همونی که میخای رو پیدا کن."
},
"Application": {
"title": "دانلود نرم افزار تلفن همراه 141",

23041
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,15 @@
"use client"
"use client";
import { Card, CardContent } from "@/components/UI/card"
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/UI/Carousel"
import { categories } from "@/data/mockCategories"
import Image from "next/image"
import {easeOut, motion} from "framer-motion"
import { Card, CardContent } from "@/components/UI/card";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/UI/Carousel";
import { categories } from "@/data/mockCategories";
import Image from "next/image";
import { easeOut, motion } from "framer-motion";
type Props = {
title: string
description: string
}
title: string;
description: string;
};
const containerVariants = {
hidden: {},
@@ -24,7 +18,7 @@ const containerVariants = {
staggerChildren: 0.08,
},
},
}
};
const itemVariants = {
hidden: {
@@ -41,59 +35,40 @@ const itemVariants = {
ease: easeOut,
},
},
}
};
export function CarouselComponent({ title, description }: Props) {
return (
<div className="relative w-full">
<Carousel dir="rtl" className="w-full" opts={{ direction: "rtl" }}>
{/* 🔥 Animated Header */}
<motion.div
initial={{ opacity: 0, x: 40 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
className="flex items-center justify-between w-full h-full my-5"
className="my-5 flex h-full w-full items-center justify-between"
>
<span className="flex items-center justify-center gap-x-2">
<motion.div
initial={{ height: 0 }}
animate={{ height: 64 }}
transition={{ duration: 0.5 }}
className="bg-primary-100 w-4 rounded-2xl rounded-tr-none"
/>
<div>
<p className="text-lg font-semibold">{title}</p>
<p className="text-muted-foreground">{description}</p>
</div>
</span>
<span className="flex items-center justify-center gap-x-2">
<motion.div
initial={{ height: 0 }}
animate={{ height: 64 }}
transition={{ duration: 0.5 }}
className="bg-primary-100 w-4 rounded-2xl rounded-tr-none"
/>
<div>
<p className="text-lg font-semibold">{title}</p>
<p className="text-muted-foreground">{description}</p>
</div>
</span>
</motion.div>
{/* 🔥 Animated Carousel Items */}
<motion.div
variants={containerVariants}
initial="hidden"
animate="show"
>
<motion.div variants={containerVariants} initial="hidden" animate="show">
<CarouselContent>
{categories.map((category) => (
<CarouselItem
key={category.id}
className="
md:basis-1/5
lg:basis-1/7
relative
after:content-['']
after:absolute
after:top-1/2
after:-translate-y-1/2
after:left-2
after:w-px
after:h-[50%]
after:bg-border
last:after:hidden
"
className="after:bg-border relative after:absolute after:top-1/2 after:left-2 after:h-[50%] after:w-px after:-translate-y-1/2 after:content-[''] last:after:hidden md:basis-1/5 lg:basis-1/7"
>
<motion.div variants={itemVariants} className="p-1">
<motion.div
@@ -103,8 +78,8 @@ export function CarouselComponent({ title, description }: Props) {
}}
transition={{ type: "spring", stiffness: 300 }}
>
<Card dir="rtl" className="transition-shadow hover:shadow-xl cursor-pointer">
<CardContent className="flex flex-col gap-3 h-30 items-center justify-center p-6">
<Card dir="rtl" className="cursor-pointer transition-shadow hover:shadow-xl">
<CardContent className="flex h-30 flex-col items-center justify-center gap-3 p-6">
<motion.div
whileHover={{ rotate: 5 }}
transition={{ type: "spring", stiffness: 200 }}
@@ -118,9 +93,7 @@ export function CarouselComponent({ title, description }: Props) {
/>
</motion.div>
<p className="font-semibold text-text-muted">
{category.title}
</p>
<p className="text-text-muted font-semibold">{category.title}</p>
</CardContent>
</Card>
</motion.div>
@@ -144,8 +117,7 @@ export function CarouselComponent({ title, description }: Props) {
<CarouselNext />
</motion.div>
</motion.div>
</Carousel>
</div>
)
);
}

View File

@@ -1,17 +1,14 @@
import { CarouselComponent } from "./CarouselComponent"
import { useTranslations } from "next-intl"
import { CarouselComponent } from "./CarouselComponent";
import { useTranslations } from "next-intl";
const Categories = () => {
const t = useTranslations("Categories")
const t = useTranslations("Categories");
return (
<div className="container mx-auto my-16">
<CarouselComponent
title={t("title")}
description={t("description")}
/>
<CarouselComponent title={t("title")} description={t("description")} />
</div>
)
}
);
};
export default Categories
export default Categories;

View File

@@ -1,4 +1,4 @@
"use client"
"use client";
import { MarkerIcon, MarkerPreviewIcon } from "@/assets";
import { useMap } from "@vis.gl/react-maplibre";
import maplibregl from "maplibre-gl";
@@ -20,7 +20,7 @@ type Props = {
};
const MarkerPosition = ({ preview = false, onChange }: Props) => {
const { current : homePageMap} = useMap();
const { current: homePageMap } = useMap();
const markerRef = useRef<maplibregl.Marker | null>(null);
useEffect(() => {

View File

@@ -1,108 +1,106 @@
"use client"
"use client";
import * as React from "react"
import useEmblaCarousel, {
type UseEmblaCarouselType,
} from "embla-carousel-react"
import * as React from "react";
import useEmblaCarousel, { type UseEmblaCarouselType } from "embla-carousel-react";
import { cn } from "@/lib/utils"
import { Button } from "@/components/UI/button"
import {ArrowLeft, ArrowRight, ChevronIcon} from "@/assets";
import { cn } from "@/lib/utils";
import { Button } from "@/components/UI/button";
import { ArrowLeft, ArrowRight, ChevronIcon } from "@/assets";
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1];
type CarouselProps = {
opts?: CarouselOptions
plugins?: CarouselPlugin
orientation?: "horizontal" | "vertical"
setApi?: (api: CarouselApi) => void
}
opts?: CarouselOptions;
plugins?: CarouselPlugin;
orientation?: "horizontal" | "vertical";
setApi?: (api: CarouselApi) => void;
};
type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0]
api: ReturnType<typeof useEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
canScrollPrev: boolean
canScrollNext: boolean
} & CarouselProps
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
api: ReturnType<typeof useEmblaCarousel>[1];
scrollPrev: () => void;
scrollNext: () => void;
canScrollPrev: boolean;
canScrollNext: boolean;
} & CarouselProps;
const CarouselContext = React.createContext<CarouselContextProps | null>(null)
const CarouselContext = React.createContext<CarouselContextProps | null>(null);
function useCarousel() {
const context = React.useContext(CarouselContext)
const context = React.useContext(CarouselContext);
if (!context) {
throw new Error("useCarousel must be used within a <Carousel />")
throw new Error("useCarousel must be used within a <Carousel />");
}
return context
return context;
}
function Carousel({
orientation = "horizontal",
opts,
setApi,
plugins,
className,
children,
...props
}: React.ComponentProps<"div"> & CarouselProps) {
orientation = "horizontal",
opts,
setApi,
plugins,
className,
children,
...props
}: React.ComponentProps<"div"> & CarouselProps) {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
axis: orientation === "horizontal" ? "x" : "y",
},
plugins
)
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
const [canScrollNext, setCanScrollNext] = React.useState(false)
);
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false);
const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) return
setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
}, [])
if (!api) return;
setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
}, []);
const scrollPrev = React.useCallback(() => {
api?.scrollPrev()
}, [api])
api?.scrollPrev();
}, [api]);
const scrollNext = React.useCallback(() => {
api?.scrollNext()
}, [api])
api?.scrollNext();
}, [api]);
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") {
event.preventDefault()
scrollPrev()
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowRight") {
event.preventDefault()
scrollNext()
event.preventDefault();
scrollNext();
}
},
[scrollPrev, scrollNext]
)
);
React.useEffect(() => {
if (!api || !setApi) return
setApi(api)
}, [api, setApi])
if (!api || !setApi) return;
setApi(api);
}, [api, setApi]);
React.useEffect(() => {
if (!api) return
onSelect(api)
api.on("reInit", onSelect)
api.on("select", onSelect)
if (!api) return;
onSelect(api);
api.on("reInit", onSelect);
api.on("select", onSelect);
return () => {
api?.off("select", onSelect)
}
}, [api, onSelect])
api?.off("select", onSelect);
};
}, [api, onSelect]);
return (
<CarouselContext.Provider
@@ -110,8 +108,7 @@ function Carousel({
carouselRef,
api: api,
opts,
orientation:
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
canScrollPrev,
@@ -129,32 +126,28 @@ function Carousel({
{children}
</div>
</CarouselContext.Provider>
)
);
}
function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
const { carouselRef, orientation } = useCarousel()
const { carouselRef, orientation } = useCarousel();
return (
<div
ref={carouselRef}
className="overflow-hidden border border-text-primary/10 p-1 rounded-2xl"
className="border-text-primary/10 overflow-hidden rounded-2xl border p-1"
data-slot="carousel-content"
>
<div
className={cn(
"flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className
)}
className={cn("flex", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", className)}
{...props}
/>
</div>
)
);
}
function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
const { orientation } = useCarousel()
const { orientation } = useCarousel();
return (
<div
@@ -168,16 +161,16 @@ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
)}
{...props}
/>
)
);
}
function CarouselPrevious({
className,
variant = "outline",
size = "icon-sm",
...props
}: React.ComponentProps<typeof Button>) {
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
className,
variant = "outline",
size = "icon-sm",
...props
}: React.ComponentProps<typeof Button>) {
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
return (
<Button
@@ -185,7 +178,7 @@ function CarouselPrevious({
variant={variant}
size={size}
className={cn(
"rounded-xl p-5 absolute touch-manipulation bg-card border border-primary-300",
"bg-card border-primary-300 absolute touch-manipulation rounded-xl border p-5",
orientation === "horizontal"
? "top-1/2 -left-12 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
@@ -198,16 +191,16 @@ function CarouselPrevious({
<ArrowLeft className="text-text-primary" />
<span className="sr-only">Previous slide</span>
</Button>
)
);
}
function CarouselNext({
className,
variant = "outline",
size = "icon-sm",
...props
}: React.ComponentProps<typeof Button>) {
const { orientation, scrollNext, canScrollNext } = useCarousel()
className,
variant = "outline",
size = "icon-sm",
...props
}: React.ComponentProps<typeof Button>) {
const { orientation, scrollNext, canScrollNext } = useCarousel();
return (
<Button
@@ -215,7 +208,7 @@ function CarouselNext({
variant={variant}
size={size}
className={cn(
"rounded-xl p-5 absolute touch-manipulation bg-card border-primary-300",
"bg-card border-primary-300 absolute touch-manipulation rounded-xl p-5",
orientation === "horizontal"
? "top-1/2 -right-12 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
@@ -228,15 +221,7 @@ function CarouselNext({
<ArrowRight className="text-text-primary size-5" />
<span className="sr-only">Next slide</span>
</Button>
)
);
}
export {
type CarouselApi,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
useCarousel,
}
export { type CarouselApi, Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext, useCarousel };

View File

@@ -1,8 +1,8 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { Slot } from "radix-ui";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
@@ -10,10 +10,13 @@ const buttonVariants = cva(
variants: {
variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
outline:
"border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
destructive:
"bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
@@ -22,7 +25,8 @@ const buttonVariants = cva(
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
icon: "size-8",
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
"icon-xs":
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
"icon-lg": "size-9",
},
@@ -32,19 +36,19 @@ const buttonVariants = cva(
size: "default",
},
}
)
);
function Button({
className,
variant = "default",
size = "default",
asChild = false,
...props
}: React.ComponentProps<"button"> &
className,
variant = "default",
size = "default",
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot.Root : "button"
asChild?: boolean;
}) {
const Comp = asChild ? Slot.Root : "button";
return (
<Comp
@@ -54,7 +58,7 @@ function Button({
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
);
}
export { Button, buttonVariants }
export { Button, buttonVariants };

View File

@@ -1,20 +1,19 @@
import * as React from "react"
import * as React from "react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Card({
className,
size = "default",
...props
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
function Card({ className, size = "default", ...props }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
return (
<div
data-slot="card"
data-size={size}
className={cn("ring-foreground/10 text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
className={cn(
"ring-foreground/10 text-card-foreground group/card flex flex-col gap-4 overflow-hidden rounded-xl py-4 text-sm has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className
)}
{...props}
/>
)
);
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -22,12 +21,12 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card-header"
className={cn(
"gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
className
)}
{...props}
/>
)
);
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
@@ -37,58 +36,38 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
className={cn("text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className)}
{...props}
/>
)
);
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
return <div data-slot="card-description" className={cn("text-muted-foreground text-sm", className)} {...props} />;
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
{...props}
/>
)
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
{...props}
/>
)
return <div data-slot="card-content" className={cn("px-4 group-data-[size=sm]/card:px-3", className)} {...props} />;
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center", className)}
className={cn(
"bg-muted/50 flex items-center rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3",
className
)}
{...props}
/>
)
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
}
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };

View File

@@ -44,4 +44,4 @@ export const categories = [
title: "ژل و بوتاکس",
image: "/images/categories/ژل.jpg",
},
]
];