pxengine 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -122
- package/dist/index.cjs +35586 -8241
- package/dist/index.d.cts +574 -33
- package/dist/index.d.ts +574 -33
- package/dist/index.js +35525 -8199
- package/dist/registry.json +1776 -113
- package/package.json +1 -1
- package/src/atoms/BadgeAtom.tsx +11 -3
- package/src/atoms/ButtonAtom.tsx +2 -2
- package/src/atoms/CalendarAtom.tsx +1 -1
- package/src/atoms/CardAtom.tsx +1 -1
- package/src/atoms/ChartAtom.tsx +190 -0
- package/src/atoms/CheckboxAtom.tsx +33 -0
- package/src/atoms/ContextMenuAtom.tsx +49 -0
- package/src/atoms/DrawerAtom.tsx +49 -0
- package/src/atoms/DropdownMenuAtom.tsx +49 -0
- package/src/atoms/InputAtom.tsx +49 -23
- package/src/atoms/InputOTPAtom.tsx +49 -0
- package/src/atoms/KbdAtom.tsx +25 -0
- package/src/atoms/LabelAtom.tsx +23 -0
- package/src/atoms/RadioGroupAtom.tsx +31 -0
- package/src/atoms/RatingAtom.tsx +37 -0
- package/src/atoms/ResizableAtom.tsx +51 -0
- package/src/atoms/SliderAtom.tsx +32 -0
- package/src/atoms/SwitchAtom.tsx +32 -0
- package/src/atoms/TextareaAtom.tsx +42 -0
- package/src/atoms/TimelineAtom.tsx +77 -0
- package/src/atoms/ToggleAtom.tsx +36 -0
- package/src/atoms/VideoAtom.tsx +34 -0
- package/src/atoms/index.ts +17 -0
- package/src/components/ui/resizable.tsx +5 -6
- package/src/molecules/creator-discovery/AudienceDemographicsCard/AudienceDemographicsCard.tsx +44 -0
- package/src/molecules/creator-discovery/AudienceDemographicsCard/index.ts +1 -0
- package/src/molecules/creator-discovery/AudienceMetricCard/AudienceMetricCard.tsx +50 -0
- package/src/molecules/creator-discovery/AudienceMetricCard/index.ts +1 -0
- package/src/molecules/creator-discovery/BrandAffinityGroup/BrandAffinityGroup.tsx +36 -0
- package/src/molecules/creator-discovery/BrandAffinityGroup/index.ts +1 -0
- package/src/molecules/creator-discovery/ContentPreviewGallery/ContentPreviewGallery.tsx +41 -0
- package/src/molecules/creator-discovery/ContentPreviewGallery/index.ts +1 -0
- package/src/molecules/creator-discovery/CreatorActionHeader/CreatorActionHeader.tsx +77 -0
- package/src/molecules/creator-discovery/CreatorActionHeader/index.ts +1 -0
- package/src/molecules/creator-discovery/CreatorGridCard/CreatorGridCard.tsx +104 -0
- package/src/molecules/creator-discovery/CreatorGridCard/index.ts +1 -0
- package/src/molecules/creator-discovery/CreatorProfileSummary/CreatorProfileSummary.tsx +65 -0
- package/src/molecules/creator-discovery/CreatorProfileSummary/index.ts +1 -0
- package/src/molecules/creator-discovery/GrowthChartCard/GrowthChartCard.tsx +58 -0
- package/src/molecules/creator-discovery/GrowthChartCard/index.ts +1 -0
- package/src/molecules/creator-discovery/PlatformIconGroup/PlatformIconGroup.tsx +72 -0
- package/src/molecules/creator-discovery/PlatformIconGroup/index.ts +1 -0
- package/src/molecules/creator-discovery/TopPostsGrid/TopPostsGrid.tsx +49 -0
- package/src/molecules/creator-discovery/TopPostsGrid/index.ts +1 -0
- package/src/molecules/creator-discovery/index.ts +10 -0
- package/src/molecules/generic/DataGrid/DataGrid.tsx +102 -0
- package/src/molecules/generic/DataGrid/index.ts +1 -0
- package/src/molecules/generic/EmptyState/EmptyState.tsx +61 -0
- package/src/molecules/generic/EmptyState/index.ts +1 -0
- package/src/molecules/generic/FileUpload/FileUpload.tsx +62 -0
- package/src/molecules/generic/FileUpload/index.ts +1 -0
- package/src/molecules/generic/FilterBar/FilterBar.tsx +54 -0
- package/src/molecules/generic/FilterBar/index.ts +1 -0
- package/src/molecules/generic/LoadingOverlay/LoadingOverlay.tsx +39 -0
- package/src/molecules/generic/LoadingOverlay/index.ts +1 -0
- package/src/molecules/generic/NotificationList/NotificationList.tsx +80 -0
- package/src/molecules/generic/NotificationList/index.ts +1 -0
- package/src/molecules/generic/StatsGrid/StatsGrid.tsx +80 -0
- package/src/molecules/generic/StatsGrid/index.ts +1 -0
- package/src/molecules/generic/StepWizard/StepWizard.tsx +67 -0
- package/src/molecules/generic/StepWizard/index.ts +1 -0
- package/src/molecules/generic/TagCloud/TagCloud.tsx +32 -0
- package/src/molecules/generic/TagCloud/index.ts +1 -0
- package/src/molecules/generic/index.ts +9 -0
- package/src/render/PXEngineRenderer.tsx +1 -1
- package/src/types/atoms.ts +150 -2
- package/src/types/molecules.ts +226 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
4
|
+
import { CreatorActionHeaderMolecule } from "../../../types/molecules";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* CreatorActionHeader
|
|
9
|
+
* A premium header for profile views with banner and sticky actions.
|
|
10
|
+
*/
|
|
11
|
+
export const CreatorActionHeader: React.FC<CreatorActionHeaderMolecule> = ({
|
|
12
|
+
bannerSrc,
|
|
13
|
+
avatarSrc,
|
|
14
|
+
name,
|
|
15
|
+
handle,
|
|
16
|
+
actions,
|
|
17
|
+
className,
|
|
18
|
+
}) => {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
"relative w-full rounded-[40px] overflow-hidden bg-white border border-purple-50 shadow-sm",
|
|
23
|
+
className,
|
|
24
|
+
)}
|
|
25
|
+
>
|
|
26
|
+
{/* Banner */}
|
|
27
|
+
<div className="h-48 w-full bg-gradient-to-r from-purple-100 to-indigo-100 relative">
|
|
28
|
+
{bannerSrc && (
|
|
29
|
+
<img
|
|
30
|
+
src={bannerSrc}
|
|
31
|
+
className="h-full w-full object-cover"
|
|
32
|
+
alt="Banner"
|
|
33
|
+
/>
|
|
34
|
+
)}
|
|
35
|
+
<div className="absolute inset-0 bg-black/5" />
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
{/* Profile Info & Actions */}
|
|
39
|
+
<div className="px-8 pb-8 flex flex-col md:flex-row md:items-end justify-between gap-6 -mt-12 relative z-10">
|
|
40
|
+
<div className="flex flex-col md:flex-row items-start md:items-end gap-6">
|
|
41
|
+
<div className="p-2 bg-white rounded-[32px] shadow-lg">
|
|
42
|
+
<Avatar className="h-32 w-32 rounded-[24px] border-4 border-white">
|
|
43
|
+
<AvatarImage src={avatarSrc} className="rounded-[24px]" />
|
|
44
|
+
<AvatarFallback className="rounded-[24px] bg-purple-50 text-purple-600 text-3xl font-bold">
|
|
45
|
+
{name.charAt(0)}
|
|
46
|
+
</AvatarFallback>
|
|
47
|
+
</Avatar>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="mb-2">
|
|
50
|
+
<h1 className="text-3xl font-bold text-gray-900 tracking-tight">
|
|
51
|
+
{name}
|
|
52
|
+
</h1>
|
|
53
|
+
<p className="text-purple-600 font-medium tracking-wide">
|
|
54
|
+
@{handle}
|
|
55
|
+
</p>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div className="flex items-center gap-3 mb-2">
|
|
60
|
+
{actions.map((action, i) => (
|
|
61
|
+
<Button
|
|
62
|
+
key={i}
|
|
63
|
+
variant={(action.variant as any) || "default"}
|
|
64
|
+
className={cn(
|
|
65
|
+
"rounded-2xl px-6 h-11 font-bold transition-all",
|
|
66
|
+
action.variant === "default" &&
|
|
67
|
+
"bg-purple-600 hover:bg-purple-700 shadow-md hover:shadow-purple-200",
|
|
68
|
+
)}
|
|
69
|
+
>
|
|
70
|
+
{action.label}
|
|
71
|
+
</Button>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./CreatorActionHeader";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
4
|
+
import { CreatorGridCardMolecule } from "../../../types/molecules";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import { Instagram, Youtube, Video, Twitter } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
const TikTokIcon = ({ className }: { className?: string }) => (
|
|
9
|
+
<Video className={className} /> // Placeholder
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* CreatorGridCard
|
|
14
|
+
* A visually rich card for discovery grids.
|
|
15
|
+
*/
|
|
16
|
+
export const CreatorGridCard: React.FC<CreatorGridCardMolecule> = ({
|
|
17
|
+
bannerSrc,
|
|
18
|
+
avatarSrc,
|
|
19
|
+
name,
|
|
20
|
+
handle,
|
|
21
|
+
metrics,
|
|
22
|
+
platforms,
|
|
23
|
+
className,
|
|
24
|
+
}) => {
|
|
25
|
+
const getIcon = (p: string) => {
|
|
26
|
+
switch (p.toLowerCase()) {
|
|
27
|
+
case "instagram":
|
|
28
|
+
return Instagram;
|
|
29
|
+
case "tiktok":
|
|
30
|
+
return TikTokIcon;
|
|
31
|
+
case "youtube":
|
|
32
|
+
return Youtube;
|
|
33
|
+
case "twitter":
|
|
34
|
+
return Twitter;
|
|
35
|
+
default:
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Card
|
|
42
|
+
className={cn(
|
|
43
|
+
"group overflow-hidden rounded-[32px] border-none bg-white shadow-sm hover:shadow-xl transition-all duration-300",
|
|
44
|
+
className,
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
<div className="relative h-32 w-full overflow-hidden">
|
|
48
|
+
{bannerSrc ? (
|
|
49
|
+
<img
|
|
50
|
+
src={bannerSrc}
|
|
51
|
+
alt={name}
|
|
52
|
+
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
|
53
|
+
/>
|
|
54
|
+
) : (
|
|
55
|
+
<div className="h-full w-full bg-gradient-to-br from-purple-500 to-indigo-600" />
|
|
56
|
+
)}
|
|
57
|
+
<div className="absolute inset-x-0 bottom-0 h-1/2 bg-gradient-to-t from-black/20 to-transparent" />
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<CardContent className="relative pt-0 px-5 pb-6">
|
|
61
|
+
<div className="absolute -top-10 left-5">
|
|
62
|
+
<Avatar className="h-20 w-20 border-4 border-white shadow-lg ring-2 ring-purple-100">
|
|
63
|
+
<AvatarImage src={avatarSrc} alt={name} />
|
|
64
|
+
<AvatarFallback className="bg-purple-50 text-purple-700 font-bold text-xl">
|
|
65
|
+
{name.charAt(0)}
|
|
66
|
+
</AvatarFallback>
|
|
67
|
+
</Avatar>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="mt-12 flex flex-col gap-1">
|
|
71
|
+
<h3 className="text-xl font-bold text-gray-900 truncate">{name}</h3>
|
|
72
|
+
<p className="text-sm text-muted-foreground font-medium">@{handle}</p>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div className="mt-4 flex gap-2">
|
|
76
|
+
{platforms.map((p) => {
|
|
77
|
+
const Icon = getIcon(p);
|
|
78
|
+
return Icon ? (
|
|
79
|
+
<div
|
|
80
|
+
key={p}
|
|
81
|
+
className="p-1.5 rounded-lg bg-gray-50 text-gray-600 hover:text-purple-600 hover:bg-purple-50 transition-colors"
|
|
82
|
+
>
|
|
83
|
+
<Icon className="h-4 w-4" />
|
|
84
|
+
</div>
|
|
85
|
+
) : null;
|
|
86
|
+
})}
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div className="mt-6 grid grid-cols-2 gap-4 border-t border-gray-50 pt-4">
|
|
90
|
+
{metrics.map((m, i) => (
|
|
91
|
+
<div key={i} className="flex flex-col">
|
|
92
|
+
<span className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">
|
|
93
|
+
{m.label}
|
|
94
|
+
</span>
|
|
95
|
+
<span className="text-sm font-bold text-purple-700">
|
|
96
|
+
{m.value}
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
</CardContent>
|
|
102
|
+
</Card>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./CreatorGridCard";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AvatarAtom } from "../../../atoms/AvatarAtom";
|
|
3
|
+
import { TextAtom } from "../../../atoms/TextAtom";
|
|
4
|
+
import { CreatorProfileSummaryMolecule } from "../../../types/molecules";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* CreatorProfileSummary
|
|
9
|
+
* A compact row showing avatar, name, and key metrics.
|
|
10
|
+
*/
|
|
11
|
+
export const CreatorProfileSummary: React.FC<CreatorProfileSummaryMolecule> = ({
|
|
12
|
+
avatarSrc,
|
|
13
|
+
name,
|
|
14
|
+
followerCount,
|
|
15
|
+
engagementRate,
|
|
16
|
+
className,
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={cn(
|
|
21
|
+
"flex items-center gap-4 p-3 rounded-2xl bg-white/40 border border-purple-50 shadow-sm",
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
>
|
|
25
|
+
<AvatarAtom
|
|
26
|
+
id={`avatar-${name}`}
|
|
27
|
+
type="avatar"
|
|
28
|
+
src={avatarSrc}
|
|
29
|
+
fallback={name.charAt(0)}
|
|
30
|
+
className="w-12 h-12 ring-2 ring-purple-100"
|
|
31
|
+
/>
|
|
32
|
+
<div className="flex flex-col flex-1">
|
|
33
|
+
<TextAtom
|
|
34
|
+
id={`name-${name}`}
|
|
35
|
+
type="text"
|
|
36
|
+
content={name}
|
|
37
|
+
variant="h4"
|
|
38
|
+
className="text-gray-900 leading-tight"
|
|
39
|
+
/>
|
|
40
|
+
<div className="flex gap-3 mt-1">
|
|
41
|
+
{followerCount && (
|
|
42
|
+
<div className="flex flex-col">
|
|
43
|
+
<span className="text-[10px] uppercase tracking-wider text-muted-foreground font-bold">
|
|
44
|
+
Followers
|
|
45
|
+
</span>
|
|
46
|
+
<span className="text-xs font-semibold text-purple-700">
|
|
47
|
+
{followerCount}
|
|
48
|
+
</span>
|
|
49
|
+
</div>
|
|
50
|
+
)}
|
|
51
|
+
{engagementRate && (
|
|
52
|
+
<div className="flex flex-col border-l border-purple-100 pl-3">
|
|
53
|
+
<span className="text-[10px] uppercase tracking-wider text-muted-foreground font-bold">
|
|
54
|
+
Engagement
|
|
55
|
+
</span>
|
|
56
|
+
<span className="text-xs font-semibold text-indigo-600">
|
|
57
|
+
{engagementRate}
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./CreatorProfileSummary";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
|
3
|
+
import { ChartAtom } from "../../../atoms/ChartAtom";
|
|
4
|
+
import { GrowthChartCardMolecule } from "../../../types/molecules";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import { TrendingUp } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* GrowthChartCard
|
|
10
|
+
* Visualizes time-series growth data for follower and engagement metrics.
|
|
11
|
+
*/
|
|
12
|
+
export const GrowthChartCard: React.FC<GrowthChartCardMolecule> = ({
|
|
13
|
+
title,
|
|
14
|
+
data,
|
|
15
|
+
config,
|
|
16
|
+
metric,
|
|
17
|
+
period,
|
|
18
|
+
className,
|
|
19
|
+
}) => {
|
|
20
|
+
return (
|
|
21
|
+
<Card
|
|
22
|
+
className={cn(
|
|
23
|
+
"rounded-[32px] border-purple-50 shadow-sm overflow-hidden",
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
27
|
+
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
|
28
|
+
<div>
|
|
29
|
+
<CardTitle className="text-lg font-bold text-gray-900">
|
|
30
|
+
{title}
|
|
31
|
+
</CardTitle>
|
|
32
|
+
<p className="text-xs text-muted-foreground">{period}</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="bg-green-50 p-2 rounded-xl">
|
|
35
|
+
<TrendingUp className="w-4 h-4 text-green-600" />
|
|
36
|
+
</div>
|
|
37
|
+
</CardHeader>
|
|
38
|
+
<CardContent>
|
|
39
|
+
<div className="h-[200px] w-full mt-4">
|
|
40
|
+
<ChartAtom
|
|
41
|
+
type="chart"
|
|
42
|
+
id="growth-trend"
|
|
43
|
+
chartType="area"
|
|
44
|
+
data={data}
|
|
45
|
+
config={config}
|
|
46
|
+
XAxisKey="date"
|
|
47
|
+
showTooltip={true}
|
|
48
|
+
showLegend={false}
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
<div className="mt-4 flex items-center justify-between">
|
|
52
|
+
<span className="text-sm font-medium text-gray-500">{metric}</span>
|
|
53
|
+
<span className="text-sm font-bold text-purple-600">+12.5%</span>
|
|
54
|
+
</div>
|
|
55
|
+
</CardContent>
|
|
56
|
+
</Card>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./GrowthChartCard";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PlatformIconGroupMolecule } from "../../../types/molecules";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { Instagram, Youtube, Twitter, Video } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
const TikTokIcon = ({ className }: { className?: string }) => (
|
|
7
|
+
<Video className={className} /> // Placeholder for TikTok
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* PlatformIconGroup
|
|
12
|
+
* Displays a horizontal list of active social platform icons.
|
|
13
|
+
*/
|
|
14
|
+
export const PlatformIconGroup: React.FC<PlatformIconGroupMolecule> = ({
|
|
15
|
+
platforms,
|
|
16
|
+
className,
|
|
17
|
+
}) => {
|
|
18
|
+
const getIcon = (platform: string) => {
|
|
19
|
+
switch (platform) {
|
|
20
|
+
case "instagram":
|
|
21
|
+
return Instagram;
|
|
22
|
+
case "tiktok":
|
|
23
|
+
return TikTokIcon;
|
|
24
|
+
case "youtube":
|
|
25
|
+
return Youtube;
|
|
26
|
+
case "twitter":
|
|
27
|
+
return Twitter;
|
|
28
|
+
default:
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getColors = (platform: string) => {
|
|
34
|
+
switch (platform) {
|
|
35
|
+
case "instagram":
|
|
36
|
+
return "text-pink-600 bg-pink-50 hover:bg-pink-100 border-pink-100";
|
|
37
|
+
case "tiktok":
|
|
38
|
+
return "text-zinc-900 bg-zinc-100 hover:bg-zinc-200 border-zinc-200";
|
|
39
|
+
case "youtube":
|
|
40
|
+
return "text-red-600 bg-red-50 hover:bg-red-100 border-red-100";
|
|
41
|
+
case "twitter":
|
|
42
|
+
return "text-sky-500 bg-sky-50 hover:bg-sky-100 border-sky-100";
|
|
43
|
+
default:
|
|
44
|
+
return "text-gray-500 bg-gray-50 border-gray-100";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className={cn("flex gap-2", className)}>
|
|
50
|
+
{platforms.map((p, i) => {
|
|
51
|
+
const Icon = getIcon(p.platform);
|
|
52
|
+
if (!Icon) return null;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<a
|
|
56
|
+
key={i}
|
|
57
|
+
href={p.url || "#"}
|
|
58
|
+
target="_blank"
|
|
59
|
+
rel="noopener noreferrer"
|
|
60
|
+
title={p.handle || p.platform}
|
|
61
|
+
className={cn(
|
|
62
|
+
"p-1.5 rounded-lg border transition-colors flex items-center justify-center",
|
|
63
|
+
getColors(p.platform),
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
<Icon className="w-4 h-4" />
|
|
67
|
+
</a>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./PlatformIconGroup";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { TopPostsGridMolecule } from "../../../types/molecules";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { Heart, MessageCircle, BarChart3 } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* TopPostsGrid
|
|
8
|
+
* Displays a grid of the creator's best-performing content pieces.
|
|
9
|
+
*/
|
|
10
|
+
export const TopPostsGrid: React.FC<TopPostsGridMolecule> = ({
|
|
11
|
+
posts,
|
|
12
|
+
className,
|
|
13
|
+
}) => {
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={cn(
|
|
17
|
+
"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
>
|
|
21
|
+
{posts.map((post, i) => (
|
|
22
|
+
<div
|
|
23
|
+
key={i}
|
|
24
|
+
className="group relative aspect-square overflow-hidden rounded-2xl bg-gray-100 border border-purple-50"
|
|
25
|
+
>
|
|
26
|
+
<img
|
|
27
|
+
src={post.thumbnail}
|
|
28
|
+
alt={`Post ${i}`}
|
|
29
|
+
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
|
30
|
+
/>
|
|
31
|
+
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity p-4 flex flex-col justify-end">
|
|
32
|
+
<div className="flex items-center gap-4 text-white text-xs font-bold">
|
|
33
|
+
<span className="flex items-center gap-1">
|
|
34
|
+
<Heart className="w-3 h-3 fill-white" /> {post.likes}
|
|
35
|
+
</span>
|
|
36
|
+
<span className="flex items-center gap-1">
|
|
37
|
+
<MessageCircle className="w-3 h-3 fill-white" /> {post.comments}
|
|
38
|
+
</span>
|
|
39
|
+
<span className="flex items-center gap-1">
|
|
40
|
+
<BarChart3 className="w-3 h-3" /> {post.engagement}
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
43
|
+
<span className="text-[10px] text-gray-300 mt-2">{post.date}</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./TopPostsGrid";
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
export * from "./CampaignSeedCard";
|
|
2
2
|
export * from "./SearchSpecCard";
|
|
3
3
|
export * from "./MCQCard";
|
|
4
|
+
export * from "./PlatformIconGroup";
|
|
5
|
+
export * from "./CreatorProfileSummary";
|
|
6
|
+
export * from "./AudienceMetricCard";
|
|
7
|
+
export * from "./CreatorGridCard";
|
|
8
|
+
export * from "./BrandAffinityGroup";
|
|
9
|
+
export * from "./ContentPreviewGallery";
|
|
10
|
+
export * from "./AudienceDemographicsCard";
|
|
11
|
+
export * from "./GrowthChartCard";
|
|
12
|
+
export * from "./TopPostsGrid";
|
|
13
|
+
export * from "./CreatorActionHeader";
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Table,
|
|
4
|
+
TableBody,
|
|
5
|
+
TableCell,
|
|
6
|
+
TableHead,
|
|
7
|
+
TableHeader,
|
|
8
|
+
TableRow,
|
|
9
|
+
} from "@/components/ui/table";
|
|
10
|
+
import { Badge } from "@/components/ui/badge";
|
|
11
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
12
|
+
import { DataGridMolecule } from "../../../types/molecules";
|
|
13
|
+
import { cn } from "@/lib/utils";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* DataGrid
|
|
17
|
+
* A premium table component with typed cells and responsive design.
|
|
18
|
+
*/
|
|
19
|
+
export const DataGrid: React.FC<DataGridMolecule> = ({
|
|
20
|
+
columns,
|
|
21
|
+
data,
|
|
22
|
+
pageSize = 10,
|
|
23
|
+
className,
|
|
24
|
+
}) => {
|
|
25
|
+
const renderCell = (item: any, column: any) => {
|
|
26
|
+
const value = item[column.accessorKey];
|
|
27
|
+
|
|
28
|
+
switch (column.type) {
|
|
29
|
+
case "badge":
|
|
30
|
+
return (
|
|
31
|
+
<Badge
|
|
32
|
+
variant="secondary"
|
|
33
|
+
className="bg-purple-50 text-purple-700 border-purple-100"
|
|
34
|
+
>
|
|
35
|
+
{value}
|
|
36
|
+
</Badge>
|
|
37
|
+
);
|
|
38
|
+
case "avatar":
|
|
39
|
+
return (
|
|
40
|
+
<div className="flex items-center gap-2">
|
|
41
|
+
<Avatar className="h-8 w-8">
|
|
42
|
+
<AvatarImage src={value} />
|
|
43
|
+
<AvatarFallback>{item.name?.charAt(0) || "U"}</AvatarFallback>
|
|
44
|
+
</Avatar>
|
|
45
|
+
{item.name && <span className="font-medium">{item.name}</span>}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
case "date":
|
|
49
|
+
return (
|
|
50
|
+
<span className="text-muted-foreground">
|
|
51
|
+
{new Date(value).toLocaleDateString()}
|
|
52
|
+
</span>
|
|
53
|
+
);
|
|
54
|
+
case "number":
|
|
55
|
+
return (
|
|
56
|
+
<span className="font-mono font-medium">
|
|
57
|
+
{value.toLocaleString()}
|
|
58
|
+
</span>
|
|
59
|
+
);
|
|
60
|
+
default:
|
|
61
|
+
return <span>{value}</span>;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
"rounded-3xl border border-purple-50 bg-white overflow-hidden shadow-sm",
|
|
69
|
+
className,
|
|
70
|
+
)}
|
|
71
|
+
>
|
|
72
|
+
<Table>
|
|
73
|
+
<TableHeader className="bg-gray-50/50">
|
|
74
|
+
<TableRow>
|
|
75
|
+
{columns.map((col) => (
|
|
76
|
+
<TableHead
|
|
77
|
+
key={col.accessorKey}
|
|
78
|
+
className="font-bold text-gray-900 h-12"
|
|
79
|
+
>
|
|
80
|
+
{col.header}
|
|
81
|
+
</TableHead>
|
|
82
|
+
))}
|
|
83
|
+
</TableRow>
|
|
84
|
+
</TableHeader>
|
|
85
|
+
<TableBody>
|
|
86
|
+
{data.slice(0, pageSize).map((item, idx) => (
|
|
87
|
+
<TableRow
|
|
88
|
+
key={idx}
|
|
89
|
+
className="hover:bg-purple-50/30 transition-colors border-gray-50"
|
|
90
|
+
>
|
|
91
|
+
{columns.map((col) => (
|
|
92
|
+
<TableCell key={col.accessorKey} className="py-4">
|
|
93
|
+
{renderCell(item, col)}
|
|
94
|
+
</TableCell>
|
|
95
|
+
))}
|
|
96
|
+
</TableRow>
|
|
97
|
+
))}
|
|
98
|
+
</TableBody>
|
|
99
|
+
</Table>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./DataGrid";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ButtonAtom } from "../../../atoms/ButtonAtom";
|
|
3
|
+
import { TextAtom } from "../../../atoms/TextAtom";
|
|
4
|
+
import { EmptyStateMolecule } from "../../../types/molecules";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import * as Icons from "lucide-react";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* EmptyState
|
|
10
|
+
* A placeholder for empty lists or results.
|
|
11
|
+
*/
|
|
12
|
+
export const EmptyState: React.FC<
|
|
13
|
+
EmptyStateMolecule & { onAction?: (action: string) => void }
|
|
14
|
+
> = ({
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
icon,
|
|
18
|
+
actionLabel,
|
|
19
|
+
action,
|
|
20
|
+
className,
|
|
21
|
+
onAction,
|
|
22
|
+
}) => {
|
|
23
|
+
const Icon = icon ? (Icons as any)[icon] : Icons.Search;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex flex-col items-center justify-center p-12 text-center rounded-3xl bg-purple-50/30 border-2 border-dashed border-purple-100",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
>
|
|
32
|
+
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-4">
|
|
33
|
+
<Icon className="w-8 h-8 text-purple-600" />
|
|
34
|
+
</div>
|
|
35
|
+
<TextAtom
|
|
36
|
+
id="empty-title"
|
|
37
|
+
type="text"
|
|
38
|
+
content={title}
|
|
39
|
+
variant="h3"
|
|
40
|
+
className="text-gray-900 mb-2"
|
|
41
|
+
/>
|
|
42
|
+
<TextAtom
|
|
43
|
+
id="empty-desc"
|
|
44
|
+
type="text"
|
|
45
|
+
content={description}
|
|
46
|
+
variant="p"
|
|
47
|
+
className="text-muted-foreground mb-6 max-w-sm"
|
|
48
|
+
/>
|
|
49
|
+
{actionLabel && action && (
|
|
50
|
+
<ButtonAtom
|
|
51
|
+
id="empty-action"
|
|
52
|
+
type="button"
|
|
53
|
+
label={actionLabel}
|
|
54
|
+
action={action}
|
|
55
|
+
variant="purple"
|
|
56
|
+
onAction={onAction}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./EmptyState";
|