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.
- package/README.md +175 -0
- package/config/tailwind-preset.js +106 -0
- package/dist/index.d.mts +1259 -0
- package/dist/index.d.ts +1259 -0
- package/dist/index.js +5175 -0
- package/dist/index.mjs +4929 -0
- package/package.json +94 -0
- package/src/atoms/AccordionAtom.tsx +44 -0
- package/src/atoms/AlertAtom.tsx +46 -0
- package/src/atoms/AlertDialogAtom.tsx +66 -0
- package/src/atoms/AspectRatioAtom.tsx +27 -0
- package/src/atoms/AvatarAtom.tsx +20 -0
- package/src/atoms/BadgeAtom.tsx +25 -0
- package/src/atoms/BreadcrumbAtom.tsx +36 -0
- package/src/atoms/ButtonAtom.tsx +63 -0
- package/src/atoms/CalendarAtom.tsx +24 -0
- package/src/atoms/CardAtom.tsx +64 -0
- package/src/atoms/CarouselAtom.tsx +40 -0
- package/src/atoms/CollapsibleAtom.tsx +44 -0
- package/src/atoms/CommandAtom.tsx +46 -0
- package/src/atoms/DialogAtom.tsx +68 -0
- package/src/atoms/InputAtom.tsx +162 -0
- package/src/atoms/LayoutAtom.tsx +43 -0
- package/src/atoms/PaginationAtom.tsx +49 -0
- package/src/atoms/PopoverAtom.tsx +40 -0
- package/src/atoms/ProgressAtom.tsx +15 -0
- package/src/atoms/ScrollAreaAtom.tsx +31 -0
- package/src/atoms/SeparatorAtom.tsx +16 -0
- package/src/atoms/SheetAtom.tsx +72 -0
- package/src/atoms/SkeletonAtom.tsx +22 -0
- package/src/atoms/SpinnerAtom.tsx +26 -0
- package/src/atoms/TableAtom.tsx +58 -0
- package/src/atoms/TabsAtom.tsx +40 -0
- package/src/atoms/TextAtom.tsx +35 -0
- package/src/atoms/TooltipAtom.tsx +39 -0
- package/src/atoms/index.ts +28 -0
- package/src/components/index.ts +178 -0
- package/src/components/ui/accordion.tsx +56 -0
- package/src/components/ui/alert-dialog.tsx +139 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/aspect-ratio.tsx +5 -0
- package/src/components/ui/avatar.tsx +50 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/breadcrumb.tsx +115 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/calendar.tsx +213 -0
- package/src/components/ui/card.tsx +79 -0
- package/src/components/ui/carousel.tsx +260 -0
- package/src/components/ui/chart.tsx +367 -0
- package/src/components/ui/checkbox.tsx +28 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/command.tsx +153 -0
- package/src/components/ui/context-menu.tsx +198 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/drawer.tsx +116 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +244 -0
- package/src/components/ui/form.tsx +176 -0
- package/src/components/ui/hover-card.tsx +27 -0
- package/src/components/ui/input-group.tsx +168 -0
- package/src/components/ui/input-otp.tsx +69 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/item.tsx +193 -0
- package/src/components/ui/kbd.tsx +28 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/menubar.tsx +254 -0
- package/src/components/ui/navigation-menu.tsx +128 -0
- package/src/components/ui/pagination.tsx +117 -0
- package/src/components/ui/popover.tsx +29 -0
- package/src/components/ui/progress.tsx +28 -0
- package/src/components/ui/radio-group.tsx +42 -0
- package/src/components/ui/resizable.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.tsx +160 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.tsx +140 -0
- package/src/components/ui/sidebar.tsx +771 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/slider.tsx +26 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.tsx +22 -0
- package/src/components/ui/toggle-group.tsx +61 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/index.ts +24 -0
- package/src/lib/countries.ts +203 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/utils.ts +15 -0
- package/src/lib/validators/index.ts +1 -0
- package/src/lib/validators/theme.ts +148 -0
- package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.tsx +123 -0
- package/src/molecules/creator-discovery/CampaignSeedCard/CampaignSeedCard.types.ts +13 -0
- package/src/molecules/creator-discovery/CampaignSeedCard/index.ts +2 -0
- package/src/molecules/creator-discovery/MCQCard/MCQCard.tsx +165 -0
- package/src/molecules/creator-discovery/MCQCard/MCQCard.types.ts +71 -0
- package/src/molecules/creator-discovery/MCQCard/index.ts +2 -0
- package/src/molecules/creator-discovery/SearchSpecCard/CustomFieldRenderers.tsx +334 -0
- package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.tsx +111 -0
- package/src/molecules/creator-discovery/SearchSpecCard/SearchSpecCard.types.ts +18 -0
- package/src/molecules/creator-discovery/SearchSpecCard/index.ts +3 -0
- package/src/molecules/creator-discovery/index.ts +3 -0
- package/src/molecules/generic/ActionButton/ActionButton.tsx +137 -0
- package/src/molecules/generic/ActionButton/ActionButton.types.ts +68 -0
- package/src/molecules/generic/ActionButton/index.ts +2 -0
- package/src/molecules/generic/EditableField/EditableField.tsx +229 -0
- package/src/molecules/generic/EditableField/EditableField.types.ts +73 -0
- package/src/molecules/generic/EditableField/index.ts +2 -0
- package/src/molecules/generic/FormCard/FormCard.tsx +136 -0
- package/src/molecules/generic/FormCard/FormCard.types.ts +93 -0
- package/src/molecules/generic/FormCard/index.ts +2 -0
- package/src/molecules/generic/index.ts +3 -0
- package/src/molecules/index.ts +2 -0
- package/src/render/PXEngineRenderer.tsx +272 -0
- package/src/render/index.ts +1 -0
- package/src/styles/globals.css +146 -0
- package/src/types/atoms.ts +294 -0
- package/src/types/common.ts +116 -0
- package/src/types/index.ts +3 -0
- package/src/types/molecules.ts +54 -0
- 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
|
+
);
|
package/src/lib/index.ts
ADDED
package/src/lib/utils.ts
ADDED
|
@@ -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,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";
|