pxengine 0.1.12 → 0.1.14
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/dist/index.cjs +227 -291
- package/dist/index.d.cts +15 -11
- package/dist/index.d.ts +15 -11
- package/dist/index.js +225 -287
- package/dist/registry.json +20 -22
- package/package.json +2 -4
- package/config/tailwind-preset.js +0 -106
- package/src/atoms/AccordionAtom.tsx +0 -44
- package/src/atoms/AlertAtom.tsx +0 -48
- package/src/atoms/AlertDialogAtom.tsx +0 -66
- package/src/atoms/AspectRatioAtom.tsx +0 -27
- package/src/atoms/AvatarAtom.tsx +0 -21
- package/src/atoms/BadgeAtom.tsx +0 -35
- package/src/atoms/BreadcrumbAtom.tsx +0 -36
- package/src/atoms/ButtonAtom.tsx +0 -65
- package/src/atoms/CalendarAtom.tsx +0 -24
- package/src/atoms/CardAtom.tsx +0 -66
- package/src/atoms/CarouselAtom.tsx +0 -40
- package/src/atoms/ChartAtom.tsx +0 -192
- package/src/atoms/CheckboxAtom.tsx +0 -33
- package/src/atoms/CollapsibleAtom.tsx +0 -44
- package/src/atoms/CommandAtom.tsx +0 -46
- package/src/atoms/ContextMenuAtom.tsx +0 -49
- package/src/atoms/DialogAtom.tsx +0 -68
- package/src/atoms/DrawerAtom.tsx +0 -49
- package/src/atoms/DropdownMenuAtom.tsx +0 -49
- package/src/atoms/FormInputAtom.tsx +0 -101
- package/src/atoms/FormSelectAtom.tsx +0 -110
- package/src/atoms/FormTextareaAtom.tsx +0 -93
- package/src/atoms/InputAtom.tsx +0 -216
- package/src/atoms/InputOTPAtom.tsx +0 -49
- package/src/atoms/KbdAtom.tsx +0 -25
- package/src/atoms/LabelAtom.tsx +0 -23
- package/src/atoms/LayoutAtom.tsx +0 -45
- package/src/atoms/PaginationAtom.tsx +0 -49
- package/src/atoms/PopoverAtom.tsx +0 -40
- package/src/atoms/ProgressAtom.tsx +0 -15
- package/src/atoms/RadioGroupAtom.tsx +0 -31
- package/src/atoms/RatingAtom.tsx +0 -37
- package/src/atoms/ResizableAtom.tsx +0 -51
- package/src/atoms/ScrollAreaAtom.tsx +0 -31
- package/src/atoms/SeparatorAtom.tsx +0 -16
- package/src/atoms/SheetAtom.tsx +0 -72
- package/src/atoms/SkeletonAtom.tsx +0 -22
- package/src/atoms/SliderAtom.tsx +0 -32
- package/src/atoms/SpinnerAtom.tsx +0 -26
- package/src/atoms/SwitchAtom.tsx +0 -32
- package/src/atoms/TableAtom.tsx +0 -60
- package/src/atoms/TabsAtom.tsx +0 -40
- package/src/atoms/TextAtom.tsx +0 -36
- package/src/atoms/TextareaAtom.tsx +0 -42
- package/src/atoms/TimelineAtom.tsx +0 -77
- package/src/atoms/ToggleAtom.tsx +0 -36
- package/src/atoms/TooltipAtom.tsx +0 -39
- package/src/atoms/VideoAtom.tsx +0 -34
- package/src/atoms/index.ts +0 -49
- package/src/components/index.ts +0 -178
- package/src/components/ui/accordion.tsx +0 -56
- package/src/components/ui/alert-dialog.tsx +0 -139
- package/src/components/ui/alert.tsx +0 -59
- package/src/components/ui/aspect-ratio.tsx +0 -5
- package/src/components/ui/avatar.tsx +0 -50
- package/src/components/ui/badge.tsx +0 -36
- package/src/components/ui/breadcrumb.tsx +0 -115
- package/src/components/ui/button-group.tsx +0 -83
- package/src/components/ui/button.tsx +0 -56
- package/src/components/ui/calendar.tsx +0 -213
- package/src/components/ui/card.tsx +0 -79
- package/src/components/ui/carousel.tsx +0 -260
- package/src/components/ui/chart.tsx +0 -367
- package/src/components/ui/checkbox.tsx +0 -28
- package/src/components/ui/collapsible.tsx +0 -11
- package/src/components/ui/command.tsx +0 -153
- package/src/components/ui/context-menu.tsx +0 -198
- package/src/components/ui/dialog.tsx +0 -122
- package/src/components/ui/drawer.tsx +0 -116
- package/src/components/ui/dropdown-menu.tsx +0 -200
- package/src/components/ui/empty.tsx +0 -104
- package/src/components/ui/field.tsx +0 -244
- package/src/components/ui/form.tsx +0 -176
- package/src/components/ui/hover-card.tsx +0 -27
- package/src/components/ui/index.ts +0 -54
- package/src/components/ui/input-group.tsx +0 -168
- package/src/components/ui/input-otp.tsx +0 -69
- package/src/components/ui/input.tsx +0 -22
- package/src/components/ui/item.tsx +0 -193
- package/src/components/ui/kbd.tsx +0 -28
- package/src/components/ui/label.tsx +0 -26
- package/src/components/ui/menubar.tsx +0 -254
- package/src/components/ui/navigation-menu.tsx +0 -128
- package/src/components/ui/pagination.tsx +0 -117
- package/src/components/ui/popover.tsx +0 -29
- package/src/components/ui/progress.tsx +0 -28
- package/src/components/ui/radio-group.tsx +0 -42
- package/src/components/ui/resizable.tsx +0 -44
- package/src/components/ui/scroll-area.tsx +0 -46
- package/src/components/ui/select.tsx +0 -160
- package/src/components/ui/separator.tsx +0 -29
- package/src/components/ui/sheet.tsx +0 -140
- package/src/components/ui/sidebar.tsx +0 -771
- package/src/components/ui/skeleton.tsx +0 -15
- package/src/components/ui/slider.tsx +0 -26
- package/src/components/ui/sonner.tsx +0 -45
- package/src/components/ui/spinner.tsx +0 -16
- package/src/components/ui/switch.tsx +0 -27
- package/src/components/ui/table.tsx +0 -117
- package/src/components/ui/tabs.tsx +0 -53
- package/src/components/ui/textarea.tsx +0 -22
- package/src/components/ui/toggle-group.tsx +0 -61
- package/src/components/ui/toggle.tsx +0 -43
- package/src/components/ui/tooltip.tsx +0 -30
- package/src/hooks/use-mobile.tsx +0 -19
- package/src/index.ts +0 -24
- package/src/lib/countries.ts +0 -203
- package/src/lib/index.ts +0 -2
- package/src/lib/utils.ts +0 -15
- package/src/lib/validators/index.ts +0 -1
- package/src/lib/validators/theme.ts +0 -148
- package/src/molecules/creator-discovery/AudienceDemographicsCard/AudienceDemographicsCard.tsx +0 -44
- package/src/molecules/creator-discovery/AudienceDemographicsCard/index.ts +0 -1
- package/src/molecules/creator-discovery/AudienceMetricCard/AudienceMetricCard.tsx +0 -50
- package/src/molecules/creator-discovery/AudienceMetricCard/index.ts +0 -1
- package/src/molecules/creator-discovery/BrandAffinityGroup/BrandAffinityGroup.tsx +0 -36
- package/src/molecules/creator-discovery/BrandAffinityGroup/index.ts +0 -1
- package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.tsx +0 -123
- package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.types.ts +0 -13
- package/src/molecules/creator-discovery/CampaignSeedCard/index.ts +0 -2
- package/src/molecules/creator-discovery/ContentPreviewGallery/ContentPreviewGallery.tsx +0 -41
- package/src/molecules/creator-discovery/ContentPreviewGallery/index.ts +0 -1
- package/src/molecules/creator-discovery/CreatorActionHeader/CreatorActionHeader.tsx +0 -77
- package/src/molecules/creator-discovery/CreatorActionHeader/index.ts +0 -1
- package/src/molecules/creator-discovery/CreatorGridCard/CreatorGridCard.tsx +0 -104
- package/src/molecules/creator-discovery/CreatorGridCard/index.ts +0 -1
- package/src/molecules/creator-discovery/CreatorProfileSummary/CreatorProfileSummary.tsx +0 -65
- package/src/molecules/creator-discovery/CreatorProfileSummary/index.ts +0 -1
- package/src/molecules/creator-discovery/GrowthChartCard/GrowthChartCard.tsx +0 -58
- package/src/molecules/creator-discovery/GrowthChartCard/index.ts +0 -1
- package/src/molecules/creator-discovery/MCQCard/MCQCard.tsx +0 -165
- package/src/molecules/creator-discovery/MCQCard/MCQCard.types.ts +0 -71
- package/src/molecules/creator-discovery/MCQCard/index.ts +0 -2
- package/src/molecules/creator-discovery/PlatformIconGroup/PlatformIconGroup.tsx +0 -72
- package/src/molecules/creator-discovery/PlatformIconGroup/index.ts +0 -1
- package/src/molecules/creator-discovery/SearchSpecCard/CustomFieldRenderers.tsx +0 -334
- package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.tsx +0 -111
- package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.types.ts +0 -18
- package/src/molecules/creator-discovery/SearchSpecCard/index.ts +0 -3
- package/src/molecules/creator-discovery/TopPostsGrid/TopPostsGrid.tsx +0 -49
- package/src/molecules/creator-discovery/TopPostsGrid/index.ts +0 -1
- package/src/molecules/creator-discovery/index.ts +0 -13
- package/src/molecules/generic/ActionButton/ActionButton.tsx +0 -137
- package/src/molecules/generic/ActionButton/ActionButton.types.ts +0 -68
- package/src/molecules/generic/ActionButton/index.ts +0 -2
- package/src/molecules/generic/DataGrid/DataGrid.tsx +0 -102
- package/src/molecules/generic/DataGrid/index.ts +0 -1
- package/src/molecules/generic/EditableField/EditableField.tsx +0 -229
- package/src/molecules/generic/EditableField/EditableField.types.ts +0 -73
- package/src/molecules/generic/EditableField/index.ts +0 -2
- package/src/molecules/generic/EmptyState/EmptyState.tsx +0 -61
- package/src/molecules/generic/EmptyState/index.ts +0 -1
- package/src/molecules/generic/FileUpload/FileUpload.tsx +0 -62
- package/src/molecules/generic/FileUpload/index.ts +0 -1
- package/src/molecules/generic/FilterBar/FilterBar.tsx +0 -54
- package/src/molecules/generic/FilterBar/index.ts +0 -1
- package/src/molecules/generic/FormCard/FormCard.tsx +0 -136
- package/src/molecules/generic/FormCard/FormCard.types.ts +0 -93
- package/src/molecules/generic/FormCard/index.ts +0 -2
- package/src/molecules/generic/LoadingOverlay/LoadingOverlay.tsx +0 -39
- package/src/molecules/generic/LoadingOverlay/index.ts +0 -1
- package/src/molecules/generic/NotificationList/NotificationList.tsx +0 -80
- package/src/molecules/generic/NotificationList/index.ts +0 -1
- package/src/molecules/generic/StatsGrid/StatsGrid.tsx +0 -80
- package/src/molecules/generic/StatsGrid/index.ts +0 -1
- package/src/molecules/generic/StepWizard/StepWizard.tsx +0 -67
- package/src/molecules/generic/StepWizard/index.ts +0 -1
- package/src/molecules/generic/TagCloud/TagCloud.tsx +0 -32
- package/src/molecules/generic/TagCloud/index.ts +0 -1
- package/src/molecules/generic/index.ts +0 -12
- package/src/molecules/index.ts +0 -2
- package/src/render/PXEngineRenderer.tsx +0 -458
- package/src/render/index.ts +0 -1
- package/src/styles/globals.css +0 -146
- package/src/types/atoms.ts +0 -450
- package/src/types/common.ts +0 -116
- package/src/types/index.ts +0 -3
- package/src/types/molecules.ts +0 -279
- package/src/types/schema.ts +0 -12
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { ButtonVariant, ButtonSize } from "@/types/common";
|
|
2
|
-
|
|
3
|
-
export interface ActionButtonProps {
|
|
4
|
-
/**
|
|
5
|
-
* Unique identifier
|
|
6
|
-
*/
|
|
7
|
-
id?: string;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Main button label
|
|
11
|
-
*/
|
|
12
|
-
label: string;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Secondary label (e.g. for sub-actions or state changes)
|
|
16
|
-
*/
|
|
17
|
-
secondaryLabel?: string;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Countdown in seconds. If provided, the button will show a timer.
|
|
21
|
-
*/
|
|
22
|
-
countdown?: number;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Whether the auto-proceed is paused
|
|
26
|
-
*/
|
|
27
|
-
isPaused?: boolean;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Triggered when the user pauses/resumes the timer
|
|
31
|
-
*/
|
|
32
|
-
onPause?: () => void;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Triggered when the user clicks the button or timer expires
|
|
36
|
-
*/
|
|
37
|
-
onProceed: () => void;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Button variant (from shadcn)
|
|
41
|
-
*/
|
|
42
|
-
variant?: ButtonVariant;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Button size (from shadcn)
|
|
46
|
-
*/
|
|
47
|
-
size?: ButtonSize;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Whether the button is disabled
|
|
51
|
-
*/
|
|
52
|
-
disabled?: boolean;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Loading state
|
|
56
|
-
*/
|
|
57
|
-
isLoading?: boolean;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Custom className
|
|
61
|
-
*/
|
|
62
|
-
className?: string;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Whether to show the countdown visually
|
|
66
|
-
*/
|
|
67
|
-
showCountdown?: boolean;
|
|
68
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./DataGrid";
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from "react";
|
|
2
|
-
import { EditableFieldProps } from "./EditableField.types";
|
|
3
|
-
import { cn } from "@/lib/utils";
|
|
4
|
-
import {
|
|
5
|
-
Button,
|
|
6
|
-
Input,
|
|
7
|
-
Label,
|
|
8
|
-
Textarea,
|
|
9
|
-
Select,
|
|
10
|
-
SelectContent,
|
|
11
|
-
SelectItem,
|
|
12
|
-
SelectTrigger,
|
|
13
|
-
SelectValue,
|
|
14
|
-
Slider,
|
|
15
|
-
} from "@/components";
|
|
16
|
-
import { Check, X, Pencil, Loader2 } from "lucide-react";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* EditableField
|
|
20
|
-
*
|
|
21
|
-
* A generic field that toggles between display and edit modes.
|
|
22
|
-
* Supports various input types and custom rendering.
|
|
23
|
-
*/
|
|
24
|
-
export const EditableField = React.memo<EditableFieldProps>(
|
|
25
|
-
({
|
|
26
|
-
label,
|
|
27
|
-
value,
|
|
28
|
-
type,
|
|
29
|
-
isEditing: isEditingProp,
|
|
30
|
-
onEdit,
|
|
31
|
-
onSave,
|
|
32
|
-
onCancel,
|
|
33
|
-
isSaving = false,
|
|
34
|
-
isChanged = false,
|
|
35
|
-
config = {},
|
|
36
|
-
className,
|
|
37
|
-
renderDisplay,
|
|
38
|
-
renderEdit,
|
|
39
|
-
}) => {
|
|
40
|
-
const [localValue, setLocalValue] = useState(value);
|
|
41
|
-
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);
|
|
42
|
-
|
|
43
|
-
// Sync local value when external value changes or editing starts
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
setLocalValue(value);
|
|
46
|
-
}, [value, isEditingProp]);
|
|
47
|
-
|
|
48
|
-
// Focus input when editing starts
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
if (isEditingProp) {
|
|
51
|
-
setTimeout(() => inputRef.current?.focus(), 0);
|
|
52
|
-
}
|
|
53
|
-
}, [isEditingProp]);
|
|
54
|
-
|
|
55
|
-
const handleSave = () => {
|
|
56
|
-
onSave?.(localValue);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
60
|
-
if (e.key === "Enter" && !e.shiftKey && type !== "textarea") {
|
|
61
|
-
e.preventDefault();
|
|
62
|
-
handleSave();
|
|
63
|
-
} else if (e.key === "Escape") {
|
|
64
|
-
onCancel?.();
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const renderInput = () => {
|
|
69
|
-
if (renderEdit) {
|
|
70
|
-
return renderEdit(localValue, setLocalValue);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
switch (type) {
|
|
74
|
-
case "textarea":
|
|
75
|
-
return (
|
|
76
|
-
<Textarea
|
|
77
|
-
ref={inputRef as React.RefObject<HTMLTextAreaElement>}
|
|
78
|
-
value={localValue || ""}
|
|
79
|
-
onChange={(e) => setLocalValue(e.target.value)}
|
|
80
|
-
onKeyDown={handleKeyDown}
|
|
81
|
-
placeholder={config.placeholder}
|
|
82
|
-
rows={config.rows || 3}
|
|
83
|
-
className="min-h-[80px] resize-none"
|
|
84
|
-
/>
|
|
85
|
-
);
|
|
86
|
-
case "select":
|
|
87
|
-
return (
|
|
88
|
-
<Select
|
|
89
|
-
value={localValue?.toString()}
|
|
90
|
-
onValueChange={(val) => setLocalValue(val)}
|
|
91
|
-
>
|
|
92
|
-
<SelectTrigger className="w-full">
|
|
93
|
-
<SelectValue
|
|
94
|
-
placeholder={config.placeholder || "Select an option"}
|
|
95
|
-
/>
|
|
96
|
-
</SelectTrigger>
|
|
97
|
-
<SelectContent>
|
|
98
|
-
{config.options?.map((opt: any) => {
|
|
99
|
-
const label = typeof opt === "string" ? opt : opt.label;
|
|
100
|
-
const val = typeof opt === "string" ? opt : opt.value;
|
|
101
|
-
return (
|
|
102
|
-
<SelectItem key={val} value={val}>
|
|
103
|
-
{label}
|
|
104
|
-
</SelectItem>
|
|
105
|
-
);
|
|
106
|
-
})}
|
|
107
|
-
</SelectContent>
|
|
108
|
-
</Select>
|
|
109
|
-
);
|
|
110
|
-
case "slider":
|
|
111
|
-
return (
|
|
112
|
-
<div className="pt-6 pb-2 px-2">
|
|
113
|
-
<Slider
|
|
114
|
-
defaultValue={[localValue?.min || 0, localValue?.max || 100]}
|
|
115
|
-
max={config.sliderConfig?.max || 100}
|
|
116
|
-
min={config.sliderConfig?.min || 0}
|
|
117
|
-
step={config.sliderConfig?.step || 1}
|
|
118
|
-
onValueChange={([min, max]) => setLocalValue({ min, max })}
|
|
119
|
-
/>
|
|
120
|
-
{config.sliderConfig?.formatValue && (
|
|
121
|
-
<div className="mt-2 text-xs text-muted-foreground text-center">
|
|
122
|
-
{config.sliderConfig.formatValue(localValue)}
|
|
123
|
-
</div>
|
|
124
|
-
)}
|
|
125
|
-
</div>
|
|
126
|
-
);
|
|
127
|
-
case "number":
|
|
128
|
-
return (
|
|
129
|
-
<Input
|
|
130
|
-
ref={inputRef as React.RefObject<HTMLInputElement>}
|
|
131
|
-
type="number"
|
|
132
|
-
value={localValue || ""}
|
|
133
|
-
onChange={(e) => setLocalValue(e.target.value)}
|
|
134
|
-
onKeyDown={handleKeyDown}
|
|
135
|
-
min={config.numberConfig?.min}
|
|
136
|
-
max={config.numberConfig?.max}
|
|
137
|
-
step={config.numberConfig?.step}
|
|
138
|
-
/>
|
|
139
|
-
);
|
|
140
|
-
default:
|
|
141
|
-
return (
|
|
142
|
-
<Input
|
|
143
|
-
ref={inputRef as React.RefObject<HTMLInputElement>}
|
|
144
|
-
type="text"
|
|
145
|
-
value={localValue || ""}
|
|
146
|
-
onChange={(e) => setLocalValue(e.target.value)}
|
|
147
|
-
onKeyDown={handleKeyDown}
|
|
148
|
-
placeholder={config.placeholder}
|
|
149
|
-
/>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const formattedValue = () => {
|
|
155
|
-
if (renderDisplay) return renderDisplay(value);
|
|
156
|
-
|
|
157
|
-
if (type === "slider") {
|
|
158
|
-
return config.sliderConfig?.formatValue
|
|
159
|
-
? config.sliderConfig.formatValue(value)
|
|
160
|
-
: `${value?.min} - ${value?.max}`;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (!value)
|
|
164
|
-
return <span className="text-muted-foreground italic">Not set</span>;
|
|
165
|
-
|
|
166
|
-
return value.toString();
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<div className={cn("group flex flex-col gap-1.5 py-2", className)}>
|
|
171
|
-
<div className="flex items-center justify-between">
|
|
172
|
-
<Label className="text-xs font-medium text-gray500 uppercase tracking-tight">
|
|
173
|
-
{label}
|
|
174
|
-
</Label>
|
|
175
|
-
{isChanged && !isEditingProp && (
|
|
176
|
-
<div
|
|
177
|
-
className="w-1.5 h-1.5 rounded-full bg-amber-500"
|
|
178
|
-
title="Unsaved changes"
|
|
179
|
-
/>
|
|
180
|
-
)}
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
{isEditingProp ? (
|
|
184
|
-
<div className="flex flex-col gap-2">
|
|
185
|
-
{renderInput()}
|
|
186
|
-
<div className="flex items-center justify-end gap-2">
|
|
187
|
-
<Button
|
|
188
|
-
size="icon"
|
|
189
|
-
variant="outline"
|
|
190
|
-
className="h-8 w-8 text-destructive border-destructive/20 hover:bg-destructive/10"
|
|
191
|
-
onClick={onCancel}
|
|
192
|
-
disabled={isSaving}
|
|
193
|
-
>
|
|
194
|
-
<X className="h-4 w-4" />
|
|
195
|
-
</Button>
|
|
196
|
-
<Button
|
|
197
|
-
size="icon"
|
|
198
|
-
className="h-8 w-8 bg-purple500 hover:bg-purple600 text-white"
|
|
199
|
-
onClick={handleSave}
|
|
200
|
-
disabled={isSaving}
|
|
201
|
-
>
|
|
202
|
-
{isSaving ? (
|
|
203
|
-
<Loader2 className="h-4 w-4 animate-spin" />
|
|
204
|
-
) : (
|
|
205
|
-
<Check className="h-4 w-4" />
|
|
206
|
-
)}
|
|
207
|
-
</Button>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
) : (
|
|
211
|
-
<div
|
|
212
|
-
className={cn(
|
|
213
|
-
"relative flex items-center justify-between rounded-md px-2 py-1.5 transition-all",
|
|
214
|
-
"hover:bg-gray-100/50 cursor-pointer border border-transparent hover:border-gray-200",
|
|
215
|
-
)}
|
|
216
|
-
onClick={onEdit}
|
|
217
|
-
>
|
|
218
|
-
<div className="text-sm text-gray-900 font-medium truncate flex-1 leading-relaxed">
|
|
219
|
-
{formattedValue()}
|
|
220
|
-
</div>
|
|
221
|
-
<Pencil className="h-3.5 w-3.5 text-gray-400 opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
222
|
-
</div>
|
|
223
|
-
)}
|
|
224
|
-
</div>
|
|
225
|
-
);
|
|
226
|
-
},
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
EditableField.displayName = "EditableField";
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { FieldType, FieldConfig } from "@/types/common";
|
|
2
|
-
|
|
3
|
-
export interface EditableFieldProps {
|
|
4
|
-
/**
|
|
5
|
-
* Unique identifier
|
|
6
|
-
*/
|
|
7
|
-
id?: string;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Field label
|
|
11
|
-
*/
|
|
12
|
-
label: string;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Field value
|
|
16
|
-
*/
|
|
17
|
-
value: any;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Input type (text, textarea, number, slider, etc.)
|
|
21
|
-
*/
|
|
22
|
-
type: FieldType;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Current editing state
|
|
26
|
-
*/
|
|
27
|
-
isEditing?: boolean;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Triggered when the user wants to start editing
|
|
31
|
-
*/
|
|
32
|
-
onEdit?: () => void;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Triggered when the user saves the new value
|
|
36
|
-
*/
|
|
37
|
-
onSave?: (newValue: any) => void;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Triggered when the user cancels editing
|
|
41
|
-
*/
|
|
42
|
-
onCancel?: () => void;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Loading state during save
|
|
46
|
-
*/
|
|
47
|
-
isSaving?: boolean;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Indicates if the value has been changed but not saved
|
|
51
|
-
*/
|
|
52
|
-
isChanged?: boolean;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Configuration for the specific field type
|
|
56
|
-
*/
|
|
57
|
-
config?: Partial<FieldConfig>;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Custom className for the container
|
|
61
|
-
*/
|
|
62
|
-
className?: string;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Custom renderer for the display state
|
|
66
|
-
*/
|
|
67
|
-
renderDisplay?: (value: any) => React.ReactNode;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Custom renderer for the edit state
|
|
71
|
-
*/
|
|
72
|
-
renderEdit?: (value: any, onChange: (v: any) => void) => React.ReactNode;
|
|
73
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./EmptyState";
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Upload } from "lucide-react";
|
|
3
|
-
import { FileUploadMolecule } from "../../../types/molecules";
|
|
4
|
-
import { cn } from "@/lib/utils";
|
|
5
|
-
import { Button } from "@/components/ui/button";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* FileUpload
|
|
9
|
-
* Drag-and-drop file upload zone.
|
|
10
|
-
*/
|
|
11
|
-
export const FileUpload: React.FC<
|
|
12
|
-
FileUploadMolecule & { onFilesSelected?: (files: File[]) => void }
|
|
13
|
-
> = ({ title, accept, multiple, className, onFilesSelected }) => {
|
|
14
|
-
const [isDragging, setIsDragging] = React.useState(false);
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<div
|
|
18
|
-
className={cn(
|
|
19
|
-
"relative flex flex-col items-center justify-center p-8 border-2 border-dashed rounded-3xl transition-all",
|
|
20
|
-
isDragging
|
|
21
|
-
? "border-purple-500 bg-purple-50"
|
|
22
|
-
: "border-purple-100 bg-white",
|
|
23
|
-
className,
|
|
24
|
-
)}
|
|
25
|
-
onDragOver={(e) => {
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
setIsDragging(true);
|
|
28
|
-
}}
|
|
29
|
-
onDragLeave={() => setIsDragging(false)}
|
|
30
|
-
onDrop={(e) => {
|
|
31
|
-
e.preventDefault();
|
|
32
|
-
setIsDragging(false);
|
|
33
|
-
if (e.dataTransfer.files) {
|
|
34
|
-
onFilesSelected?.(Array.from(e.dataTransfer.files));
|
|
35
|
-
}
|
|
36
|
-
}}
|
|
37
|
-
>
|
|
38
|
-
<div className="w-12 h-12 bg-purple-100 rounded-2xl flex items-center justify-center mb-4 text-purple-600">
|
|
39
|
-
<Upload className="w-6 h-6" />
|
|
40
|
-
</div>
|
|
41
|
-
<h4 className="font-bold text-gray-900 mb-1">{title}</h4>
|
|
42
|
-
<p className="text-sm text-muted-foreground mb-6">
|
|
43
|
-
Drag and drop or click to browse
|
|
44
|
-
</p>
|
|
45
|
-
<input
|
|
46
|
-
type="file"
|
|
47
|
-
multiple={multiple}
|
|
48
|
-
accept={accept}
|
|
49
|
-
className="absolute inset-0 opacity-0 cursor-pointer"
|
|
50
|
-
onChange={(e) =>
|
|
51
|
-
e.target.files && onFilesSelected?.(Array.from(e.target.files))
|
|
52
|
-
}
|
|
53
|
-
/>
|
|
54
|
-
<Button
|
|
55
|
-
variant="outline"
|
|
56
|
-
className="rounded-full border-purple-200 text-purple-700 hover:bg-purple-50"
|
|
57
|
-
>
|
|
58
|
-
Select Files
|
|
59
|
-
</Button>
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
62
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./FileUpload";
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Badge } from "@/components/ui/badge";
|
|
3
|
-
import { Input } from "@/components/ui/input";
|
|
4
|
-
import { Search } from "lucide-react";
|
|
5
|
-
import { FilterBarMolecule } from "../../../types/molecules";
|
|
6
|
-
import { cn } from "@/lib/utils";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* FilterBar
|
|
10
|
-
* A horizontal bar with active filter chips and search.
|
|
11
|
-
*/
|
|
12
|
-
export const FilterBar: React.FC<
|
|
13
|
-
FilterBarMolecule & {
|
|
14
|
-
onFilterToggle?: (val: string) => void;
|
|
15
|
-
onSearch?: (val: string) => void;
|
|
16
|
-
}
|
|
17
|
-
> = ({ filters, showSearch = true, className, onFilterToggle, onSearch }) => {
|
|
18
|
-
return (
|
|
19
|
-
<div
|
|
20
|
-
className={cn(
|
|
21
|
-
"flex flex-wrap items-center gap-3 p-2 bg-white/50 backdrop-blur-md border border-purple-50 rounded-2xl shadow-sm",
|
|
22
|
-
className,
|
|
23
|
-
)}
|
|
24
|
-
>
|
|
25
|
-
{showSearch && (
|
|
26
|
-
<div className="relative flex-1 min-w-[200px]">
|
|
27
|
-
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
|
28
|
-
<Input
|
|
29
|
-
placeholder="Search creators..."
|
|
30
|
-
className="pl-9 bg-transparent border-none focus-visible:ring-0 placeholder:text-muted-foreground/50"
|
|
31
|
-
onChange={(e) => onSearch?.(e.target.value)}
|
|
32
|
-
/>
|
|
33
|
-
</div>
|
|
34
|
-
)}
|
|
35
|
-
<div className="flex flex-wrap gap-2">
|
|
36
|
-
{filters.map((filter) => (
|
|
37
|
-
<Badge
|
|
38
|
-
key={filter.value}
|
|
39
|
-
variant={filter.active ? "default" : "outline"}
|
|
40
|
-
className={cn(
|
|
41
|
-
"cursor-pointer px-3 py-1 rounded-full transition-all",
|
|
42
|
-
filter.active
|
|
43
|
-
? "bg-purple-600 hover:bg-purple-700 border-none"
|
|
44
|
-
: "hover:bg-purple-50 hover:border-purple-200",
|
|
45
|
-
)}
|
|
46
|
-
onClick={() => onFilterToggle?.(filter.value)}
|
|
47
|
-
>
|
|
48
|
-
{filter.label}
|
|
49
|
-
</Badge>
|
|
50
|
-
))}
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./FilterBar";
|