Files
reui-blocks/blocks/settings-integrations7.tsx
T

226 lines
7.4 KiB
TypeScript

"use client";
import { Check, Plus, Search } from "lucide-react";
import { useMemo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
interface IntegrationItem {
image: string;
title: string;
description: string;
isConnected?: boolean;
category: string;
className?: string;
}
interface IntegrationCardProps {
integration: IntegrationItem;
onToggle?: (title: string) => void;
}
const IntegrationCard = ({ integration, onToggle }: IntegrationCardProps) => {
return (
<div className="group relative overflow-hidden rounded-xl border bg-card transition-all hover:shadow-lg">
<div className="p-6">
<div className="flex items-start gap-4">
<div className="flex size-14 shrink-0 items-center justify-center rounded-xl border bg-background">
<img
src={integration.image}
alt={`آیکون ${integration.title}`}
className={cn("size-8", integration.className)}
/>
</div>
<div className="flex-1 space-y-1">
<h3 className="font-semibold">{integration.title}</h3>
<p className="text-sm text-muted-foreground">
{integration.description}
</p>
</div>
</div>
</div>
<div className="border-t bg-muted/30 px-6 py-3">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">
{integration.category}
</span>
<Button
size="sm"
variant={integration.isConnected ? "secondary" : "default"}
className="h-8"
onClick={() => onToggle?.(integration.title)}
>
{integration.isConnected ? (
<>
<Check className="size-3.5" />
متصل
</>
) : (
<>
<Plus className="size-3.5" />
اتصال
</>
)}
</Button>
</div>
</div>
</div>
);
};
interface SettingsIntegrations7Props {
className?: string;
heading?: string;
subHeading?: string;
integrations?: IntegrationItem[];
}
const SettingsIntegrations7 = ({
className,
heading = "بازارچه یکپارچه‌سازی",
subHeading = "ابزارهای فروش و پشتیبانی را وصل کنید تا داده‌ها در CRM جاری بمانند.",
integrations: initialIntegrations = [
{
image:
"/block-media/shadcnblocks/block/logos/slack-icon.svg",
title: "Slack",
description: "هشدار لید و تیکت را لحظه‌ای در کانال تیم بگیرید.",
isConnected: true,
category: "ارتباطات تیمی",
},
{
image:
"/block-media/shadcnblocks/block/logos/discord-icon.svg",
title: "Discord",
description: "وضعیت پایپ‌لاین را به سرور تیم بفرستید.",
category: "ارتباطات تیمی",
},
{
image:
"/block-media/shadcnblocks/block/logos/google-icon.svg",
title: "Google Drive",
description: "اسناد معامله را خودکار همگام و پشتیبان کنید.",
isConnected: true,
category: "فایل و اسناد",
},
{
image:
"/block-media/shadcnblocks/block/logos/dropbox-icon.svg",
title: "Dropbox",
description: "اسناد مشتری را بدون خروج از CRM باز کنید.",
category: "فایل و اسناد",
},
{
image:
"/block-media/shadcnblocks/block/logos/github-icon.svg",
title: "GitHub",
description: "وضعیت مخزن و دیپلوی را پیگیری کنید.",
isConnected: true,
category: "مهندسی",
className: "dark:invert",
},
{
image:
"/block-media/shadcnblocks/block/logos/gitlab-icon.svg",
title: "GitLab",
description: "استقرار خودکار را با رویدادهای محصول همگام کنید.",
category: "مهندسی",
},
{
image:
"/block-media/shadcnblocks/block/logos/notion-icon.svg",
title: "Notion",
description: "صفحات دانش فروش را دوطرفه همگام کنید.",
category: "عملیات فروش",
className: "dark:invert",
},
{
image:
"/block-media/shadcnblocks/block/logos/linear-icon.svg",
title: "Linear",
description: "کارهای محصول را همین‌جا بسازید و پیگیری کنید.",
isConnected: true,
category: "عملیات فروش",
className: "dark:invert",
},
{
image:
"/block-media/shadcnblocks/block/logos/stripe-icon.svg",
title: "Stripe",
description: "پرداخت و تمدید اشتراک را پردازش کنید.",
isConnected: true,
category: "پرداخت و صورتحساب",
},
],
}: SettingsIntegrations7Props) => {
const [integrations, setIntegrations] = useState(initialIntegrations);
const [searchValue, setSearchValue] = useState("");
const filteredIntegrations = useMemo(() => {
if (!searchValue) return integrations;
return integrations.filter(
(i) =>
i.title.toLowerCase().includes(searchValue.toLowerCase()) ||
i.category.toLowerCase().includes(searchValue.toLowerCase()),
);
}, [integrations, searchValue]);
const handleToggle = (title: string) => {
setIntegrations((prev) =>
prev.map((i) =>
i.title === title ? { ...i, isConnected: !i.isConnected } : i,
),
);
};
const connectedCount = integrations.filter((i) => i.isConnected).length;
return (
<section className={cn("py-32", className)}>
<div className="container max-w-6xl">
<div className="mb-8 flex flex-col gap-6 sm:flex-row sm:items-end sm:justify-between">
<div className="space-y-2">
<h1 className="text-3xl font-bold tracking-tight">{heading}</h1>
<p className="text-muted-foreground">{subHeading}</p>
<p className="text-sm text-muted-foreground">
{connectedCount.toLocaleString("fa-IR")} از{" "}
{integrations.length.toLocaleString("fa-IR")} سرویس متصل است
</p>
</div>
<div className="relative w-full sm:w-72">
<Search className="absolute top-1/2 start-3 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="جستجوی یکپارچه‌سازی…"
className="ps-9"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
/>
</div>
</div>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{filteredIntegrations.map((integration) => (
<IntegrationCard
key={integration.title}
integration={integration}
onToggle={handleToggle}
/>
))}
</div>
{filteredIntegrations.length === 0 && (
<div className="py-12 text-center">
<p className="text-muted-foreground">
نتیجه‌ای برای «{searchValue}» پیدا نشد
</p>
</div>
)}
</div>
</section>
);
};
export { SettingsIntegrations7 };