242 lines
7.5 KiB
TypeScript
242 lines
7.5 KiB
TypeScript
import {
|
|
Calendar,
|
|
Github,
|
|
Globe,
|
|
Linkedin,
|
|
MapPin,
|
|
Twitter,
|
|
} from "lucide-react";
|
|
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface SocialLinks {
|
|
twitter?: string;
|
|
linkedin?: string;
|
|
github?: string;
|
|
website?: string;
|
|
}
|
|
|
|
interface Stats {
|
|
followers: number;
|
|
following: number;
|
|
contributions: number;
|
|
}
|
|
|
|
interface User {
|
|
name: string;
|
|
avatar?: string;
|
|
coverImage?: string;
|
|
role?: string;
|
|
company?: string;
|
|
bio?: string;
|
|
location?: string;
|
|
joinedAt?: string;
|
|
skills?: string[];
|
|
socialLinks?: SocialLinks;
|
|
stats?: Stats;
|
|
isFollowing?: boolean;
|
|
}
|
|
|
|
interface UserProfile3Props {
|
|
user?: User;
|
|
className?: string;
|
|
}
|
|
|
|
const formatNumber = (num: number): string => {
|
|
if (num >= 1000000) {
|
|
const value = num / 1000000;
|
|
const formatted = parseFloat(value.toFixed(1).replace(/\.0$/, "")).toLocaleString(
|
|
"fa-IR",
|
|
{ useGrouping: false },
|
|
);
|
|
return `${formatted} میلیون`;
|
|
}
|
|
if (num >= 1000) {
|
|
const value = num / 1000;
|
|
const formatted = parseFloat(value.toFixed(1).replace(/\.0$/, "")).toLocaleString(
|
|
"fa-IR",
|
|
{ useGrouping: false },
|
|
);
|
|
return `${formatted} هزار`;
|
|
}
|
|
return num.toLocaleString("fa-IR");
|
|
};
|
|
|
|
const UserProfile3 = ({
|
|
user = {
|
|
name: "سارا احمدی",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar8.jpg",
|
|
coverImage:
|
|
"/block-media/shadcnblocks/block/photos/pawel-czerwinski-O4fAgtXLRwI-unsplash.jpg",
|
|
role: "مهندس طراحی",
|
|
company: "لینیر",
|
|
bio: "پل زدن بین طراحی و مهندسی برای ساخت تجربههای کاربری روان. پیشتر سیستمهای طراحی در نوشن ساخته و به Radix UI مشارکت کردهام. درباره توکنهای طراحی، معماری کامپوننت و تقاطع زیباییشناسی و کارایی مینویسم.",
|
|
location: "تهران، ایران",
|
|
joinedAt: "فروردین ۱۴۰۰",
|
|
skills: [
|
|
"React",
|
|
"TypeScript",
|
|
"Figma",
|
|
"سیستمهای طراحی",
|
|
"CSS",
|
|
"انیمیشن",
|
|
],
|
|
socialLinks: {
|
|
twitter: "https://twitter.com",
|
|
linkedin: "https://linkedin.com",
|
|
github: "https://github.com",
|
|
website: "https://example.com",
|
|
},
|
|
stats: {
|
|
followers: 8420,
|
|
following: 312,
|
|
contributions: 1247,
|
|
},
|
|
isFollowing: false,
|
|
},
|
|
className,
|
|
}: UserProfile3Props) => {
|
|
const initials = user.name
|
|
.split(" ")
|
|
.map((n) => n[0])
|
|
.join("");
|
|
|
|
const socialIcons = [
|
|
{ key: "twitter", icon: Twitter, href: user.socialLinks?.twitter },
|
|
{ key: "linkedin", icon: Linkedin, href: user.socialLinks?.linkedin },
|
|
{ key: "github", icon: Github, href: user.socialLinks?.github },
|
|
{ key: "website", icon: Globe, href: user.socialLinks?.website },
|
|
].filter((item) => item.href);
|
|
|
|
return (
|
|
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
|
|
<div className="container">
|
|
<div className="mx-auto max-w-2xl overflow-hidden rounded-xl border bg-card">
|
|
<div className="relative h-32 bg-muted sm:h-40">
|
|
{user.coverImage && (
|
|
<img
|
|
src={user.coverImage}
|
|
alt=""
|
|
className="size-full object-cover"
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
<div className="relative px-6 pb-6">
|
|
<div className="-mt-12 mb-4 flex items-end justify-between sm:-mt-14">
|
|
<Avatar className="size-24 border-4 border-card sm:size-28">
|
|
<AvatarImage
|
|
src={user.avatar}
|
|
alt={user.name}
|
|
className="object-cover"
|
|
/>
|
|
<AvatarFallback className="text-xl font-semibold">
|
|
{initials}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div className="flex gap-2">
|
|
<Button variant={user.isFollowing ? "outline" : "default"}>
|
|
{user.isFollowing ? "دنبالشده" : "دنبال کردن"}
|
|
</Button>
|
|
<Button variant="outline">پیام</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<h2 className="text-xl font-bold">{user.name}</h2>
|
|
<p className="text-muted-foreground">
|
|
{user.role}
|
|
{user.company && (
|
|
<>
|
|
{" "}
|
|
در <span className="font-medium">{user.company}</span>
|
|
</>
|
|
)}
|
|
</p>
|
|
</div>
|
|
|
|
{user.bio && (
|
|
<p className="mt-4 text-sm leading-relaxed text-muted-foreground">
|
|
{user.bio}
|
|
</p>
|
|
)}
|
|
|
|
<div className="mt-4 flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-muted-foreground">
|
|
{user.location && (
|
|
<div className="flex items-center gap-1.5">
|
|
<MapPin className="size-3.5" />
|
|
<span>{user.location}</span>
|
|
</div>
|
|
)}
|
|
{user.joinedAt && (
|
|
<div className="flex items-center gap-1.5">
|
|
<Calendar className="size-3.5" />
|
|
<span>عضویت از {user.joinedAt}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{socialIcons.length > 0 && (
|
|
<div className="mt-4 flex gap-3">
|
|
{socialIcons.map(({ key, icon: Icon, href }) => (
|
|
<a
|
|
key={key}
|
|
href={href}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-muted-foreground transition-colors hover:text-foreground"
|
|
>
|
|
<Icon className="size-5" />
|
|
</a>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{user.stats && (
|
|
<div className="mt-6 flex gap-6 border-t pt-6">
|
|
<div>
|
|
<span className="font-semibold">
|
|
{formatNumber(user.stats.followers)}
|
|
</span>{" "}
|
|
<span className="text-muted-foreground">دنبالکننده</span>
|
|
</div>
|
|
<div>
|
|
<span className="font-semibold">
|
|
{formatNumber(user.stats.following)}
|
|
</span>{" "}
|
|
<span className="text-muted-foreground">دنبالشده</span>
|
|
</div>
|
|
<div>
|
|
<span className="font-semibold">
|
|
{formatNumber(user.stats.contributions)}
|
|
</span>{" "}
|
|
<span className="text-muted-foreground">مشارکت</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{user.skills && user.skills.length > 0 && (
|
|
<div className="mt-6 border-t pt-6">
|
|
<h3 className="mb-3 text-sm font-medium">مهارتها</h3>
|
|
<div className="flex flex-wrap gap-2">
|
|
{user.skills.map((skill) => (
|
|
<Badge key={skill} variant="secondary">
|
|
{skill}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { UserProfile3 };
|