pxengine 0.1.11 → 0.1.13

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 (186) hide show
  1. package/dist/index.cjs +205 -120
  2. package/dist/index.d.cts +1 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +205 -120
  5. package/dist/registry.json +1200 -288
  6. package/package.json +9 -4
  7. package/config/tailwind-preset.js +0 -106
  8. package/src/atoms/AccordionAtom.tsx +0 -44
  9. package/src/atoms/AlertAtom.tsx +0 -46
  10. package/src/atoms/AlertDialogAtom.tsx +0 -66
  11. package/src/atoms/AspectRatioAtom.tsx +0 -27
  12. package/src/atoms/AvatarAtom.tsx +0 -20
  13. package/src/atoms/BadgeAtom.tsx +0 -33
  14. package/src/atoms/BreadcrumbAtom.tsx +0 -36
  15. package/src/atoms/ButtonAtom.tsx +0 -63
  16. package/src/atoms/CalendarAtom.tsx +0 -24
  17. package/src/atoms/CardAtom.tsx +0 -64
  18. package/src/atoms/CarouselAtom.tsx +0 -40
  19. package/src/atoms/ChartAtom.tsx +0 -190
  20. package/src/atoms/CheckboxAtom.tsx +0 -33
  21. package/src/atoms/CollapsibleAtom.tsx +0 -44
  22. package/src/atoms/CommandAtom.tsx +0 -46
  23. package/src/atoms/ContextMenuAtom.tsx +0 -49
  24. package/src/atoms/DialogAtom.tsx +0 -68
  25. package/src/atoms/DrawerAtom.tsx +0 -49
  26. package/src/atoms/DropdownMenuAtom.tsx +0 -49
  27. package/src/atoms/FormInputAtom.tsx +0 -101
  28. package/src/atoms/FormSelectAtom.tsx +0 -110
  29. package/src/atoms/FormTextareaAtom.tsx +0 -93
  30. package/src/atoms/InputAtom.tsx +0 -188
  31. package/src/atoms/InputOTPAtom.tsx +0 -49
  32. package/src/atoms/KbdAtom.tsx +0 -25
  33. package/src/atoms/LabelAtom.tsx +0 -23
  34. package/src/atoms/LayoutAtom.tsx +0 -45
  35. package/src/atoms/PaginationAtom.tsx +0 -49
  36. package/src/atoms/PopoverAtom.tsx +0 -40
  37. package/src/atoms/ProgressAtom.tsx +0 -15
  38. package/src/atoms/RadioGroupAtom.tsx +0 -31
  39. package/src/atoms/RatingAtom.tsx +0 -37
  40. package/src/atoms/ResizableAtom.tsx +0 -51
  41. package/src/atoms/ScrollAreaAtom.tsx +0 -31
  42. package/src/atoms/SeparatorAtom.tsx +0 -16
  43. package/src/atoms/SheetAtom.tsx +0 -72
  44. package/src/atoms/SkeletonAtom.tsx +0 -22
  45. package/src/atoms/SliderAtom.tsx +0 -32
  46. package/src/atoms/SpinnerAtom.tsx +0 -26
  47. package/src/atoms/SwitchAtom.tsx +0 -32
  48. package/src/atoms/TableAtom.tsx +0 -58
  49. package/src/atoms/TabsAtom.tsx +0 -40
  50. package/src/atoms/TextAtom.tsx +0 -35
  51. package/src/atoms/TextareaAtom.tsx +0 -42
  52. package/src/atoms/TimelineAtom.tsx +0 -77
  53. package/src/atoms/ToggleAtom.tsx +0 -36
  54. package/src/atoms/TooltipAtom.tsx +0 -39
  55. package/src/atoms/VideoAtom.tsx +0 -34
  56. package/src/atoms/index.ts +0 -49
  57. package/src/components/index.ts +0 -178
  58. package/src/components/ui/accordion.tsx +0 -56
  59. package/src/components/ui/alert-dialog.tsx +0 -139
  60. package/src/components/ui/alert.tsx +0 -59
  61. package/src/components/ui/aspect-ratio.tsx +0 -5
  62. package/src/components/ui/avatar.tsx +0 -50
  63. package/src/components/ui/badge.tsx +0 -36
  64. package/src/components/ui/breadcrumb.tsx +0 -115
  65. package/src/components/ui/button-group.tsx +0 -83
  66. package/src/components/ui/button.tsx +0 -56
  67. package/src/components/ui/calendar.tsx +0 -213
  68. package/src/components/ui/card.tsx +0 -79
  69. package/src/components/ui/carousel.tsx +0 -260
  70. package/src/components/ui/chart.tsx +0 -367
  71. package/src/components/ui/checkbox.tsx +0 -28
  72. package/src/components/ui/collapsible.tsx +0 -11
  73. package/src/components/ui/command.tsx +0 -153
  74. package/src/components/ui/context-menu.tsx +0 -198
  75. package/src/components/ui/dialog.tsx +0 -122
  76. package/src/components/ui/drawer.tsx +0 -116
  77. package/src/components/ui/dropdown-menu.tsx +0 -200
  78. package/src/components/ui/empty.tsx +0 -104
  79. package/src/components/ui/field.tsx +0 -244
  80. package/src/components/ui/form.tsx +0 -176
  81. package/src/components/ui/hover-card.tsx +0 -27
  82. package/src/components/ui/index.ts +0 -54
  83. package/src/components/ui/input-group.tsx +0 -168
  84. package/src/components/ui/input-otp.tsx +0 -69
  85. package/src/components/ui/input.tsx +0 -22
  86. package/src/components/ui/item.tsx +0 -193
  87. package/src/components/ui/kbd.tsx +0 -28
  88. package/src/components/ui/label.tsx +0 -26
  89. package/src/components/ui/menubar.tsx +0 -254
  90. package/src/components/ui/navigation-menu.tsx +0 -128
  91. package/src/components/ui/pagination.tsx +0 -117
  92. package/src/components/ui/popover.tsx +0 -29
  93. package/src/components/ui/progress.tsx +0 -28
  94. package/src/components/ui/radio-group.tsx +0 -42
  95. package/src/components/ui/resizable.tsx +0 -44
  96. package/src/components/ui/scroll-area.tsx +0 -46
  97. package/src/components/ui/select.tsx +0 -160
  98. package/src/components/ui/separator.tsx +0 -29
  99. package/src/components/ui/sheet.tsx +0 -140
  100. package/src/components/ui/sidebar.tsx +0 -771
  101. package/src/components/ui/skeleton.tsx +0 -15
  102. package/src/components/ui/slider.tsx +0 -26
  103. package/src/components/ui/sonner.tsx +0 -45
  104. package/src/components/ui/spinner.tsx +0 -16
  105. package/src/components/ui/switch.tsx +0 -27
  106. package/src/components/ui/table.tsx +0 -117
  107. package/src/components/ui/tabs.tsx +0 -53
  108. package/src/components/ui/textarea.tsx +0 -22
  109. package/src/components/ui/toggle-group.tsx +0 -61
  110. package/src/components/ui/toggle.tsx +0 -43
  111. package/src/components/ui/tooltip.tsx +0 -30
  112. package/src/hooks/use-mobile.tsx +0 -19
  113. package/src/index.ts +0 -24
  114. package/src/lib/countries.ts +0 -203
  115. package/src/lib/index.ts +0 -2
  116. package/src/lib/utils.ts +0 -15
  117. package/src/lib/validators/index.ts +0 -1
  118. package/src/lib/validators/theme.ts +0 -148
  119. package/src/molecules/creator-discovery/AudienceDemographicsCard/AudienceDemographicsCard.tsx +0 -44
  120. package/src/molecules/creator-discovery/AudienceDemographicsCard/index.ts +0 -1
  121. package/src/molecules/creator-discovery/AudienceMetricCard/AudienceMetricCard.tsx +0 -50
  122. package/src/molecules/creator-discovery/AudienceMetricCard/index.ts +0 -1
  123. package/src/molecules/creator-discovery/BrandAffinityGroup/BrandAffinityGroup.tsx +0 -36
  124. package/src/molecules/creator-discovery/BrandAffinityGroup/index.ts +0 -1
  125. package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.tsx +0 -123
  126. package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.types.ts +0 -13
  127. package/src/molecules/creator-discovery/CampaignSeedCard/index.ts +0 -2
  128. package/src/molecules/creator-discovery/ContentPreviewGallery/ContentPreviewGallery.tsx +0 -41
  129. package/src/molecules/creator-discovery/ContentPreviewGallery/index.ts +0 -1
  130. package/src/molecules/creator-discovery/CreatorActionHeader/CreatorActionHeader.tsx +0 -77
  131. package/src/molecules/creator-discovery/CreatorActionHeader/index.ts +0 -1
  132. package/src/molecules/creator-discovery/CreatorGridCard/CreatorGridCard.tsx +0 -104
  133. package/src/molecules/creator-discovery/CreatorGridCard/index.ts +0 -1
  134. package/src/molecules/creator-discovery/CreatorProfileSummary/CreatorProfileSummary.tsx +0 -65
  135. package/src/molecules/creator-discovery/CreatorProfileSummary/index.ts +0 -1
  136. package/src/molecules/creator-discovery/GrowthChartCard/GrowthChartCard.tsx +0 -58
  137. package/src/molecules/creator-discovery/GrowthChartCard/index.ts +0 -1
  138. package/src/molecules/creator-discovery/MCQCard/MCQCard.tsx +0 -165
  139. package/src/molecules/creator-discovery/MCQCard/MCQCard.types.ts +0 -71
  140. package/src/molecules/creator-discovery/MCQCard/index.ts +0 -2
  141. package/src/molecules/creator-discovery/PlatformIconGroup/PlatformIconGroup.tsx +0 -72
  142. package/src/molecules/creator-discovery/PlatformIconGroup/index.ts +0 -1
  143. package/src/molecules/creator-discovery/SearchSpecCard/CustomFieldRenderers.tsx +0 -334
  144. package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.tsx +0 -111
  145. package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.types.ts +0 -18
  146. package/src/molecules/creator-discovery/SearchSpecCard/index.ts +0 -3
  147. package/src/molecules/creator-discovery/TopPostsGrid/TopPostsGrid.tsx +0 -49
  148. package/src/molecules/creator-discovery/TopPostsGrid/index.ts +0 -1
  149. package/src/molecules/creator-discovery/index.ts +0 -13
  150. package/src/molecules/generic/ActionButton/ActionButton.tsx +0 -137
  151. package/src/molecules/generic/ActionButton/ActionButton.types.ts +0 -68
  152. package/src/molecules/generic/ActionButton/index.ts +0 -2
  153. package/src/molecules/generic/DataGrid/DataGrid.tsx +0 -102
  154. package/src/molecules/generic/DataGrid/index.ts +0 -1
  155. package/src/molecules/generic/EditableField/EditableField.tsx +0 -229
  156. package/src/molecules/generic/EditableField/EditableField.types.ts +0 -73
  157. package/src/molecules/generic/EditableField/index.ts +0 -2
  158. package/src/molecules/generic/EmptyState/EmptyState.tsx +0 -61
  159. package/src/molecules/generic/EmptyState/index.ts +0 -1
  160. package/src/molecules/generic/FileUpload/FileUpload.tsx +0 -62
  161. package/src/molecules/generic/FileUpload/index.ts +0 -1
  162. package/src/molecules/generic/FilterBar/FilterBar.tsx +0 -54
  163. package/src/molecules/generic/FilterBar/index.ts +0 -1
  164. package/src/molecules/generic/FormCard/FormCard.tsx +0 -136
  165. package/src/molecules/generic/FormCard/FormCard.types.ts +0 -93
  166. package/src/molecules/generic/FormCard/index.ts +0 -2
  167. package/src/molecules/generic/LoadingOverlay/LoadingOverlay.tsx +0 -39
  168. package/src/molecules/generic/LoadingOverlay/index.ts +0 -1
  169. package/src/molecules/generic/NotificationList/NotificationList.tsx +0 -80
  170. package/src/molecules/generic/NotificationList/index.ts +0 -1
  171. package/src/molecules/generic/StatsGrid/StatsGrid.tsx +0 -80
  172. package/src/molecules/generic/StatsGrid/index.ts +0 -1
  173. package/src/molecules/generic/StepWizard/StepWizard.tsx +0 -67
  174. package/src/molecules/generic/StepWizard/index.ts +0 -1
  175. package/src/molecules/generic/TagCloud/TagCloud.tsx +0 -32
  176. package/src/molecules/generic/TagCloud/index.ts +0 -1
  177. package/src/molecules/generic/index.ts +0 -12
  178. package/src/molecules/index.ts +0 -2
  179. package/src/render/PXEngineRenderer.tsx +0 -416
  180. package/src/render/index.ts +0 -1
  181. package/src/styles/globals.css +0 -146
  182. package/src/types/atoms.ts +0 -449
  183. package/src/types/common.ts +0 -116
  184. package/src/types/index.ts +0 -3
  185. package/src/types/molecules.ts +0 -279
  186. package/src/types/schema.ts +0 -12
@@ -1,334 +0,0 @@
1
- import { useState, useRef, useEffect } from "react";
2
- import { ChevronDown, Check } from "lucide-react";
3
- import { countries } from "@/lib/countries";
4
- import { Badge } from "@/components";
5
- import { cn } from "@/lib/utils";
6
-
7
- // ============================================================================
8
- // COUNTRY SELECT FIELD
9
- // ============================================================================
10
-
11
- export const CountrySelectEdit = ({
12
- value,
13
- onChange,
14
- }: {
15
- value: any;
16
- onChange: (value: any) => void;
17
- }) => {
18
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
19
- const [searchTerm, setSearchTerm] = useState("");
20
- const dropdownRef = useRef<HTMLDivElement>(null);
21
-
22
- useEffect(() => {
23
- const handleClickOutside = (event: MouseEvent) => {
24
- if (
25
- dropdownRef.current &&
26
- !dropdownRef.current.contains(event.target as Node)
27
- ) {
28
- setIsDropdownOpen(false);
29
- }
30
- };
31
- document.addEventListener("mousedown", handleClickOutside);
32
- return () => document.removeEventListener("mousedown", handleClickOutside);
33
- }, []);
34
-
35
- const inputValue = Array.isArray(value) ? value : [];
36
-
37
- return (
38
- <div className="space-y-3">
39
- <div className="relative" ref={dropdownRef}>
40
- <div
41
- className="flex-1 bg-white border border-gray200 rounded-md px-3 py-2 text-sm cursor-pointer flex items-center justify-between font-medium hover:border-purple500 transition-colors"
42
- onClick={() => setIsDropdownOpen(!isDropdownOpen)}
43
- >
44
- <span className="text-gray-700">
45
- {inputValue.length > 0
46
- ? `${inputValue.length} ${inputValue.length === 1 ? "country" : "countries"} selected`
47
- : "Select countries..."}
48
- </span>
49
- <ChevronDown
50
- className={cn(
51
- "h-4 w-4 text-gray-400 transition-transform",
52
- isDropdownOpen && "rotate-180",
53
- )}
54
- />
55
- </div>
56
-
57
- {isDropdownOpen && (
58
- <div className="absolute top-full left-0 right-0 mt-1 bg-white border border-gray200 rounded-lg shadow-xl z-50 max-h-60 overflow-hidden animate-in fade-in slide-in-from-top-1">
59
- <div className="p-2 border-b border-gray-100">
60
- <input
61
- type="text"
62
- placeholder="Search countries..."
63
- value={searchTerm}
64
- onChange={(e) => setSearchTerm(e.target.value)}
65
- className="w-full px-3 py-1.5 text-sm border border-gray-100 rounded-md bg-gray-50 focus:outline-none focus:ring-2 focus:ring-purple500/20 focus:border-purple500"
66
- onClick={(e) => e.stopPropagation()}
67
- />
68
- </div>
69
-
70
- <div className="max-h-40 overflow-y-auto p-1">
71
- {countries
72
- .filter(
73
- (country) =>
74
- country.name
75
- .toLowerCase()
76
- .includes(searchTerm.toLowerCase()) ||
77
- country.code
78
- .toLowerCase()
79
- .includes(searchTerm.toLowerCase()),
80
- )
81
- .map((country) => (
82
- <div
83
- key={country.code}
84
- className={cn(
85
- "flex items-center justify-between px-3 py-2 rounded-md hover:bg-purple50 cursor-pointer transition-colors",
86
- inputValue.includes(country.code) && "bg-purple50/50",
87
- )}
88
- onClick={(e) => {
89
- e.stopPropagation();
90
- const currentGeography = inputValue;
91
- if (currentGeography.includes(country.code)) {
92
- onChange(
93
- currentGeography.filter(
94
- (c: string) => c !== country.code,
95
- ),
96
- );
97
- } else {
98
- onChange([...currentGeography, country.code]);
99
- }
100
- }}
101
- >
102
- <div className="flex items-center gap-2">
103
- <span className="text-sm text-gray-700">
104
- {country.name}
105
- </span>
106
- <span className="text-xs text-gray-400 font-mono">
107
- {country.code}
108
- </span>
109
- </div>
110
- {inputValue.includes(country.code) && (
111
- <Check className="h-4 w-4 text-purple500" />
112
- )}
113
- </div>
114
- ))}
115
- </div>
116
- </div>
117
- )}
118
- </div>
119
-
120
- {inputValue.length > 0 && (
121
- <div className="flex flex-wrap gap-1.5">
122
- {inputValue.map((countryCode: string) => (
123
- <Badge
124
- key={countryCode}
125
- variant="secondary"
126
- className="bg-purple50 text-purple700 border-purple100 hover:bg-purple100 cursor-default flex items-center gap-1 pr-1"
127
- >
128
- {countryCode}
129
- <button
130
- onClick={() =>
131
- onChange(inputValue.filter((c: string) => c !== countryCode))
132
- }
133
- className="hover:bg-purple200 rounded-full p-0.5 transition-colors"
134
- >
135
- <ChevronDown className="h-3 w-3 rotate-45" />{" "}
136
- {/* Close icon substitute */}
137
- </button>
138
- </Badge>
139
- ))}
140
- </div>
141
- )}
142
- </div>
143
- );
144
- };
145
-
146
- export const CountrySelectDisplay = ({ value }: { value: any }) => {
147
- if (!value || !Array.isArray(value) || value.length === 0) {
148
- return <span className="text-muted-foreground italic">Not specified</span>;
149
- }
150
-
151
- return (
152
- <div className="flex flex-wrap gap-1.5">
153
- {value.map((countryCode: string) => (
154
- <Badge
155
- key={countryCode}
156
- variant="outline"
157
- className="bg-gray-50 text-gray-700 border-gray-200"
158
- >
159
- {countryCode}
160
- </Badge>
161
- ))}
162
- </div>
163
- );
164
- };
165
-
166
- // ============================================================================
167
- // KEYWORD BUNDLES FIELD
168
- // ============================================================================
169
-
170
- export const KeywordBundlesEdit = ({
171
- value,
172
- onChange,
173
- }: {
174
- value: any;
175
- onChange: (value: any) => void;
176
- }) => {
177
- const bundles = Array.isArray(value) ? value : [];
178
-
179
- const groups: { [priority: number]: { bundle: any; index: number }[] } = {};
180
- bundles.forEach((b: any, idx: number) => {
181
- const p = Number(b?.priority) || 1;
182
- if (!groups[p]) groups[p] = [];
183
- groups[p].push({ bundle: b, index: idx });
184
- });
185
-
186
- const sortedPriorities = Object.keys(groups)
187
- .map((n) => parseInt(n))
188
- .sort((a, b) => a - b);
189
-
190
- return (
191
- <div className="space-y-6 pt-2">
192
- {sortedPriorities.map((priority) => (
193
- <div key={priority} className="space-y-3">
194
- <div className="flex items-center gap-2">
195
- <Badge className="bg-purple500 hover:bg-purple500">
196
- Priority {priority}
197
- </Badge>
198
- <div className="h-px flex-1 bg-gray-100" />
199
- </div>
200
- {groups[priority].map(({ bundle, index: bundleIndex }) => (
201
- <div
202
- key={bundleIndex}
203
- className="bg-gray-50/50 border border-gray-100 rounded-xl p-4 transition-all hover:bg-gray-50 hover:border-gray-200 shadow-sm"
204
- >
205
- <div className="flex flex-col gap-3">
206
- <label className="text-[10px] font-bold text-gray-400 uppercase tracking-widest pl-1">
207
- Keywords
208
- </label>
209
- <div className="space-y-3">
210
- <div className="flex flex-wrap gap-1.5">
211
- {Array.isArray(bundle.keywords) &&
212
- bundle.keywords.map((keyword: string, kIndex: number) => (
213
- <Badge
214
- key={kIndex}
215
- className="bg-white border-gray-200 text-gray-700 hover:bg-red-50 hover:text-red-600 hover:border-red-100 transition-all cursor-pointer group"
216
- onClick={() => {
217
- const updatedBundles = [...bundles];
218
- updatedBundles[bundleIndex] = {
219
- ...bundle,
220
- keywords: bundle.keywords.filter(
221
- (_: string, i: number) => i !== kIndex,
222
- ),
223
- };
224
- onChange(updatedBundles);
225
- }}
226
- >
227
- {keyword}
228
- <span className="ml-1 opacity-0 group-hover:opacity-100 transition-opacity">
229
- ×
230
- </span>
231
- </Badge>
232
- ))}
233
- </div>
234
- <div className="flex gap-2">
235
- <input
236
- type="text"
237
- placeholder="Add keyword..."
238
- className="flex-1 px-3 py-1.5 text-sm border border-gray-200 rounded-md bg-white focus:outline-none focus:ring-2 focus:ring-purple-500/20 focus:border-purple-500"
239
- onKeyPress={(e) => {
240
- if (e.key === "Enter") {
241
- const input = e.target as HTMLInputElement;
242
- const val = input.value.trim();
243
- if (val) {
244
- const updatedBundles = [...bundles];
245
- updatedBundles[bundleIndex] = {
246
- ...bundle,
247
- keywords: [...(bundle.keywords || []), val],
248
- };
249
- onChange(updatedBundles);
250
- input.value = "";
251
- }
252
- }
253
- }}
254
- />
255
- </div>
256
- </div>
257
- <div className="flex flex-col gap-1.5 mt-1">
258
- <label className="text-[10px] font-bold text-gray-400 uppercase tracking-widest pl-1">
259
- Priority Level (1-5)
260
- </label>
261
- <input
262
- type="number"
263
- min="1"
264
- max="5"
265
- value={bundle.priority || 1}
266
- onChange={(e) => {
267
- const updatedBundles = [...bundles];
268
- updatedBundles[bundleIndex] = {
269
- ...bundle,
270
- priority: parseInt(e.target.value) || 1,
271
- };
272
- onChange(updatedBundles);
273
- }}
274
- className="w-20 px-3 py-1.5 text-sm border border-gray-200 rounded-md bg-white focus:ring-2 focus:ring-purple-500/20 focus:border-purple-500 outline-none"
275
- />
276
- </div>
277
- </div>
278
- </div>
279
- ))}
280
- </div>
281
- ))}
282
- </div>
283
- );
284
- };
285
-
286
- export const KeywordBundlesDisplay = ({ value }: { value: any }) => {
287
- const bundles = Array.isArray(value) ? value : [];
288
- if (bundles.length === 0)
289
- return <span className="text-muted-foreground italic">Not specified</span>;
290
-
291
- const groups: { [priority: number]: string[] } = {};
292
- bundles.forEach((b: any) => {
293
- const p = Number(b?.priority) || 1;
294
- const keywords = Array.isArray(b?.keywords) ? b.keywords : [];
295
- if (!groups[p]) groups[p] = [];
296
- groups[p].push(...keywords);
297
- });
298
-
299
- const sortedPriorities = Object.keys(groups)
300
- .map((n) => parseInt(n))
301
- .sort((a, b) => a - b);
302
-
303
- return (
304
- <div className="space-y-4 pt-1">
305
- {sortedPriorities.map((priority) => {
306
- const deduped = Array.from(new Set(groups[priority]));
307
- return (
308
- <div key={priority} className="space-y-2">
309
- <div className="flex items-center gap-2">
310
- <Badge
311
- variant="outline"
312
- className="text-[10px] uppercase tracking-wider text-purple600 border-purple100 bg-purple50/30"
313
- >
314
- Priority {priority}
315
- </Badge>
316
- <div className="h-px flex-1 bg-gray-100/50" />
317
- </div>
318
- <div className="flex flex-wrap gap-1.5">
319
- {deduped.map((keyword: string) => (
320
- <Badge
321
- key={keyword}
322
- variant="secondary"
323
- className="bg-white border-gray-200 text-gray-700 font-medium"
324
- >
325
- {keyword}
326
- </Badge>
327
- ))}
328
- </div>
329
- </div>
330
- );
331
- })}
332
- </div>
333
- );
334
- };
@@ -1,111 +0,0 @@
1
- import React from "react";
2
- import { SearchSpecCardProps } from "./SearchSpecCard.types";
3
- import { FormCard } from "../../generic/FormCard";
4
- import { FieldConfig } from "@/types/common";
5
- import {
6
- CountrySelectEdit,
7
- CountrySelectDisplay,
8
- KeywordBundlesEdit,
9
- KeywordBundlesDisplay,
10
- } from "./CustomFieldRenderers";
11
- import { CheckCircle2 } from "lucide-react";
12
-
13
- /**
14
- * Default field configuration for Search Specification
15
- */
16
- export const SEARCH_SPEC_FIELDS: FieldConfig[] = [
17
- {
18
- key: "platforms",
19
- label: "Platforms",
20
- type: "select", // Changed to select for simplicity in form, or custom if multi-select needed
21
- placeholder: "Select platforms",
22
- options: ["Instagram", "YouTube", "TikTok"],
23
- },
24
- {
25
- key: "follower_range",
26
- label: "Follower Range",
27
- type: "slider",
28
- placeholder: "Not specified",
29
- sliderConfig: {
30
- min: 0,
31
- max: 5000000,
32
- step: 10000,
33
- formatValue: (value: any) => {
34
- if (!value || typeof value !== "object") return "0 - 2.5M followers";
35
- const formatNum = (n: number) => {
36
- if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`;
37
- if (n >= 1000) return `${(n / 1000).toFixed(0)}K`;
38
- return n.toLocaleString();
39
- };
40
- return `${formatNum(value.min || 0)} - ${formatNum(value.max || 2500000)} followers`;
41
- },
42
- },
43
- },
44
- {
45
- key: "geography",
46
- label: "Geography",
47
- type: "custom",
48
- renderEdit: (value, onChange) => (
49
- <CountrySelectEdit value={value} onChange={onChange} />
50
- ),
51
- renderDisplay: (value) => <CountrySelectDisplay value={value} />,
52
- },
53
- {
54
- key: "keyword_bundles",
55
- label: "Keyword Bundles",
56
- type: "custom",
57
- renderEdit: (value, onChange) => (
58
- <KeywordBundlesEdit value={value} onChange={onChange} />
59
- ),
60
- renderDisplay: (value) => <KeywordBundlesDisplay value={value} />,
61
- },
62
- {
63
- key: "desired_candidate_count",
64
- label: "Initial Creators",
65
- type: "number",
66
- numberConfig: {
67
- min: 1,
68
- formatValue: (value: any) => `${value || 0} creators`,
69
- },
70
- },
71
- ];
72
-
73
- /**
74
- * SearchSpecCard
75
- *
76
- * A domain-specific molecule for the Creator Discovery workflow.
77
- * Encapsulates search settings like platforms, ranges, and custom keyword bundles.
78
- */
79
- export const SearchSpecCard = React.memo<SearchSpecCardProps>(
80
- ({
81
- selectionStatus,
82
- isLatestMessage = true,
83
- className,
84
- ...formCardProps
85
- }) => {
86
- return (
87
- <FormCard
88
- {...formCardProps}
89
- title={formCardProps.title || "Creator Search Settings"}
90
- fields={SEARCH_SPEC_FIELDS}
91
- className={className}
92
- footer={
93
- !isLatestMessage && selectionStatus ? (
94
- <div className="flex justify-end items-center gap-1.5 text-green-600 text-xs font-semibold py-1">
95
- <CheckCircle2 className="h-4 w-4" />
96
- <span>
97
- {selectionStatus === "agent"
98
- ? "Selected by Agent"
99
- : "Selected by User"}
100
- </span>
101
- </div>
102
- ) : (
103
- formCardProps.footer
104
- )
105
- }
106
- />
107
- );
108
- },
109
- );
110
-
111
- SearchSpecCard.displayName = "SearchSpecCard";
@@ -1,18 +0,0 @@
1
- import { FormCardProps } from "../../generic/FormCard";
2
-
3
- export interface SearchSpecCardProps extends Omit<FormCardProps, "fields"> {
4
- /**
5
- * Version of the search specification
6
- */
7
- version?: number;
8
-
9
- /**
10
- * Status of the selection
11
- */
12
- selectionStatus?: "user" | "agent";
13
-
14
- /**
15
- * Whether the message is the latest
16
- */
17
- isLatestMessage?: boolean;
18
- }
@@ -1,3 +0,0 @@
1
- export * from "./SearchSpecCard";
2
- export * from "./SearchSpecCard.types";
3
- export * from "./CustomFieldRenderers";
@@ -1,49 +0,0 @@
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
- };
@@ -1 +0,0 @@
1
- export * from "./TopPostsGrid";
@@ -1,13 +0,0 @@
1
- export * from "./CampaignSeedCard";
2
- export * from "./SearchSpecCard";
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";
@@ -1,137 +0,0 @@
1
- import React, { useState, useEffect } from "react";
2
- import { ActionButtonProps } from "./ActionButton.types";
3
- import { Button } from "@/components";
4
- import { cn } from "@/lib/utils";
5
- import { Pause, Play, ArrowRight, Loader2 } from "lucide-react";
6
-
7
- /**
8
- * ActionButton
9
- *
10
- * A powerful button molecule that supports:
11
- * - Auto-countdown for agent workflows
12
- * - Pause/Resume toggle
13
- * - Loading and Disabled states
14
- * - Custom themes
15
- */
16
- export const ActionButton = React.memo<ActionButtonProps>(
17
- ({
18
- label,
19
- secondaryLabel,
20
- countdown: countdownProp,
21
- isPaused = false,
22
- onPause,
23
- onProceed,
24
- variant = "default",
25
- size = "default",
26
- disabled = false,
27
- isLoading = false,
28
- className,
29
- showCountdown = true,
30
- }) => {
31
- const [timeLeft, setTimeLeft] = useState(countdownProp || 0);
32
-
33
- // Sync with prop
34
- useEffect(() => {
35
- if (countdownProp !== undefined) {
36
- setTimeLeft(countdownProp);
37
- }
38
- }, [countdownProp]);
39
-
40
- // Countdown logic
41
- useEffect(() => {
42
- if (
43
- countdownProp === undefined ||
44
- countdownProp <= 0 ||
45
- isPaused ||
46
- isLoading ||
47
- disabled
48
- ) {
49
- return;
50
- }
51
-
52
- const timer = setInterval(() => {
53
- setTimeLeft((prev) => {
54
- if (prev <= 1) {
55
- clearInterval(timer);
56
- onProceed();
57
- return 0;
58
- }
59
- return prev - 1;
60
- });
61
- }, 1000);
62
-
63
- return () => clearInterval(timer);
64
- }, [countdownProp, isPaused, isLoading, disabled, onProceed]);
65
-
66
- const progress = countdownProp ? (timeLeft / countdownProp) * 100 : 0;
67
-
68
- return (
69
- <div className={cn("inline-flex items-center gap-2", className)}>
70
- <Button
71
- variant={variant}
72
- size={size}
73
- disabled={disabled || isLoading}
74
- onClick={onProceed}
75
- className={cn(
76
- "relative min-w-[140px] overflow-hidden group transition-all duration-300",
77
- "bg-purple500 hover:bg-purple600 text-white font-semibold rounded-full px-6 py-2.5 shadow-md hover:shadow-lg",
78
- variant === "outline" &&
79
- "bg-transparent border-purple500 text-purple500 hover:bg-purple50",
80
- isLoading && "opacity-80",
81
- )}
82
- >
83
- <div className="relative z-10 flex items-center justify-center gap-2">
84
- {isLoading ? (
85
- <Loader2 className="h-4 w-4 animate-spin" />
86
- ) : (
87
- <>
88
- <span>{label}</span>
89
- {showCountdown &&
90
- countdownProp &&
91
- countdownProp > 0 &&
92
- !isLoading && (
93
- <span className="opacity-70 text-xs bg-white/20 px-1.5 py-0.5 rounded-md tabular-nums min-w-[24px]">
94
- {timeLeft}s
95
- </span>
96
- )}
97
- <ArrowRight className="h-4 w-4 group-hover:translate-x-1 transition-transform" />
98
- </>
99
- )}
100
- </div>
101
-
102
- {/* Progress Background Overlay (Optional) */}
103
- {!isPaused && countdownProp && countdownProp > 0 && (
104
- <div
105
- className="absolute bottom-0 left-0 h-0.5 bg-white/30 transition-all duration-1000 ease-linear"
106
- style={{ width: `${progress}%` }}
107
- />
108
- )}
109
- </Button>
110
-
111
- {countdownProp !== undefined && countdownProp > 0 && onPause && (
112
- <Button
113
- variant="ghost"
114
- size="icon"
115
- onClick={onPause}
116
- className="h-10 w-10 rounded-full hover:bg-gray200 text-gray600"
117
- title={isPaused ? "Resume auto-proceed" : "Pause auto-proceed"}
118
- >
119
- {isPaused ? (
120
- <Play className="h-4 w-4 fill-current" />
121
- ) : (
122
- <Pause className="h-4 w-4 fill-current" />
123
- )}
124
- </Button>
125
- )}
126
-
127
- {secondaryLabel && (
128
- <span className="text-sm text-gray-500 font-medium italic">
129
- {secondaryLabel}
130
- </span>
131
- )}
132
- </div>
133
- );
134
- },
135
- );
136
-
137
- ActionButton.displayName = "ActionButton";