327 lines
11 KiB
TypeScript
327 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import { GitPullRequest, Star } from "lucide-react";
|
|
import { useState } from "react";
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const CATEGORIES = [
|
|
"React",
|
|
"TailwindCSS",
|
|
"TypeScript",
|
|
"Express",
|
|
"MongoDB",
|
|
"Python",
|
|
] as const;
|
|
|
|
const ROLE_LABELS = {
|
|
author: "مدیریت فروش",
|
|
contributor: "سامانه مدیریت ارتباط با مشتری",
|
|
} as const;
|
|
|
|
interface Project {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
category: string[];
|
|
link: string;
|
|
stars: number;
|
|
contributions: number;
|
|
pullRequests: number;
|
|
role: "author" | "contributor";
|
|
}
|
|
|
|
const PROJECTS: Project[] = [
|
|
{
|
|
id: "react-portfolio",
|
|
title: "react-portfolio",
|
|
description:
|
|
"تاریخچه تماس، قرارداد و تیکت هر مشتری را در یک صفحه ببینید و بدون جستجو تصمیم بگیرید.",
|
|
category: ["React", "TailwindCSS", "TypeScript"],
|
|
link: "#",
|
|
stars: 5,
|
|
contributions: 2,
|
|
pullRequests: 15,
|
|
role: "author",
|
|
},
|
|
{
|
|
id: "ai-chatbot",
|
|
title: "ai-chatbot",
|
|
description: "وظایف تکراری فروش را خودکار کنید و تمرکز تیم را روی بستن معامله بگذارید.",
|
|
category: ["React", "TailwindCSS", "TypeScript", "Python"],
|
|
link: "#",
|
|
stars: 86,
|
|
contributions: 12,
|
|
pullRequests: 43,
|
|
role: "contributor",
|
|
},
|
|
{
|
|
id: "task-manager-app",
|
|
title: "task-manager-app",
|
|
description:
|
|
"بر اساس داده واقعی پایپلاین، درآمد ماههای آینده را با اطمینان بیشتر تخمین بزنید.",
|
|
category: ["React", "Express", "MongoDB", "TypeScript"],
|
|
link: "#",
|
|
stars: 34,
|
|
contributions: 8,
|
|
pullRequests: 27,
|
|
role: "contributor",
|
|
},
|
|
{
|
|
id: "data-visualization-dashboard",
|
|
title: "data-visualization-dashboard",
|
|
description: "پیگیریهای از قلم افتاده را قبل از سرد شدن سرنخ بهموقع یادآوری کنید.",
|
|
category: ["React", "TypeScript", "Python"],
|
|
link: "#",
|
|
stars: 92,
|
|
contributions: 15,
|
|
pullRequests: 56,
|
|
role: "author",
|
|
},
|
|
{
|
|
id: "e-commerce-platform",
|
|
title: "e-commerce-platform",
|
|
description: "سرنخها را بر اساس منطقه، اندازه یا صنعت به نماینده مناسب اختصاص دهید.",
|
|
category: ["React", "Express", "MongoDB", "TypeScript"],
|
|
link: "#",
|
|
stars: 127,
|
|
contributions: 23,
|
|
pullRequests: 89,
|
|
role: "contributor",
|
|
},
|
|
{
|
|
id: "social-media-analyzer",
|
|
title: "social-media-analyzer",
|
|
description: "عملکرد هر نماینده و نرخ تبدیل هر مرحله را در یک داشبورد مقایسه کنید.",
|
|
category: ["Python", "React", "MongoDB"],
|
|
link: "#",
|
|
stars: 73,
|
|
contributions: 9,
|
|
pullRequests: 31,
|
|
role: "author",
|
|
},
|
|
{
|
|
id: "weather-forecast-app",
|
|
title: "weather-forecast-app",
|
|
description: "تاریخچه تماس، قرارداد و تیکت هر مشتری را در یک صفحه ببینید و بدون جستجو تصمیم بگیرید.",
|
|
category: ["React", "TypeScript", "Python"],
|
|
link: "#",
|
|
stars: 45,
|
|
contributions: 7,
|
|
pullRequests: 22,
|
|
role: "author",
|
|
},
|
|
{
|
|
id: "inventory-management-system",
|
|
title: "inventory-management-system",
|
|
description: "وظایف تکراری فروش را خودکار کنید و تمرکز تیم را روی بستن معامله بگذارید.",
|
|
category: ["React", "Express", "MongoDB", "TypeScript"],
|
|
link: "#",
|
|
stars: 156,
|
|
contributions: 28,
|
|
pullRequests: 94,
|
|
role: "contributor",
|
|
},
|
|
{
|
|
id: "fitness-tracking-platform",
|
|
title: "fitness-tracking-platform",
|
|
description: "بر اساس داده واقعی پایپلاین، درآمد ماههای آینده را با اطمینان بیشتر تخمین بزنید.",
|
|
category: ["React", "TailwindCSS", "MongoDB"],
|
|
link: "#",
|
|
stars: 112,
|
|
contributions: 18,
|
|
pullRequests: 67,
|
|
role: "author",
|
|
},
|
|
{
|
|
id: "recipe-sharing-network",
|
|
title: "recipe-sharing-network",
|
|
description: "پیگیریهای از قلم افتاده را قبل از سرد شدن سرنخ بهموقع یادآوری کنید.",
|
|
category: ["React", "Express", "MongoDB"],
|
|
link: "#",
|
|
stars: 89,
|
|
contributions: 14,
|
|
pullRequests: 41,
|
|
role: "contributor",
|
|
},
|
|
{
|
|
id: "budget-tracker",
|
|
title: "budget-tracker",
|
|
description: "سرنخها را بر اساس منطقه، اندازه یا صنعت به نماینده مناسب اختصاص دهید.",
|
|
category: ["React", "TypeScript", "MongoDB"],
|
|
link: "#",
|
|
stars: 67,
|
|
contributions: 11,
|
|
pullRequests: 35,
|
|
role: "author",
|
|
},
|
|
{
|
|
id: "learning-management-system",
|
|
title: "learning-management-system",
|
|
description: "عملکرد هر نماینده و نرخ تبدیل هر مرحله را در یک داشبورد مقایسه کنید.",
|
|
category: ["React", "Express", "TypeScript", "MongoDB"],
|
|
link: "#",
|
|
stars: 143,
|
|
contributions: 25,
|
|
pullRequests: 78,
|
|
role: "contributor",
|
|
},
|
|
];
|
|
|
|
interface Feature295Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Feature295 = ({ className }: Feature295Props) => {
|
|
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
|
const [selectedRole, setSelectedRole] = useState<string>("all");
|
|
const [showAllProjects, setShowAllProjects] = useState(false);
|
|
|
|
const filteredProjects = PROJECTS.filter((project) => {
|
|
const matchesCategory =
|
|
selectedCategory === null || project.category.includes(selectedCategory);
|
|
|
|
const matchesRole = selectedRole === "all" || project.role === selectedRole;
|
|
|
|
return matchesCategory && matchesRole;
|
|
});
|
|
|
|
return (
|
|
<section dir="rtl" lang="fa" className={cn("bg-muted/50 py-32", className)}>
|
|
<div className="container">
|
|
<h1 className="text-center text-3xl font-medium text-balance md:text-2xll">
|
|
پایپلاین فروش را شفاف کنید
|
|
</h1>
|
|
<div className="mt-10">
|
|
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
<div
|
|
className="-mx-8 flex items-center gap-2 overflow-x-auto px-8"
|
|
style={{
|
|
scrollbarWidth: "none",
|
|
}}
|
|
>
|
|
<Badge
|
|
variant={selectedCategory === null ? "default" : "outline"}
|
|
onClick={() => setSelectedCategory(null)}
|
|
className={cn(
|
|
"cursor-pointer",
|
|
selectedCategory !== null && "bg-background",
|
|
)}
|
|
>
|
|
معاملات را سریعتر به قرارداد برسانید
|
|
</Badge>
|
|
{CATEGORIES.map((category) => (
|
|
<Badge
|
|
key={category}
|
|
variant={
|
|
selectedCategory === category ? "default" : "outline"
|
|
}
|
|
onClick={() => setSelectedCategory(category)}
|
|
className={cn(
|
|
"cursor-pointer",
|
|
selectedCategory !== category && "bg-background",
|
|
)}
|
|
>
|
|
{category}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
<Select
|
|
defaultValue={selectedRole}
|
|
onValueChange={(value) => setSelectedRole(value)}
|
|
>
|
|
<SelectTrigger className="bg-background md:max-w-40 lg:max-w-96">
|
|
<SelectValue placeholder="هر سرنخ را تا قرارداد دنبال کنید" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="all">داده مشتری را در یک منبع واحد جمع کنید</SelectItem>
|
|
<SelectItem value="author">ارتباط با مشتری</SelectItem>
|
|
<SelectItem value="contributor">هر سرنخ را تا قرارداد دنبال کنید</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{filteredProjects.length > 0 ? (
|
|
<>
|
|
<div className="mt-10 grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
|
|
{showAllProjects
|
|
? filteredProjects.map((project) => (
|
|
<ProjectCard key={project.id} project={project} />
|
|
))
|
|
: filteredProjects
|
|
.slice(0, 9)
|
|
.map((project) => (
|
|
<ProjectCard key={project.id} project={project} />
|
|
))}
|
|
</div>
|
|
{filteredProjects.length > 9 && (
|
|
<div className="mt-10 flex justify-center">
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => setShowAllProjects(!showAllProjects)}
|
|
>
|
|
{showAllProjects
|
|
? "فروش تیمتان را یکجا مدیریت کنید"
|
|
: "روابط مشتری را در یک نگاه ببینید"}
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</>
|
|
) : (
|
|
<div className="mt-16 flex flex-col items-center justify-center text-center">
|
|
<h3 className="text-xl font-medium">پروفایل ۳۶۰ درجه مشتری</h3>
|
|
<p className="mt-2 max-w-md text-pretty text-muted-foreground">
|
|
اتوماسیون مراحل فروش
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Feature295 };
|
|
|
|
const ProjectCard = ({ project }: { project: Project }) => {
|
|
return (
|
|
<a
|
|
href={project.link}
|
|
className="flex flex-col justify-between gap-8 rounded-lg border border-border bg-background p-5 text-start"
|
|
>
|
|
<div>
|
|
<div className="flex items-center justify-between gap-2">
|
|
<h3 className="text-lg font-medium" dir="rtl">
|
|
{project.title}
|
|
</h3>
|
|
<span className="flex items-center gap-1 text-sm">
|
|
<Star className="size-4 shrink-0 fill-yellow-500 text-yellow-500" />
|
|
{project.stars.toLocaleString("fa-IR")}
|
|
</span>
|
|
</div>
|
|
<p className="mt-2 text-pretty text-muted-foreground">
|
|
{project.description}
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center justify-between gap-2">
|
|
<Badge variant="outline">{ROLE_LABELS[project.role]}</Badge>
|
|
<span className="flex items-center gap-3 text-sm text-muted-foreground">
|
|
<span className="flex items-center gap-1">
|
|
<GitPullRequest className="size-4 shrink-0" />
|
|
{project.pullRequests.toLocaleString("fa-IR")}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</a>
|
|
);
|
|
};
|