pxengine 0.1.0

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 (127) hide show
  1. package/README.md +175 -0
  2. package/config/tailwind-preset.js +106 -0
  3. package/dist/index.d.mts +1259 -0
  4. package/dist/index.d.ts +1259 -0
  5. package/dist/index.js +5175 -0
  6. package/dist/index.mjs +4929 -0
  7. package/package.json +94 -0
  8. package/src/atoms/AccordionAtom.tsx +44 -0
  9. package/src/atoms/AlertAtom.tsx +46 -0
  10. package/src/atoms/AlertDialogAtom.tsx +66 -0
  11. package/src/atoms/AspectRatioAtom.tsx +27 -0
  12. package/src/atoms/AvatarAtom.tsx +20 -0
  13. package/src/atoms/BadgeAtom.tsx +25 -0
  14. package/src/atoms/BreadcrumbAtom.tsx +36 -0
  15. package/src/atoms/ButtonAtom.tsx +63 -0
  16. package/src/atoms/CalendarAtom.tsx +24 -0
  17. package/src/atoms/CardAtom.tsx +64 -0
  18. package/src/atoms/CarouselAtom.tsx +40 -0
  19. package/src/atoms/CollapsibleAtom.tsx +44 -0
  20. package/src/atoms/CommandAtom.tsx +46 -0
  21. package/src/atoms/DialogAtom.tsx +68 -0
  22. package/src/atoms/InputAtom.tsx +162 -0
  23. package/src/atoms/LayoutAtom.tsx +43 -0
  24. package/src/atoms/PaginationAtom.tsx +49 -0
  25. package/src/atoms/PopoverAtom.tsx +40 -0
  26. package/src/atoms/ProgressAtom.tsx +15 -0
  27. package/src/atoms/ScrollAreaAtom.tsx +31 -0
  28. package/src/atoms/SeparatorAtom.tsx +16 -0
  29. package/src/atoms/SheetAtom.tsx +72 -0
  30. package/src/atoms/SkeletonAtom.tsx +22 -0
  31. package/src/atoms/SpinnerAtom.tsx +26 -0
  32. package/src/atoms/TableAtom.tsx +58 -0
  33. package/src/atoms/TabsAtom.tsx +40 -0
  34. package/src/atoms/TextAtom.tsx +35 -0
  35. package/src/atoms/TooltipAtom.tsx +39 -0
  36. package/src/atoms/index.ts +28 -0
  37. package/src/components/index.ts +178 -0
  38. package/src/components/ui/accordion.tsx +56 -0
  39. package/src/components/ui/alert-dialog.tsx +139 -0
  40. package/src/components/ui/alert.tsx +59 -0
  41. package/src/components/ui/aspect-ratio.tsx +5 -0
  42. package/src/components/ui/avatar.tsx +50 -0
  43. package/src/components/ui/badge.tsx +36 -0
  44. package/src/components/ui/breadcrumb.tsx +115 -0
  45. package/src/components/ui/button-group.tsx +83 -0
  46. package/src/components/ui/button.tsx +56 -0
  47. package/src/components/ui/calendar.tsx +213 -0
  48. package/src/components/ui/card.tsx +79 -0
  49. package/src/components/ui/carousel.tsx +260 -0
  50. package/src/components/ui/chart.tsx +367 -0
  51. package/src/components/ui/checkbox.tsx +28 -0
  52. package/src/components/ui/collapsible.tsx +11 -0
  53. package/src/components/ui/command.tsx +153 -0
  54. package/src/components/ui/context-menu.tsx +198 -0
  55. package/src/components/ui/dialog.tsx +122 -0
  56. package/src/components/ui/drawer.tsx +116 -0
  57. package/src/components/ui/dropdown-menu.tsx +200 -0
  58. package/src/components/ui/empty.tsx +104 -0
  59. package/src/components/ui/field.tsx +244 -0
  60. package/src/components/ui/form.tsx +176 -0
  61. package/src/components/ui/hover-card.tsx +27 -0
  62. package/src/components/ui/input-group.tsx +168 -0
  63. package/src/components/ui/input-otp.tsx +69 -0
  64. package/src/components/ui/input.tsx +22 -0
  65. package/src/components/ui/item.tsx +193 -0
  66. package/src/components/ui/kbd.tsx +28 -0
  67. package/src/components/ui/label.tsx +26 -0
  68. package/src/components/ui/menubar.tsx +254 -0
  69. package/src/components/ui/navigation-menu.tsx +128 -0
  70. package/src/components/ui/pagination.tsx +117 -0
  71. package/src/components/ui/popover.tsx +29 -0
  72. package/src/components/ui/progress.tsx +28 -0
  73. package/src/components/ui/radio-group.tsx +42 -0
  74. package/src/components/ui/resizable.tsx +45 -0
  75. package/src/components/ui/scroll-area.tsx +46 -0
  76. package/src/components/ui/select.tsx +160 -0
  77. package/src/components/ui/separator.tsx +29 -0
  78. package/src/components/ui/sheet.tsx +140 -0
  79. package/src/components/ui/sidebar.tsx +771 -0
  80. package/src/components/ui/skeleton.tsx +15 -0
  81. package/src/components/ui/slider.tsx +26 -0
  82. package/src/components/ui/sonner.tsx +45 -0
  83. package/src/components/ui/spinner.tsx +16 -0
  84. package/src/components/ui/switch.tsx +27 -0
  85. package/src/components/ui/table.tsx +117 -0
  86. package/src/components/ui/tabs.tsx +53 -0
  87. package/src/components/ui/textarea.tsx +22 -0
  88. package/src/components/ui/toggle-group.tsx +61 -0
  89. package/src/components/ui/toggle.tsx +43 -0
  90. package/src/components/ui/tooltip.tsx +30 -0
  91. package/src/hooks/use-mobile.tsx +19 -0
  92. package/src/index.ts +24 -0
  93. package/src/lib/countries.ts +203 -0
  94. package/src/lib/index.ts +2 -0
  95. package/src/lib/utils.ts +15 -0
  96. package/src/lib/validators/index.ts +1 -0
  97. package/src/lib/validators/theme.ts +148 -0
  98. package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.tsx +123 -0
  99. package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.types.ts +13 -0
  100. package/src/molecules/creator-discovery/CampaignSeedCard/index.ts +2 -0
  101. package/src/molecules/creator-discovery/MCQCard/MCQCard.tsx +165 -0
  102. package/src/molecules/creator-discovery/MCQCard/MCQCard.types.ts +71 -0
  103. package/src/molecules/creator-discovery/MCQCard/index.ts +2 -0
  104. package/src/molecules/creator-discovery/SearchSpecCard/CustomFieldRenderers.tsx +334 -0
  105. package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.tsx +111 -0
  106. package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.types.ts +18 -0
  107. package/src/molecules/creator-discovery/SearchSpecCard/index.ts +3 -0
  108. package/src/molecules/creator-discovery/index.ts +3 -0
  109. package/src/molecules/generic/ActionButton/ActionButton.tsx +137 -0
  110. package/src/molecules/generic/ActionButton/ActionButton.types.ts +68 -0
  111. package/src/molecules/generic/ActionButton/index.ts +2 -0
  112. package/src/molecules/generic/EditableField/EditableField.tsx +229 -0
  113. package/src/molecules/generic/EditableField/EditableField.types.ts +73 -0
  114. package/src/molecules/generic/EditableField/index.ts +2 -0
  115. package/src/molecules/generic/FormCard/FormCard.tsx +136 -0
  116. package/src/molecules/generic/FormCard/FormCard.types.ts +93 -0
  117. package/src/molecules/generic/FormCard/index.ts +2 -0
  118. package/src/molecules/generic/index.ts +3 -0
  119. package/src/molecules/index.ts +2 -0
  120. package/src/render/PXEngineRenderer.tsx +272 -0
  121. package/src/render/index.ts +1 -0
  122. package/src/styles/globals.css +146 -0
  123. package/src/types/atoms.ts +294 -0
  124. package/src/types/common.ts +116 -0
  125. package/src/types/index.ts +3 -0
  126. package/src/types/molecules.ts +54 -0
  127. package/src/types/schema.ts +12 -0
@@ -0,0 +1,203 @@
1
+ export const countries = [
2
+ { code: "AF", name: "Afghanistan", flag: "🇦🇫" },
3
+ { code: "AL", name: "Albania", flag: "🇦🇱" },
4
+ { code: "DZ", name: "Algeria", flag: "🇩🇿" },
5
+ { code: "AD", name: "Andorra", flag: "🇦🇩" },
6
+ { code: "AO", name: "Angola", flag: "🇦🇴" },
7
+ { code: "AG", name: "Antigua and Barbuda", flag: "🇦🇬" },
8
+ { code: "AR", name: "Argentina", flag: "🇦🇷" },
9
+ { code: "AM", name: "Armenia", flag: "🇦🇲" },
10
+ { code: "AU", name: "Australia", flag: "🇦🇺" },
11
+ { code: "AT", name: "Austria", flag: "🇦🇹" },
12
+ { code: "AZ", name: "Azerbaijan", flag: "🇦🇿" },
13
+ { code: "BS", name: "Bahamas", flag: "🇧🇸" },
14
+ { code: "BH", name: "Bahrain", flag: "🇧🇭" },
15
+ { code: "BD", name: "Bangladesh", flag: "🇧🇩" },
16
+ { code: "BB", name: "Barbados", flag: "🇧🇧" },
17
+ { code: "BY", name: "Belarus", flag: "🇧🇾" },
18
+ { code: "BE", name: "Belgium", flag: "🇧🇪" },
19
+ { code: "BZ", name: "Belize", flag: "🇧🇿" },
20
+ { code: "BJ", name: "Benin", flag: "🇧🇯" },
21
+ { code: "BT", name: "Bhutan", flag: "🇧🇹" },
22
+ { code: "BO", name: "Bolivia", flag: "🇧🇴" },
23
+ { code: "BA", name: "Bosnia and Herzegovina", flag: "🇧🇦" },
24
+ { code: "BW", name: "Botswana", flag: "🇧🇼" },
25
+ { code: "BR", name: "Brazil", flag: "🇧🇷" },
26
+ { code: "BN", name: "Brunei", flag: "🇧🇳" },
27
+ { code: "BG", name: "Bulgaria", flag: "🇧🇬" },
28
+ { code: "BF", name: "Burkina Faso", flag: "🇧🇫" },
29
+ { code: "BI", name: "Burundi", flag: "🇧🇮" },
30
+ { code: "CV", name: "Cabo Verde", flag: "🇨🇻" },
31
+ { code: "KH", name: "Cambodia", flag: "🇰🇭" },
32
+ { code: "CM", name: "Cameroon", flag: "🇨🇲" },
33
+ { code: "CA", name: "Canada", flag: "🇨🇦" },
34
+ { code: "CF", name: "Central African Republic", flag: "🇨🇫" },
35
+ { code: "TD", name: "Chad", flag: "🇹🇩" },
36
+ { code: "CL", name: "Chile", flag: "🇨🇱" },
37
+ { code: "CN", name: "China", flag: "🇨🇳" },
38
+ { code: "CO", name: "Colombia", flag: "🇨🇴" },
39
+ { code: "KM", name: "Comoros", flag: "🇰🇲" },
40
+ { code: "CG", name: "Congo", flag: "🇨🇬" },
41
+ { code: "CD", name: "Democratic Republic of the Congo", flag: "🇨🇩" },
42
+ { code: "CR", name: "Costa Rica", flag: "🇨🇷" },
43
+ { code: "HR", name: "Croatia", flag: "🇭🇷" },
44
+ { code: "CU", name: "Cuba", flag: "🇨🇺" },
45
+ { code: "CY", name: "Cyprus", flag: "🇨🇾" },
46
+ { code: "CZ", name: "Czech Republic", flag: "🇨🇿" },
47
+ { code: "CI", name: "Ivory Coast", flag: "🇨🇮" },
48
+ { code: "DK", name: "Denmark", flag: "🇩🇰" },
49
+ { code: "DJ", name: "Djibouti", flag: "🇩🇯" },
50
+ { code: "DM", name: "Dominica", flag: "🇩🇲" },
51
+ { code: "DO", name: "Dominican Republic", flag: "🇩🇴" },
52
+ { code: "EC", name: "Ecuador", flag: "🇪🇨" },
53
+ { code: "EG", name: "Egypt", flag: "🇪🇬" },
54
+ { code: "SV", name: "El Salvador", flag: "🇸🇻" },
55
+ { code: "GQ", name: "Equatorial Guinea", flag: "🇬🇶" },
56
+ { code: "ER", name: "Eritrea", flag: "🇪🇷" },
57
+ { code: "EE", name: "Estonia", flag: "🇪🇪" },
58
+ { code: "SZ", name: "Eswatini", flag: "🇸🇿" },
59
+ { code: "ET", name: "Ethiopia", flag: "🇪🇹" },
60
+ { code: "FJ", name: "Fiji", flag: "🇫🇯" },
61
+ { code: "FI", name: "Finland", flag: "🇫🇮" },
62
+ { code: "FR", name: "France", flag: "🇫🇷" },
63
+ { code: "GA", name: "Gabon", flag: "🇬🇦" },
64
+ { code: "GM", name: "Gambia", flag: "🇬🇲" },
65
+ { code: "GE", name: "Georgia", flag: "🇬🇪" },
66
+ { code: "DE", name: "Germany", flag: "🇩🇪" },
67
+ { code: "GH", name: "Ghana", flag: "🇬🇭" },
68
+ { code: "GR", name: "Greece", flag: "🇬🇷" },
69
+ { code: "GD", name: "Grenada", flag: "🇬🇩" },
70
+ { code: "GT", name: "Guatemala", flag: "🇬🇹" },
71
+ { code: "GN", name: "Guinea", flag: "🇬🇳" },
72
+ { code: "GW", name: "Guinea-Bissau", flag: "🇬🇼" },
73
+ { code: "GY", name: "Guyana", flag: "🇬🇾" },
74
+ { code: "HT", name: "Haiti", flag: "🇭🇹" },
75
+ { code: "HN", name: "Honduras", flag: "🇭🇳" },
76
+ { code: "HU", name: "Hungary", flag: "🇭🇺" },
77
+ { code: "HK", name: "Hong Kong", flag: "🇭🇰" },
78
+ { code: "IS", name: "Iceland", flag: "🇮🇸" },
79
+ { code: "IN", name: "India", flag: "🇮🇳" },
80
+ { code: "ID", name: "Indonesia", flag: "🇮🇩" },
81
+ { code: "IR", name: "Iran", flag: "🇮🇷" },
82
+ { code: "IQ", name: "Iraq", flag: "🇮🇶" },
83
+ { code: "IE", name: "Ireland", flag: "🇮🇪" },
84
+ { code: "IL", name: "Israel", flag: "🇮🇱" },
85
+ { code: "IT", name: "Italy", flag: "🇮🇹" },
86
+ { code: "JM", name: "Jamaica", flag: "🇯🇲" },
87
+ { code: "JP", name: "Japan", flag: "🇯🇵" },
88
+ { code: "JO", name: "Jordan", flag: "🇯🇴" },
89
+ { code: "KZ", name: "Kazakhstan", flag: "🇰🇿" },
90
+ { code: "KE", name: "Kenya", flag: "🇰🇪" },
91
+ { code: "KI", name: "Kiribati", flag: "🇰🇮" },
92
+ { code: "KP", name: "North Korea", flag: "🇰🇵" },
93
+ { code: "KR", name: "South Korea", flag: "🇰🇷" },
94
+ { code: "KW", name: "Kuwait", flag: "🇰🇼" },
95
+ { code: "KG", name: "Kyrgyzstan", flag: "🇰🇬" },
96
+ { code: "LA", name: "Laos", flag: "🇱🇦" },
97
+ { code: "LV", name: "Latvia", flag: "🇱🇻" },
98
+ { code: "LB", name: "Lebanon", flag: "🇱🇧" },
99
+ { code: "LS", name: "Lesotho", flag: "🇱🇸" },
100
+ { code: "LR", name: "Liberia", flag: "🇱🇷" },
101
+ { code: "LY", name: "Libya", flag: "🇱🇾" },
102
+ { code: "LI", name: "Liechtenstein", flag: "🇱🇮" },
103
+ { code: "LT", name: "Lithuania", flag: "🇱🇹" },
104
+ { code: "LU", name: "Luxembourg", flag: "🇱🇺" },
105
+ { code: "MG", name: "Madagascar", flag: "🇲🇬" },
106
+ { code: "MW", name: "Malawi", flag: "🇲🇼" },
107
+ { code: "MY", name: "Malaysia", flag: "🇲🇾" },
108
+ { code: "MV", name: "Maldives", flag: "🇲🇻" },
109
+ { code: "ML", name: "Mali", flag: "🇲🇱" },
110
+ { code: "MT", name: "Malta", flag: "🇲🇹" },
111
+ { code: "MH", name: "Marshall Islands", flag: "🇲🇭" },
112
+ { code: "MR", name: "Mauritania", flag: "🇲🇷" },
113
+ { code: "MU", name: "Mauritius", flag: "🇲🇺" },
114
+ { code: "MX", name: "Mexico", flag: "🇲🇽" },
115
+ { code: "FM", name: "Micronesia", flag: "🇫🇲" },
116
+ { code: "MD", name: "Moldova", flag: "🇲🇩" },
117
+ { code: "MC", name: "Monaco", flag: "🇲🇨" },
118
+ { code: "MN", name: "Mongolia", flag: "🇲🇳" },
119
+ { code: "ME", name: "Montenegro", flag: "🇲🇪" },
120
+ { code: "MA", name: "Morocco", flag: "🇲🇦" },
121
+ { code: "MZ", name: "Mozambique", flag: "🇲🇿" },
122
+ { code: "MM", name: "Myanmar", flag: "🇲🇲" },
123
+ { code: "NA", name: "Namibia", flag: "🇳🇦" },
124
+ { code: "NR", name: "Nauru", flag: "🇳🇷" },
125
+ { code: "NP", name: "Nepal", flag: "🇳🇵" },
126
+ { code: "NL", name: "Netherlands", flag: "🇳🇱" },
127
+ { code: "NZ", name: "New Zealand", flag: "🇳🇿" },
128
+ { code: "NI", name: "Nicaragua", flag: "🇳🇮" },
129
+ { code: "NE", name: "Niger", flag: "🇳🇪" },
130
+ { code: "NG", name: "Nigeria", flag: "🇳🇬" },
131
+ { code: "MK", name: "North Macedonia", flag: "🇲🇰" },
132
+ { code: "NO", name: "Norway", flag: "🇳🇴" },
133
+ { code: "OM", name: "Oman", flag: "🇴🇲" },
134
+ { code: "PK", name: "Pakistan", flag: "🇵🇰" },
135
+ { code: "PW", name: "Palau", flag: "🇵🇼" },
136
+ { code: "PS", name: "Palestine", flag: "🇵🇸" },
137
+ { code: "PA", name: "Panama", flag: "🇵🇦" },
138
+ { code: "PG", name: "Papua New Guinea", flag: "🇵🇬" },
139
+ { code: "PY", name: "Paraguay", flag: "🇵🇾" },
140
+ { code: "PE", name: "Peru", flag: "🇵🇪" },
141
+ { code: "PH", name: "Philippines", flag: "🇵🇭" },
142
+ { code: "PL", name: "Poland", flag: "🇵🇱" },
143
+ { code: "PT", name: "Portugal", flag: "🇵🇹" },
144
+ { code: "QA", name: "Qatar", flag: "🇶🇦" },
145
+ { code: "RO", name: "Romania", flag: "🇷🇴" },
146
+ { code: "RU", name: "Russia", flag: "🇷🇺" },
147
+ { code: "RW", name: "Rwanda", flag: "🇷🇼" },
148
+ { code: "KN", name: "Saint Kitts and Nevis", flag: "🇰🇳" },
149
+ { code: "LC", name: "Saint Lucia", flag: "🇱🇨" },
150
+ { code: "VC", name: "Saint Vincent and the Grenadines", flag: "🇻🇨" },
151
+ { code: "WS", name: "Samoa", flag: "🇼🇸" },
152
+ { code: "SM", name: "San Marino", flag: "🇸🇲" },
153
+ { code: "ST", name: "Sao Tome and Principe", flag: "🇸🇹" },
154
+ { code: "SA", name: "Saudi Arabia", flag: "🇸🇦" },
155
+ { code: "SN", name: "Senegal", flag: "🇸🇳" },
156
+ { code: "RS", name: "Serbia", flag: "🇷🇸" },
157
+ { code: "SC", name: "Seychelles", flag: "🇸🇨" },
158
+ { code: "SL", name: "Sierra Leone", flag: "🇸🇱" },
159
+ { code: "SG", name: "Singapore", flag: "🇸🇬" },
160
+ { code: "SK", name: "Slovakia", flag: "🇸🇰" },
161
+ { code: "SI", name: "Slovenia", flag: "🇸🇮" },
162
+ { code: "SB", name: "Solomon Islands", flag: "🇸🇧" },
163
+ { code: "SO", name: "Somalia", flag: "🇸🇴" },
164
+ { code: "ZA", name: "South Africa", flag: "🇿🇦" },
165
+ { code: "SS", name: "South Sudan", flag: "🇸🇸" },
166
+ { code: "ES", name: "Spain", flag: "🇪🇸" },
167
+ { code: "LK", name: "Sri Lanka", flag: "🇱🇰" },
168
+ { code: "SD", name: "Sudan", flag: "🇸🇩" },
169
+ { code: "SR", name: "Suriname", flag: "🇸🇷" },
170
+ { code: "SE", name: "Sweden", flag: "🇸🇪" },
171
+ { code: "CH", name: "Switzerland", flag: "🇨🇭" },
172
+ { code: "SY", name: "Syria", flag: "🇸🇾" },
173
+ { code: "TW", name: "Taiwan", flag: "🇹🇼" },
174
+ { code: "TJ", name: "Tajikistan", flag: "🇹🇯" },
175
+ { code: "TZ", name: "Tanzania", flag: "🇹🇿" },
176
+ { code: "TH", name: "Thailand", flag: "🇹🇭" },
177
+ { code: "TL", name: "Timor-Leste", flag: "🇹🇱" },
178
+ { code: "TG", name: "Togo", flag: "🇹🇬" },
179
+ { code: "TO", name: "Tonga", flag: "🇹🇴" },
180
+ { code: "TT", name: "Trinidad and Tobago", flag: "🇹🇹" },
181
+ { code: "TN", name: "Tunisia", flag: "🇹🇳" },
182
+ { code: "TR", name: "Turkey", flag: "🇹🇷" },
183
+ { code: "TM", name: "Turkmenistan", flag: "🇹🇲" },
184
+ { code: "TV", name: "Tuvalu", flag: "🇹🇻" },
185
+ { code: "UG", name: "Uganda", flag: "🇺🇬" },
186
+ { code: "UA", name: "Ukraine", flag: "🇺🇦" },
187
+ { code: "AE", name: "United Arab Emirates", flag: "🇦🇪" },
188
+ { code: "GB", name: "United Kingdom", flag: "🇬🇧" },
189
+ { code: "US", name: "United States", flag: "🇺🇸" },
190
+ { code: "UY", name: "Uruguay", flag: "🇺🇾" },
191
+ { code: "UZ", name: "Uzbekistan", flag: "🇺🇿" },
192
+ { code: "VU", name: "Vanuatu", flag: "🇻🇺" },
193
+ { code: "VA", name: "Vatican City", flag: "🇻🇦" },
194
+ { code: "VE", name: "Venezuela", flag: "🇻🇪" },
195
+ { code: "VN", name: "Vietnam", flag: "🇻🇳" },
196
+ { code: "YE", name: "Yemen", flag: "🇾🇪" },
197
+ { code: "ZM", name: "Zambia", flag: "🇿🇲" },
198
+ { code: "ZW", name: "Zimbabwe", flag: "🇿🇼" }
199
+ ];
200
+
201
+ export const countriesWithFlags = Object.fromEntries(
202
+ countries.map((c) => [c.code, { name: c.name, flag: c.flag }])
203
+ );
@@ -0,0 +1,2 @@
1
+ export { cn } from "./utils";
2
+ export * from "./validators";
@@ -0,0 +1,15 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ /**
5
+ * Utility function to merge Tailwind CSS classes
6
+ * Combines clsx for conditional classes and tailwind-merge for deduplication
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * cn("px-4 py-2", isActive && "bg-purple500", className)
11
+ * ```
12
+ */
13
+ export function cn(...inputs: ClassValue[]) {
14
+ return twMerge(clsx(inputs));
15
+ }
@@ -0,0 +1 @@
1
+ export * from "./theme";
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Theme Validator
3
+ * Validates that required CSS variables are defined in the theme
4
+ */
5
+
6
+ const requiredCSSVariables = [
7
+ // Shadcn default variables
8
+ "--background",
9
+ "--foreground",
10
+ "--card",
11
+ "--card-foreground",
12
+ "--popover",
13
+ "--popover-foreground",
14
+ "--primary",
15
+ "--primary-foreground",
16
+ "--secondary",
17
+ "--secondary-foreground",
18
+ "--muted",
19
+ "--muted-foreground",
20
+ "--accent",
21
+ "--accent-foreground",
22
+ "--destructive",
23
+ "--destructive-foreground",
24
+ "--border",
25
+ "--input",
26
+ "--ring",
27
+ "--radius",
28
+
29
+ // Custom theme variables (from client)
30
+ "--gray25",
31
+ "--gray50",
32
+ "--gray100",
33
+ "--gray200",
34
+ "--gray300",
35
+ "--gray400",
36
+ "--gray500",
37
+ "--gray600",
38
+ "--gray700",
39
+ "--gray800",
40
+ "--gray900",
41
+ "--purple100",
42
+ "--purple200",
43
+ "--purple500",
44
+ "--purple-text",
45
+ "--purple50",
46
+ "--purple20",
47
+ "--purple-border",
48
+ "--purpleLight",
49
+ "--purple-text-1",
50
+ "--purple-text-2",
51
+ ] as const;
52
+
53
+ /**
54
+ * Get computed CSS variable value
55
+ */
56
+ export function getCSSVariable(variable: string, element: HTMLElement = document.documentElement): string {
57
+ return getComputedStyle(element).getPropertyValue(variable).trim();
58
+ }
59
+
60
+ /**
61
+ * Check if a CSS variable is defined
62
+ */
63
+ export function isCSSVariableDefined(variable: string, element: HTMLElement = document.documentElement): boolean {
64
+ const value = getCSSVariable(variable, element);
65
+ return value !== "";
66
+ }
67
+
68
+ /**
69
+ * Validate that all required CSS variables are defined
70
+ * @returns Object with validation result and missing variables
71
+ */
72
+ export function validateTheme(element: HTMLElement = document.documentElement): {
73
+ isValid: boolean;
74
+ missingVariables: string[];
75
+ warnings: string[];
76
+ } {
77
+ const missingVariables: string[] = [];
78
+ const warnings: string[] = [];
79
+
80
+ for (const variable of requiredCSSVariables) {
81
+ if (!isCSSVariableDefined(variable, element)) {
82
+ missingVariables.push(variable);
83
+ }
84
+ }
85
+
86
+ // Check for dark mode variables
87
+ const darkElement = document.querySelector(".dark");
88
+ if (!darkElement) {
89
+ warnings.push("Dark mode class '.dark' not found. Dark mode may not work correctly.");
90
+ }
91
+
92
+ return {
93
+ isValid: missingVariables.length === 0,
94
+ missingVariables,
95
+ warnings,
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Log theme validation results to console
101
+ */
102
+ export function logThemeValidation(): void {
103
+ const result = validateTheme();
104
+
105
+ if (result.isValid) {
106
+ console.log("✅ Theme validation passed! All required CSS variables are defined.");
107
+ } else {
108
+ console.error("❌ Theme validation failed! Missing CSS variables:");
109
+ result.missingVariables.forEach((variable) => {
110
+ console.error(` - ${variable}`);
111
+ });
112
+ }
113
+
114
+ if (result.warnings.length > 0) {
115
+ console.warn("⚠️ Theme warnings:");
116
+ result.warnings.forEach((warning) => {
117
+ console.warn(` - ${warning}`);
118
+ });
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Assert that theme is valid (throws error if not)
124
+ * Use this in development to catch theme issues early
125
+ */
126
+ export function assertThemeValid(): void {
127
+ const result = validateTheme();
128
+
129
+ if (!result.isValid) {
130
+ throw new Error(
131
+ `Theme validation failed! Missing CSS variables: ${result.missingVariables.join(", ")}`
132
+ );
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Get all CSS variables as an object
138
+ * Useful for debugging
139
+ */
140
+ export function getAllCSSVariables(element: HTMLElement = document.documentElement): Record<string, string> {
141
+ const variables: Record<string, string> = {};
142
+
143
+ for (const variable of requiredCSSVariables) {
144
+ variables[variable] = getCSSVariable(variable, element);
145
+ }
146
+
147
+ return variables;
148
+ }
@@ -0,0 +1,123 @@
1
+ import React from "react";
2
+ import { CampaignSeedCardProps } from "./CampaignSeedCard.types";
3
+ import { FormCard } from "../../generic/FormCard";
4
+ import { FieldConfig } from "@/types/common";
5
+ import { CheckCircle2 } from "lucide-react";
6
+
7
+ /**
8
+ * Default field configuration for Campaign Seed
9
+ */
10
+ export const CAMPAIGN_SEED_FIELDS: FieldConfig[] = [
11
+ {
12
+ key: "brand",
13
+ label: "Brand / Campaign",
14
+ type: "text",
15
+ placeholder: "Not specified",
16
+ },
17
+ {
18
+ key: "objective",
19
+ label: "Objective",
20
+ type: "text",
21
+ placeholder: "Not specified",
22
+ },
23
+ {
24
+ key: "budget_hint",
25
+ label: "Budget Hint",
26
+ type: "slider",
27
+ placeholder: "Not specified",
28
+ sliderConfig: {
29
+ min: 0,
30
+ max: 1000000,
31
+ step: 1000,
32
+ formatValue: (value: any) => {
33
+ if (!value) return "$0 - $100K";
34
+
35
+ const formatCurrency = (amount: number) => {
36
+ if (amount >= 1000000) return `$${(amount / 1000000).toFixed(1)}M`;
37
+ if (amount >= 1000) return `$${(amount / 1000).toFixed(0)}K`;
38
+ return `$${amount.toLocaleString()}`;
39
+ };
40
+
41
+ let normalized;
42
+ if (typeof value === "object" && value?.min !== undefined) {
43
+ normalized = value;
44
+ } else if (typeof value === "string" && value.includes("-")) {
45
+ const [minStr, maxStr] = value.split("-");
46
+ normalized = {
47
+ min: Number(minStr) || 0,
48
+ max: Number(maxStr) || 100000,
49
+ };
50
+ } else {
51
+ const val = Number(String(value).replace(/[^0-9.]/g, "")) || 0;
52
+ normalized =
53
+ val > 0 ? { min: val, max: val } : { min: 0, max: 100000 };
54
+ }
55
+
56
+ if (normalized.min === normalized.max)
57
+ return formatCurrency(normalized.min);
58
+ return `${formatCurrency(normalized.min)} - ${formatCurrency(normalized.max)}`;
59
+ },
60
+ },
61
+ },
62
+ {
63
+ key: "geo_or_audience",
64
+ label: "Geography or Audience",
65
+ type: "text",
66
+ placeholder: "Not specified",
67
+ },
68
+ {
69
+ key: "timeline_opt",
70
+ label: "Timeline Option",
71
+ type: "text",
72
+ placeholder: "Not specified",
73
+ },
74
+ {
75
+ key: "notes",
76
+ label: "Notes",
77
+ type: "textarea",
78
+ placeholder: "No notes",
79
+ rows: 4,
80
+ },
81
+ ];
82
+
83
+ /**
84
+ * CampaignSeedCard
85
+ *
86
+ * A domain-specific molecule for the Creator Discovery workflow.
87
+ * Encapsulates the specific brand info fields and selection status.
88
+ */
89
+ export const CampaignSeedCard = React.memo<CampaignSeedCardProps>(
90
+ ({
91
+ selectionStatus,
92
+ isLatestMessage = true,
93
+ className,
94
+ ...formCardProps
95
+ }) => {
96
+ return (
97
+ <div className="flex flex-col gap-2">
98
+ <FormCard
99
+ {...formCardProps}
100
+ title={formCardProps.title || "Brand Information"}
101
+ fields={CAMPAIGN_SEED_FIELDS}
102
+ className={className}
103
+ footer={
104
+ !isLatestMessage && selectionStatus ? (
105
+ <div className="flex justify-end items-center gap-1.5 text-green-600 text-xs font-semibold py-1">
106
+ <CheckCircle2 className="h-4 w-4" />
107
+ <span>
108
+ {selectionStatus === "agent"
109
+ ? "Selected by Agent"
110
+ : "Selected by User"}
111
+ </span>
112
+ </div>
113
+ ) : (
114
+ formCardProps.footer
115
+ )
116
+ }
117
+ />
118
+ </div>
119
+ );
120
+ },
121
+ );
122
+
123
+ CampaignSeedCard.displayName = "CampaignSeedCard";
@@ -0,0 +1,13 @@
1
+ import { FormCardProps } from "../../generic/FormCard";
2
+
3
+ export interface CampaignSeedCardProps extends Omit<FormCardProps, "fields"> {
4
+ /**
5
+ * Status of the selection (done by user or agent)
6
+ */
7
+ selectionStatus?: "user" | "agent";
8
+
9
+ /**
10
+ * Whether the message is the latest (to show countdown/actions)
11
+ */
12
+ isLatestMessage?: boolean;
13
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./CampaignSeedCard";
2
+ export * from "./CampaignSeedCard.types";
@@ -0,0 +1,165 @@
1
+ import React from "react";
2
+ import { MCQCardProps } from "./MCQCard.types";
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardHeader,
7
+ CardTitle,
8
+ CardFooter,
9
+ Badge,
10
+ } from "@/components";
11
+ import { ActionButton } from "../../generic/ActionButton";
12
+ import { cn } from "@/lib/utils";
13
+ import { CheckCircle2, Sparkles } from "lucide-react";
14
+
15
+ /**
16
+ * MCQCard
17
+ *
18
+ * A molecule for Multiple Choice Questions.
19
+ * Supports:
20
+ * - Recommended options with styling
21
+ * - Radio-style selection
22
+ * - Integrated ActionButton with countdown
23
+ * - Peer/Agent approval labels
24
+ * - Premium aesthetics
25
+ */
26
+ export const MCQCard = React.memo<MCQCardProps>(
27
+ ({
28
+ question,
29
+ options,
30
+ recommended,
31
+ selectedOption,
32
+ onSelect,
33
+ onProceed,
34
+ isLatestMessage = true,
35
+ countdown,
36
+ isPaused = false,
37
+ onPause,
38
+ isLoading = false,
39
+ className,
40
+ selectionStatus,
41
+ }) => {
42
+ const handleOptionClick = (key: string) => {
43
+ if (isLatestMessage && !isLoading) {
44
+ onSelect?.(key);
45
+ }
46
+ };
47
+
48
+ const handleProceed = () => {
49
+ if (selectedOption || recommended) {
50
+ onProceed?.(selectedOption || recommended || "");
51
+ }
52
+ };
53
+
54
+ return (
55
+ <Card
56
+ className={cn(
57
+ "w-full rounded-[24px] border border-gray200 bg-white shadow-sm overflow-hidden",
58
+ className,
59
+ )}
60
+ >
61
+ <CardHeader className="pb-2">
62
+ <CardTitle className="text-lg font-bold text-gray-900 tracking-tight leading-snug">
63
+ {question}
64
+ </CardTitle>
65
+ </CardHeader>
66
+
67
+ <CardContent className="space-y-3 pb-6">
68
+ {Object.entries(options).map(([key, label]) => {
69
+ const isSelected = selectedOption === key;
70
+ const isRecommended = key === recommended;
71
+
72
+ return (
73
+ <div
74
+ key={key}
75
+ onClick={() => handleOptionClick(key)}
76
+ className={cn(
77
+ "group relative flex items-start gap-4 p-4 rounded-2xl border transition-all duration-200 cursor-pointer",
78
+ isSelected
79
+ ? "border-purple500 bg-purple50/30"
80
+ : "border-gray-100 bg-gray-50/30 hover:border-gray-300 hover:bg-gray-50",
81
+ isLoading && "opacity-50 cursor-not-allowed",
82
+ )}
83
+ >
84
+ {/* Radio Circle */}
85
+ <div
86
+ className={cn(
87
+ "mt-1 w-5 h-5 rounded-full border-2 flex items-center justify-center transition-colors",
88
+ isSelected
89
+ ? "border-purple500 bg-purple500"
90
+ : "border-gray-300 bg-white group-hover:border-gray-400",
91
+ )}
92
+ >
93
+ {isSelected && (
94
+ <div className="w-1.5 h-1.5 rounded-full bg-white" />
95
+ )}
96
+ </div>
97
+
98
+ <div className="flex-1 space-y-1.5">
99
+ <div className="flex items-center gap-2 flex-wrap">
100
+ <span
101
+ className={cn(
102
+ "text-sm font-semibold transition-colors",
103
+ isSelected ? "text-purple900" : "text-gray700",
104
+ )}
105
+ >
106
+ {label}
107
+ </span>
108
+
109
+ {isRecommended && (
110
+ <Badge
111
+ variant="outline"
112
+ className="bg-green-50 text-green-700 border-green-100 flex items-center gap-1 text-[10px] py-0 h-5"
113
+ >
114
+ <Sparkles className="h-3 w-3" />
115
+ Recommended
116
+ </Badge>
117
+ )}
118
+
119
+ {isSelected && !isLatestMessage && (
120
+ <Badge
121
+ variant="outline"
122
+ className="bg-purple-50 text-purple-700 border-purple-100 text-[10px] py-0 h-5"
123
+ >
124
+ Selected
125
+ </Badge>
126
+ )}
127
+ </div>
128
+ </div>
129
+ </div>
130
+ );
131
+ })}
132
+ </CardContent>
133
+
134
+ <CardFooter className="flex flex-col gap-4 pt-0 border-t border-gray-100/50 bg-gray-50/30 p-6">
135
+ {isLatestMessage ? (
136
+ <div className="w-full flex justify-center">
137
+ <ActionButton
138
+ label={
139
+ selectedOption ? "Continue" : "Proceed with Recommendation"
140
+ }
141
+ countdown={countdown}
142
+ isPaused={isPaused}
143
+ onPause={onPause}
144
+ onProceed={handleProceed}
145
+ isLoading={isLoading}
146
+ disabled={!selectedOption && !recommended}
147
+ />
148
+ </div>
149
+ ) : (
150
+ <div className="w-full flex justify-end items-center gap-1.5 text-green-600 text-xs font-semibold">
151
+ <CheckCircle2 className="h-4 w-4" />
152
+ <span>
153
+ {selectionStatus === "agent"
154
+ ? "Suggested by Agent, Approved by You"
155
+ : "Selected by You"}
156
+ </span>
157
+ </div>
158
+ )}
159
+ </CardFooter>
160
+ </Card>
161
+ );
162
+ },
163
+ );
164
+
165
+ MCQCard.displayName = "MCQCard";