pxengine 0.1.9 → 0.1.11

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.
Files changed (74) hide show
  1. package/README.md +79 -122
  2. package/dist/index.cjs +36909 -9518
  3. package/dist/index.d.cts +574 -33
  4. package/dist/index.d.ts +574 -33
  5. package/dist/index.js +35564 -8192
  6. package/dist/registry.json +1789 -126
  7. package/package.json +3 -3
  8. package/src/atoms/BadgeAtom.tsx +11 -3
  9. package/src/atoms/ButtonAtom.tsx +2 -2
  10. package/src/atoms/CalendarAtom.tsx +1 -1
  11. package/src/atoms/CardAtom.tsx +1 -1
  12. package/src/atoms/ChartAtom.tsx +190 -0
  13. package/src/atoms/CheckboxAtom.tsx +33 -0
  14. package/src/atoms/ContextMenuAtom.tsx +49 -0
  15. package/src/atoms/DrawerAtom.tsx +49 -0
  16. package/src/atoms/DropdownMenuAtom.tsx +49 -0
  17. package/src/atoms/InputAtom.tsx +49 -23
  18. package/src/atoms/InputOTPAtom.tsx +49 -0
  19. package/src/atoms/KbdAtom.tsx +25 -0
  20. package/src/atoms/LabelAtom.tsx +23 -0
  21. package/src/atoms/RadioGroupAtom.tsx +31 -0
  22. package/src/atoms/RatingAtom.tsx +37 -0
  23. package/src/atoms/ResizableAtom.tsx +51 -0
  24. package/src/atoms/SliderAtom.tsx +32 -0
  25. package/src/atoms/SwitchAtom.tsx +32 -0
  26. package/src/atoms/TextareaAtom.tsx +42 -0
  27. package/src/atoms/TimelineAtom.tsx +77 -0
  28. package/src/atoms/ToggleAtom.tsx +36 -0
  29. package/src/atoms/VideoAtom.tsx +34 -0
  30. package/src/atoms/index.ts +17 -0
  31. package/src/components/ui/resizable.tsx +5 -6
  32. package/src/molecules/creator-discovery/AudienceDemographicsCard/AudienceDemographicsCard.tsx +44 -0
  33. package/src/molecules/creator-discovery/AudienceDemographicsCard/index.ts +1 -0
  34. package/src/molecules/creator-discovery/AudienceMetricCard/AudienceMetricCard.tsx +50 -0
  35. package/src/molecules/creator-discovery/AudienceMetricCard/index.ts +1 -0
  36. package/src/molecules/creator-discovery/BrandAffinityGroup/BrandAffinityGroup.tsx +36 -0
  37. package/src/molecules/creator-discovery/BrandAffinityGroup/index.ts +1 -0
  38. package/src/molecules/creator-discovery/ContentPreviewGallery/ContentPreviewGallery.tsx +41 -0
  39. package/src/molecules/creator-discovery/ContentPreviewGallery/index.ts +1 -0
  40. package/src/molecules/creator-discovery/CreatorActionHeader/CreatorActionHeader.tsx +77 -0
  41. package/src/molecules/creator-discovery/CreatorActionHeader/index.ts +1 -0
  42. package/src/molecules/creator-discovery/CreatorGridCard/CreatorGridCard.tsx +104 -0
  43. package/src/molecules/creator-discovery/CreatorGridCard/index.ts +1 -0
  44. package/src/molecules/creator-discovery/CreatorProfileSummary/CreatorProfileSummary.tsx +65 -0
  45. package/src/molecules/creator-discovery/CreatorProfileSummary/index.ts +1 -0
  46. package/src/molecules/creator-discovery/GrowthChartCard/GrowthChartCard.tsx +58 -0
  47. package/src/molecules/creator-discovery/GrowthChartCard/index.ts +1 -0
  48. package/src/molecules/creator-discovery/PlatformIconGroup/PlatformIconGroup.tsx +72 -0
  49. package/src/molecules/creator-discovery/PlatformIconGroup/index.ts +1 -0
  50. package/src/molecules/creator-discovery/TopPostsGrid/TopPostsGrid.tsx +49 -0
  51. package/src/molecules/creator-discovery/TopPostsGrid/index.ts +1 -0
  52. package/src/molecules/creator-discovery/index.ts +10 -0
  53. package/src/molecules/generic/DataGrid/DataGrid.tsx +102 -0
  54. package/src/molecules/generic/DataGrid/index.ts +1 -0
  55. package/src/molecules/generic/EmptyState/EmptyState.tsx +61 -0
  56. package/src/molecules/generic/EmptyState/index.ts +1 -0
  57. package/src/molecules/generic/FileUpload/FileUpload.tsx +62 -0
  58. package/src/molecules/generic/FileUpload/index.ts +1 -0
  59. package/src/molecules/generic/FilterBar/FilterBar.tsx +54 -0
  60. package/src/molecules/generic/FilterBar/index.ts +1 -0
  61. package/src/molecules/generic/LoadingOverlay/LoadingOverlay.tsx +39 -0
  62. package/src/molecules/generic/LoadingOverlay/index.ts +1 -0
  63. package/src/molecules/generic/NotificationList/NotificationList.tsx +80 -0
  64. package/src/molecules/generic/NotificationList/index.ts +1 -0
  65. package/src/molecules/generic/StatsGrid/StatsGrid.tsx +80 -0
  66. package/src/molecules/generic/StatsGrid/index.ts +1 -0
  67. package/src/molecules/generic/StepWizard/StepWizard.tsx +67 -0
  68. package/src/molecules/generic/StepWizard/index.ts +1 -0
  69. package/src/molecules/generic/TagCloud/TagCloud.tsx +32 -0
  70. package/src/molecules/generic/TagCloud/index.ts +1 -0
  71. package/src/molecules/generic/index.ts +9 -0
  72. package/src/render/PXEngineRenderer.tsx +74 -2
  73. package/src/types/atoms.ts +150 -2
  74. 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";