@dyrected/admin 2.0.0 → 2.4.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 (67) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/package.json +4 -4
  3. package/scripts/prefix-tailwind-precision.py +98 -0
  4. package/scripts/prefix-tailwind.py +67 -0
  5. package/src/components/auth/auth-gate.tsx +4 -4
  6. package/src/components/error-boundary.tsx +4 -4
  7. package/src/components/forms/fields/block-builder.tsx +24 -24
  8. package/src/components/forms/fields/date-picker.tsx +7 -7
  9. package/src/components/forms/fields/json-editor.tsx +5 -5
  10. package/src/components/forms/fields/media-picker.tsx +39 -39
  11. package/src/components/forms/fields/multi-select.tsx +12 -12
  12. package/src/components/forms/fields/radio-field.tsx +8 -8
  13. package/src/components/forms/fields/relationship-picker.tsx +13 -13
  14. package/src/components/forms/fields/rich-text-editor.tsx +22 -22
  15. package/src/components/forms/fields/select-field.tsx +3 -3
  16. package/src/components/forms/form-engine.tsx +3 -3
  17. package/src/components/forms/form-field-renderer.tsx +37 -37
  18. package/src/components/layout/admin-shell.tsx +60 -60
  19. package/src/components/live-preview/LivePreviewPane.tsx +14 -14
  20. package/src/components/media/focal-point-picker.tsx +9 -9
  21. package/src/components/media/media-card.tsx +10 -10
  22. package/src/components/media/media-grid.tsx +3 -3
  23. package/src/components/media/media-library-dialog.tsx +105 -105
  24. package/src/components/ui/badge.tsx +5 -5
  25. package/src/components/ui/button.tsx +11 -11
  26. package/src/components/ui/calendar.tsx +36 -36
  27. package/src/components/ui/card.tsx +6 -6
  28. package/src/components/ui/checkbox.tsx +3 -3
  29. package/src/components/ui/command.tsx +12 -12
  30. package/src/components/ui/data-table.tsx +18 -18
  31. package/src/components/ui/dialog.tsx +9 -9
  32. package/src/components/ui/dropdown-menu.tsx +16 -16
  33. package/src/components/ui/form.tsx +4 -4
  34. package/src/components/ui/input.tsx +3 -3
  35. package/src/components/ui/label.tsx +1 -1
  36. package/src/components/ui/page-header.tsx +6 -6
  37. package/src/components/ui/pagination.tsx +6 -6
  38. package/src/components/ui/popover.tsx +1 -1
  39. package/src/components/ui/progress.tsx +2 -2
  40. package/src/components/ui/radio-group.tsx +4 -4
  41. package/src/components/ui/render-cell.tsx +16 -16
  42. package/src/components/ui/scroll-area.tsx +6 -6
  43. package/src/components/ui/select.tsx +14 -14
  44. package/src/components/ui/separator.tsx +2 -2
  45. package/src/components/ui/sheet.tsx +13 -13
  46. package/src/components/ui/sidebar.tsx +60 -60
  47. package/src/components/ui/skeleton.tsx +1 -1
  48. package/src/components/ui/sonner.tsx +1 -1
  49. package/src/components/ui/switch.tsx +2 -2
  50. package/src/components/ui/table.tsx +7 -7
  51. package/src/components/ui/tabs.tsx +3 -3
  52. package/src/components/ui/textarea.tsx +1 -1
  53. package/src/components/ui/toggle.tsx +6 -6
  54. package/src/components/ui/tooltip.tsx +1 -1
  55. package/src/index.css +27 -27
  56. package/src/index.tsx +4 -4
  57. package/src/lib/utils.ts +7 -3
  58. package/src/pages/auth/first-user-page.tsx +18 -18
  59. package/src/pages/auth/login-page.tsx +14 -14
  60. package/src/pages/collections/edit-page.tsx +37 -37
  61. package/src/pages/collections/list-page.tsx +23 -23
  62. package/src/pages/dashboard/dashboard.tsx +49 -49
  63. package/src/pages/globals/editor-page.tsx +13 -13
  64. package/src/pages/media/media-page.tsx +106 -106
  65. package/src/pages/setup/setup-prompt.tsx +73 -44
  66. package/tailwind.config.ts +1 -0
  67. package/vite.config.ts +0 -1
@@ -98,70 +98,70 @@ export function MediaPicker({
98
98
 
99
99
  if (multiple && !isIcon) {
100
100
  return (
101
- <div className="space-y-4">
101
+ <div className="dy-space-y-4">
102
102
  {label && (
103
- <label className="text-sm font-semibold text-foreground/70 tracking-tight leading-none">
103
+ <label className="dy-text-sm dy-font-semibold dy-text-foreground/70 dy-tracking-tight dy-leading-none">
104
104
  {label}
105
105
  </label>
106
106
  )}
107
107
 
108
- <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
108
+ <div className="dy-grid dy-grid-cols-2 md:dy-grid-cols-3 lg:dy-grid-cols-4 dy-gap-4">
109
109
  <button
110
110
  type="button"
111
111
  onClick={() => setIsOpen(true)}
112
- className="group relative aspect-square rounded-xl border-2 border-dashed border-muted hover:border-primary/40 hover:bg-primary/5 transition-all flex flex-col items-center justify-center gap-3 overflow-hidden"
112
+ className="dy-group dy-relative dy-aspect-square dy-rounded-xl dy-border-2 dy-border-dashed dy-border-muted hover:dy-border-primary/40 hover:dy-bg-primary/5 dy-transition-all dy-flex dy-flex-col dy-items-center dy-justify-center dy-gap-3 dy-overflow-hidden"
113
113
  >
114
- <div className="absolute inset-0 bg-primary/5 opacity-0 group-hover:opacity-100 transition-opacity" />
115
- <div className="h-12 w-12 bg-muted rounded-full flex items-center justify-center text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary transition-all shadow-inner">
116
- <Plus className="h-6 w-6" />
114
+ <div className="dy-absolute dy-inset-0 dy-bg-primary/5 dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-opacity" />
115
+ <div className="dy-h-12 dy-w-12 dy-bg-muted dy-rounded-full dy-flex dy-items-center dy-justify-center dy-text-muted-foreground dy-group-hover:dy-bg-primary/10 dy-group-hover:dy-text-primary dy-transition-all dy-shadow-inner">
116
+ <Plus className="dy-h-6 dy-w-6" />
117
117
  </div>
118
- <div className="text-center px-4">
119
- <p className="text-[11px] font-bold uppercase tracking-widest text-muted-foreground group-hover:text-primary transition-colors">Add Media</p>
120
- <p className="text-[10px] text-muted-foreground/40 mt-1 font-medium group-hover:text-primary/60">Select or upload</p>
118
+ <div className="dy-text-center dy-px-4">
119
+ <p className="dy-text-[11px] dy-font-bold dy-uppercase dy-tracking-widest dy-text-muted-foreground dy-group-hover:dy-text-primary dy-transition-colors">Add Media</p>
120
+ <p className="dy-text-[10px] dy-text-muted-foreground/40 dy-mt-1 dy-font-medium dy-group-hover:dy-text-primary/60">Select or upload</p>
121
121
  </div>
122
122
  </button>
123
123
 
124
124
  {selectedValues.map((val, index) => {
125
125
  const item = media?.find((m: any) => m.id === val)
126
126
  return (
127
- <div key={val} className="relative group animate-in zoom-in duration-300">
127
+ <div key={val} className="dy-relative dy-group dy-animate-in dy-zoom-in dy-duration-300">
128
128
  <div className={cn(
129
- "relative aspect-square rounded-xl overflow-hidden border-2 bg-muted/20 transition-all shadow-sm",
130
- index === 0 ? "border-primary ring-4 ring-primary/10" : "border-border/40 hover:border-border/80"
129
+ "dy-relative dy-aspect-square dy-rounded-xl dy-overflow-hidden dy-border-2 dy-bg-muted/20 dy-transition-all dy-shadow-sm",
130
+ index === 0 ? "dy-border-primary dy-ring-4 dy-ring-primary/10" : "dy-border-border/40 hover:dy-border-border/80"
131
131
  )}>
132
132
  {item ? (
133
133
  <img
134
134
  src={getPreviewUrl(item)}
135
135
  alt=""
136
- className="w-full h-full object-cover transition-transform group-hover:scale-110"
136
+ className="dy-w-full dy-h-full dy-object-cover dy-transition-transform dy-group-hover:dy-scale-110"
137
137
  />
138
138
  ) : (
139
- <div className="w-full h-full animate-pulse bg-muted/50 flex items-center justify-center">
140
- <ImageIcon className="h-6 w-6 text-muted-foreground/20" />
139
+ <div className="dy-w-full dy-h-full dy-animate-pulse dy-bg-muted/50 dy-flex dy-items-center dy-justify-center">
140
+ <ImageIcon className="dy-h-6 dy-w-6 dy-text-muted-foreground/20" />
141
141
  </div>
142
142
  )}
143
143
 
144
144
  {index === 0 && (
145
- <div className="absolute top-0 left-0 w-full text-center z-10 px-3 py-1 bg-primary text-white text-[9px] font-black uppercase tracking-widest shadow-primary/20">
145
+ <div className="dy-absolute dy-top-0 dy-left-0 dy-w-full dy-text-center dy-z-10 dy-px-3 dy-py-1 dy-bg-primary dy-text-white dy-text-[9px] dy-font-black dy-uppercase dy-tracking-widest dy-shadow-primary/20">
146
146
  Main Image
147
147
  </div>
148
148
  )}
149
149
 
150
- <div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-all flex items-start justify-end backdrop-blur-[2px]">
150
+ <div className="dy-absolute dy-inset-0 dy-bg-black/40 dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-all dy-flex dy-items-start dy-justify-end dy-backdrop-blur-[2px]">
151
151
  <Button
152
152
  type="button"
153
153
  variant="outline"
154
154
  size="icon"
155
- className="h-8 w-8 rounded-lg text-destructive bg-destructive-foreground shadow-2xl scale-75 group-hover:scale-100 transition-all"
155
+ className="dy-h-8 dy-w-8 dy-rounded-lg dy-text-destructive dy-bg-destructive-foreground dy-shadow-2xl dy-scale-75 dy-group-hover:dy-scale-100 dy-transition-all"
156
156
  onClick={() => toggleValue(val)}
157
157
  >
158
- <Trash2 className="w-5 h-5" />
158
+ <Trash2 className="dy-w-5 dy-h-5" />
159
159
  </Button>
160
160
  </div>
161
161
 
162
162
  {item && (
163
- <div className="absolute inset-x-0 bottom-0 p-2 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity">
164
- <p className="text-[10px] text-white truncate font-medium">{item.filename}</p>
163
+ <div className="dy-absolute dy-inset-x-0 dy-bottom-0 dy-p-2 dy-bg-gradient-to-t dy-from-black/60 dy-to-transparent dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-opacity">
164
+ <p className="dy-text-[10px] dy-text-white dy-truncate dy-font-medium">{item.filename}</p>
165
165
  </div>
166
166
  )}
167
167
  </div>
@@ -186,41 +186,41 @@ export function MediaPicker({
186
186
  return (
187
187
  <div className={isIcon ? "" : "space-y-3"}>
188
188
  {label && !isIcon && (
189
- <label className="text-sm font-semibold text-foreground/70 tracking-tight leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
189
+ <label className="dy-text-sm dy-font-semibold dy-text-foreground/70 dy-tracking-tight dy-leading-none dy-peer-disabled:dy-cursor-not-allowed dy-peer-disabled:dy-opacity-70">
190
190
  {label}
191
191
  </label>
192
192
  )}
193
193
 
194
194
  <div className={isIcon ? "" : "relative flex items-center gap-2"}>
195
195
  {!isIcon && (
196
- <div className="relative flex-1 group">
196
+ <div className="dy-relative dy-flex-1 dy-group">
197
197
  <Input
198
198
  value={displayValue}
199
199
  readOnly
200
200
  disabled={disabled}
201
201
  placeholder={placeholder || "No media selected"}
202
- className="pr-24 bg-muted/30 border-dashed focus-visible:ring-offset-0 focus-visible:ring-1 h-10 rounded-xl"
202
+ className="dy-pr-24 dy-bg-muted/30 dy-border-dashed focus-visible:dy-ring-offset-0 focus-visible:dy-ring-1 dy-h-10 dy-rounded-xl"
203
203
  />
204
- <div className="absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-1 pr-1">
204
+ <div className="dy-absolute dy-right-1 dy-top-1/2 dy--translate-y-1/2 dy-flex dy-items-center dy-gap-1 dy-pr-1">
205
205
  {value && (
206
206
  <Button
207
207
  type="button"
208
208
  variant="ghost"
209
209
  size="icon"
210
- className="h-7 w-7 text-muted-foreground hover:text-destructive transition-colors rounded-lg"
210
+ className="dy-h-7 dy-w-7 dy-text-muted-foreground hover:dy-text-destructive dy-transition-colors dy-rounded-lg"
211
211
  onClick={(e) => {
212
212
  e.preventDefault();
213
213
  onChange(multiple ? [] : "");
214
214
  }}
215
215
  >
216
- <X className="h-4 w-4" />
216
+ <X className="dy-h-4 dy-w-4" />
217
217
  </Button>
218
218
  )}
219
219
  <Button
220
220
  type="button"
221
221
  variant="secondary"
222
222
  size="sm"
223
- className="h-8 text-xs font-bold px-3 rounded-lg shadow-sm border border-border/50"
223
+ className="dy-h-8 dy-text-xs dy-font-bold dy-px-3 dy-rounded-lg dy-shadow-sm dy-border dy-border-border/50"
224
224
  disabled={disabled}
225
225
  onClick={() => setIsOpen(true)}
226
226
  >
@@ -231,8 +231,8 @@ export function MediaPicker({
231
231
  )}
232
232
 
233
233
  {isIcon && (
234
- <Button variant="ghost" size="sm" className="px-2 h-8 w-8 rounded-lg" disabled={disabled} onClick={() => setIsOpen(true)}>
235
- <ImageIcon className="h-4 w-4" />
234
+ <Button variant="ghost" size="sm" className="dy-px-2 dy-h-8 dy-w-8 dy-rounded-lg" disabled={disabled} onClick={() => setIsOpen(true)}>
235
+ <ImageIcon className="dy-h-4 dy-w-4" />
236
236
  </Button>
237
237
  )}
238
238
 
@@ -248,33 +248,33 @@ export function MediaPicker({
248
248
  </div>
249
249
 
250
250
  {!isIcon && selectedValues.length > 0 && !multiple && (
251
- <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 pt-2">
251
+ <div className="dy-grid dy-grid-cols-2 sm:dy-grid-cols-3 md:dy-grid-cols-4 lg:dy-grid-cols-5 dy-gap-4 dy-pt-2">
252
252
  {selectedValues.map((val) => {
253
253
  const item = media?.find((m: any) => m.id === val)
254
254
  if (!item) return (
255
- <div key={val} className="aspect-square rounded-xl bg-muted/20 animate-pulse border-2 border-dashed border-border/50" />
255
+ <div key={val} className="dy-aspect-square dy-rounded-xl dy-bg-muted/20 dy-animate-pulse dy-border-2 dy-border-dashed dy-border-border/50" />
256
256
  )
257
257
  return (
258
258
  <div
259
259
  key={val}
260
- className="relative aspect-square group rounded-2xl overflow-hidden border-2 border-border/50 hover:border-primary/50 transition-all bg-muted/20 shadow-sm"
260
+ className="dy-relative dy-aspect-square dy-group dy-rounded-2xl dy-overflow-hidden dy-border-2 dy-border-border/50 hover:dy-border-primary/50 dy-transition-all dy-bg-muted/20 dy-shadow-sm"
261
261
  >
262
262
  <img
263
263
  src={getPreviewUrl(item)}
264
264
  alt=""
265
- className="w-full h-full object-cover transition-transform group-hover:scale-110"
265
+ className="dy-w-full dy-h-full dy-object-cover dy-transition-transform dy-group-hover:dy-scale-110"
266
266
  />
267
267
  {!disabled && (
268
268
  <button
269
269
  type="button"
270
270
  onClick={() => toggleValue(val)}
271
- className="absolute top-2 right-2 p-1.5 bg-destructive text-destructive-foreground rounded-full opacity-0 group-hover:opacity-100 transition-all hover:scale-110 shadow-lg border-2 border-white"
271
+ className="dy-absolute dy-top-2 dy-right-2 dy-p-1.5 dy-bg-destructive dy-text-destructive-foreground dy-rounded-full dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-all hover:dy-scale-110 dy-shadow-lg dy-border-2 dy-border-white"
272
272
  >
273
- <X className="h-3.5 w-3.5" />
273
+ <X className="dy-h-3.5 dy-w-3.5" />
274
274
  </button>
275
275
  )}
276
- <div className="absolute inset-x-0 bottom-0 p-2 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity">
277
- <p className="text-[10px] text-white truncate font-medium">{item.filename}</p>
276
+ <div className="dy-absolute dy-inset-x-0 dy-bottom-0 dy-p-2 dy-bg-gradient-to-t dy-from-black/60 dy-to-transparent dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-opacity">
277
+ <p className="dy-text-[10px] dy-text-white dy-truncate dy-font-medium">{item.filename}</p>
278
278
  </div>
279
279
  </div>
280
280
  )
@@ -61,8 +61,8 @@ export function MultiSelect({
61
61
  }
62
62
 
63
63
  return (
64
- <div className="flex flex-col gap-2">
65
- {label && <label className="text-sm font-medium leading-none">{label}</label>}
64
+ <div className="dy-flex dy-flex-col dy-gap-2">
65
+ {label && <label className="dy-text-sm dy-font-medium dy-leading-none">{label}</label>}
66
66
  <Popover open={disabled ? false : open} onOpenChange={setOpen}>
67
67
  <PopoverTrigger asChild>
68
68
  <Button
@@ -70,11 +70,11 @@ export function MultiSelect({
70
70
  role="combobox"
71
71
  aria-expanded={open}
72
72
  disabled={disabled}
73
- className="w-full justify-between h-auto min-h-10 font-normal"
73
+ className="dy-w-full dy-justify-between dy-h-auto dy-min-h-10 dy-font-normal"
74
74
  >
75
- <div className="flex flex-wrap gap-1 items-center">
75
+ <div className="dy-flex dy-flex-wrap dy-gap-1 dy-items-center">
76
76
  {value.length === 0 && (
77
- <span className="text-muted-foreground">{placeholder}</span>
77
+ <span className="dy-text-muted-foreground">{placeholder}</span>
78
78
  )}
79
79
  {value.map((val) => {
80
80
  const option = options.find((opt) => opt.value === val)
@@ -82,14 +82,14 @@ export function MultiSelect({
82
82
  <Badge
83
83
  key={val}
84
84
  variant="secondary"
85
- className="mr-1 mb-1 items-center gap-1"
85
+ className="dy-mr-1 dy-mb-1 dy-items-center dy-gap-1"
86
86
  >
87
87
  {option?.label || val}
88
88
  {!disabled && (
89
89
  <div
90
90
  role="button"
91
91
  tabIndex={0}
92
- className="ring-offset-background rounded-full outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
92
+ className="dy-ring-offset-background dy-rounded-full dy-outline-none focus:dy-ring-2 focus:dy-ring-ring focus:dy-ring-offset-2"
93
93
  onKeyDown={(e) => {
94
94
  if (e.key === "Enter") {
95
95
  handleRemove(val)
@@ -101,17 +101,17 @@ export function MultiSelect({
101
101
  }}
102
102
  onClick={() => handleRemove(val)}
103
103
  >
104
- <X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
104
+ <X className="dy-h-3 dy-w-3 dy-text-muted-foreground hover:dy-text-foreground" />
105
105
  </div>
106
106
  )}
107
107
  </Badge>
108
108
  )
109
109
  })}
110
110
  </div>
111
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
111
+ <ChevronsUpDown className="dy-ml-2 dy-h-4 dy-w-4 dy-shrink-0 dy-opacity-50" />
112
112
  </Button>
113
113
  </PopoverTrigger>
114
- <PopoverContent className="w-[400px] p-0" align="start">
114
+ <PopoverContent className="dy-w-[400px] dy-p-0" align="start">
115
115
  <Command>
116
116
  <CommandInput placeholder="Search options..." />
117
117
  <CommandList>
@@ -127,8 +127,8 @@ export function MultiSelect({
127
127
  >
128
128
  <Check
129
129
  className={cn(
130
- "mr-2 h-4 w-4",
131
- isSelected ? "opacity-100" : "opacity-0"
130
+ "dy-mr-2 dy-h-4 dy-w-4",
131
+ isSelected ? "dy-opacity-100" : "dy-opacity-0"
132
132
  )}
133
133
  />
134
134
  {option.label}
@@ -20,26 +20,26 @@ export function RadioField({ schema, field, disabled }: RadioFieldProps) {
20
20
  defaultValue={field.value}
21
21
  disabled={disabled}
22
22
  className={cn(
23
- "gap-4",
24
- isHorizontal ? "flex flex-wrap items-center" : "flex flex-col"
23
+ "dy-gap-4",
24
+ isHorizontal ? "dy-flex dy-flex-wrap dy-items-center" : "dy-flex dy-flex-col"
25
25
  )}
26
26
  >
27
27
  {options.map((opt) => (
28
28
  <div key={opt.value} className={cn(
29
- "relative flex items-center",
30
- isHorizontal ? "min-w-[120px]" : "w-full"
29
+ "dy-relative dy-flex dy-items-center",
30
+ isHorizontal ? "dy-min-w-[120px]" : "dy-w-full"
31
31
  )}>
32
32
  <RadioGroupItem
33
33
  value={opt.value}
34
34
  id={`${field.name}-${opt.value}`}
35
- className="peer absolute left-4 z-10"
35
+ className="dy-peer dy-absolute dy-left-4 dy-z-10"
36
36
  />
37
37
  <Label
38
38
  htmlFor={`${field.name}-${opt.value}`}
39
39
  className={cn(
40
- "flex flex-1 items-center pl-12 pr-4 py-3 rounded-xl border border-border/40 bg-white/50 cursor-pointer transition-all hover:bg-white/80 hover:shadow-sm",
41
- "peer-data-[state=checked]:border-primary peer-data-[state=checked]:bg-primary/5 peer-data-[state=checked]:shadow-md peer-data-[state=checked]:ring-1 peer-data-[state=checked]:ring-primary/20",
42
- "text-sm font-medium text-foreground/70 peer-data-[state=checked]:text-primary"
40
+ "dy-flex dy-flex-1 dy-items-center dy-pl-12 dy-pr-4 dy-py-3 dy-rounded-xl dy-border dy-border-border/40 dy-bg-white/50 dy-cursor-pointer dy-transition-all hover:dy-bg-white/80 hover:dy-shadow-sm",
41
+ "dy-peer-data-[state=checked]:dy-border-primary dy-peer-data-[state=checked]:dy-bg-primary/5 dy-peer-data-[state=checked]:dy-shadow-md dy-peer-data-[state=checked]:dy-ring-1 dy-peer-data-[state=checked]:dy-ring-primary/20",
42
+ "dy-text-sm dy-font-medium dy-text-foreground/70 dy-peer-data-[state=checked]:dy-text-primary"
43
43
  )}
44
44
  >
45
45
  {opt.label}
@@ -61,8 +61,8 @@ export function RelationshipPicker({ value, onChange, label, relationTo, multipl
61
61
  const selectedItems = values.map(v => data?.find((item: any) => item.id === v)).filter(Boolean)
62
62
 
63
63
  return (
64
- <div className="flex flex-col gap-2">
65
- {label && <label className="text-sm font-medium leading-none">{label}</label>}
64
+ <div className="dy-flex dy-flex-col dy-gap-2">
65
+ {label && <label className="dy-text-sm dy-font-medium dy-leading-none">{label}</label>}
66
66
  <Popover open={disabled ? false : open} onOpenChange={setOpen}>
67
67
  <PopoverTrigger asChild>
68
68
  <Button
@@ -70,25 +70,25 @@ export function RelationshipPicker({ value, onChange, label, relationTo, multipl
70
70
  role="combobox"
71
71
  aria-expanded={open}
72
72
  disabled={disabled}
73
- className="w-full justify-between font-normal"
73
+ className="dy-w-full dy-justify-between dy-font-normal"
74
74
  >
75
75
  {isLoading ? (
76
76
  "Loading..."
77
77
  ) : selectedItems.length > 0 ? (
78
- <div className="flex flex-wrap gap-1">
78
+ <div className="dy-flex dy-flex-wrap dy-gap-1">
79
79
  {selectedItems.map((item: any) => (
80
- <Badge key={item.id} variant="secondary" className="text-[10px] h-5 px-1.5">
80
+ <Badge key={item.id} variant="secondary" className="dy-text-[10px] dy-h-5 dy-px-1.5">
81
81
  {getDisplayLabel(item)}
82
82
  </Badge>
83
83
  ))}
84
84
  </div>
85
85
  ) : (
86
- <span className="text-muted-foreground">Select {relationTo}...</span>
86
+ <span className="dy-text-muted-foreground">Select {relationTo}...</span>
87
87
  )}
88
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
88
+ <ChevronsUpDown className="dy-ml-2 dy-h-4 dy-w-4 dy-shrink-0 dy-opacity-50" />
89
89
  </Button>
90
90
  </PopoverTrigger>
91
- <PopoverContent className="w-[400px] p-0" align="start">
91
+ <PopoverContent className="dy-w-[400px] dy-p-0" align="start">
92
92
  <Command>
93
93
  <CommandInput
94
94
  placeholder={`Search ${relationTo}...`}
@@ -113,20 +113,20 @@ export function RelationshipPicker({ value, onChange, label, relationTo, multipl
113
113
  }
114
114
  }}
115
115
  >
116
- <div className="flex items-center gap-3 flex-1">
116
+ <div className="dy-flex dy-items-center dy-gap-3 dy-flex-1">
117
117
  {isUpload && (
118
- <div className="h-6 w-6 rounded border bg-muted overflow-hidden flex-shrink-0">
118
+ <div className="dy-h-6 dy-w-6 dy-rounded dy-border dy-bg-muted dy-overflow-hidden dy-flex-shrink-0">
119
119
  <img
120
120
  src={getMediaUrl(item, client?.getBaseUrl() || "")}
121
- className="h-full w-full object-cover"
121
+ className="dy-h-full dy-w-full dy-object-cover"
122
122
  alt=""
123
123
  />
124
124
  </div>
125
125
  )}
126
- <span className="flex-1">{getDisplayLabel(item)}</span>
126
+ <span className="dy-flex-1">{getDisplayLabel(item)}</span>
127
127
  <Check
128
128
  className={cn(
129
- "h-4 w-4",
129
+ "dy-h-4 dy-w-4",
130
130
  values.includes(item.id) ? "opacity-100" : "opacity-0"
131
131
  )}
132
132
  />
@@ -45,109 +45,109 @@ const MenuBar = ({ editor, collection = "media" }: { editor: Editor | null, coll
45
45
  }
46
46
 
47
47
  return (
48
- <div className="border border-input rounded-t-md p-1 flex flex-wrap gap-1 items-center bg-muted/50">
48
+ <div className="dy-border dy-border-input dy-rounded-t-md dy-p-1 dy-flex dy-flex-wrap dy-gap-1 dy-items-center dy-bg-muted/50">
49
49
  <Toggle
50
50
  size="sm"
51
51
  pressed={editor.isActive("bold")}
52
52
  onPressedChange={() => editor.chain().focus().toggleBold().run()}
53
53
  >
54
- <Bold className="h-4 w-4" />
54
+ <Bold className="dy-h-4 dy-w-4" />
55
55
  </Toggle>
56
56
  <Toggle
57
57
  size="sm"
58
58
  pressed={editor.isActive("italic")}
59
59
  onPressedChange={() => editor.chain().focus().toggleItalic().run()}
60
60
  >
61
- <Italic className="h-4 w-4" />
61
+ <Italic className="dy-h-4 dy-w-4" />
62
62
  </Toggle>
63
63
  <Toggle
64
64
  size="sm"
65
65
  pressed={editor.isActive("underline")}
66
66
  onPressedChange={() => editor.chain().focus().toggleUnderline().run()}
67
67
  >
68
- <UnderlineIcon className="h-4 w-4" />
68
+ <UnderlineIcon className="dy-h-4 dy-w-4" />
69
69
  </Toggle>
70
70
  <Toggle
71
71
  size="sm"
72
72
  pressed={editor.isActive("strike")}
73
73
  onPressedChange={() => editor.chain().focus().toggleStrike().run()}
74
74
  >
75
- <Strikethrough className="h-4 w-4" />
75
+ <Strikethrough className="dy-h-4 dy-w-4" />
76
76
  </Toggle>
77
77
 
78
- <div className="w-[1px] h-6 bg-border mx-1" />
78
+ <div className="dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" />
79
79
 
80
80
  <Toggle
81
81
  size="sm"
82
82
  pressed={editor.isActive("heading", { level: 1 })}
83
83
  onPressedChange={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
84
84
  >
85
- <Heading1 className="h-4 w-4" />
85
+ <Heading1 className="dy-h-4 dy-w-4" />
86
86
  </Toggle>
87
87
  <Toggle
88
88
  size="sm"
89
89
  pressed={editor.isActive("heading", { level: 2 })}
90
90
  onPressedChange={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
91
91
  >
92
- <Heading2 className="h-4 w-4" />
92
+ <Heading2 className="dy-h-4 dy-w-4" />
93
93
  </Toggle>
94
94
  <Toggle
95
95
  size="sm"
96
96
  pressed={editor.isActive("bulletList")}
97
97
  onPressedChange={() => editor.chain().focus().toggleBulletList().run()}
98
98
  >
99
- <List className="h-4 w-4" />
99
+ <List className="dy-h-4 dy-w-4" />
100
100
  </Toggle>
101
101
  <Toggle
102
102
  size="sm"
103
103
  pressed={editor.isActive("orderedList")}
104
104
  onPressedChange={() => editor.chain().focus().toggleOrderedList().run()}
105
105
  >
106
- <ListOrdered className="h-4 w-4" />
106
+ <ListOrdered className="dy-h-4 dy-w-4" />
107
107
  </Toggle>
108
108
  <Toggle
109
109
  size="sm"
110
110
  pressed={editor.isActive("blockquote")}
111
111
  onPressedChange={() => editor.chain().focus().toggleBlockquote().run()}
112
112
  >
113
- <Quote className="h-4 w-4" />
113
+ <Quote className="dy-h-4 dy-w-4" />
114
114
  </Toggle>
115
115
 
116
- <div className="w-[1px] h-6 bg-border mx-1" />
116
+ <div className="dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" />
117
117
 
118
118
  <Toggle
119
119
  size="sm"
120
120
  pressed={editor.isActive({ textAlign: "left" })}
121
121
  onPressedChange={() => editor.chain().focus().setTextAlign("left").run()}
122
122
  >
123
- <AlignLeft className="h-4 w-4" />
123
+ <AlignLeft className="dy-h-4 dy-w-4" />
124
124
  </Toggle>
125
125
  <Toggle
126
126
  size="sm"
127
127
  pressed={editor.isActive({ textAlign: "center" })}
128
128
  onPressedChange={() => editor.chain().focus().setTextAlign("center").run()}
129
129
  >
130
- <AlignCenter className="h-4 w-4" />
130
+ <AlignCenter className="dy-h-4 dy-w-4" />
131
131
  </Toggle>
132
132
  <Toggle
133
133
  size="sm"
134
134
  pressed={editor.isActive({ textAlign: "right" })}
135
135
  onPressedChange={() => editor.chain().focus().setTextAlign("right").run()}
136
136
  >
137
- <AlignRight className="h-4 w-4" />
137
+ <AlignRight className="dy-h-4 dy-w-4" />
138
138
  </Toggle>
139
139
 
140
- <div className="w-[1px] h-6 bg-border mx-1" />
140
+ <div className="dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" />
141
141
 
142
142
  <Toggle
143
143
  size="sm"
144
144
  pressed={editor.isActive("link")}
145
145
  onPressedChange={addLink}
146
146
  >
147
- <LinkIcon className="h-4 w-4" />
147
+ <LinkIcon className="dy-h-4 dy-w-4" />
148
148
  </Toggle>
149
149
 
150
- <div className="ml-auto">
150
+ <div className="dy-ml-auto">
151
151
  <MediaPicker
152
152
  collection={collection}
153
153
  variant="icon"
@@ -213,11 +213,11 @@ export function RichTextEditor({ value, onChange, label, disabled, collection =
213
213
  }, [value, editor])
214
214
 
215
215
  return (
216
- <div className="space-y-2">
217
- {label && <label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">{label}</label>}
218
- <div className="flex flex-col w-full">
216
+ <div className="dy-space-y-2">
217
+ {label && <label className="dy-text-sm dy-font-medium dy-leading-none dy-peer-disabled:dy-cursor-not-allowed dy-peer-disabled:dy-opacity-70">{label}</label>}
218
+ <div className="dy-flex dy-flex-col dy-w-full">
219
219
  {!disabled && <MenuBar editor={editor} collection={collection} />}
220
- <EditorContent editor={editor} className={cn(disabled && "opacity-80")} />
220
+ <EditorContent editor={editor} className={cn(disabled && "dy-opacity-80")} />
221
221
  </div>
222
222
  </div>
223
223
  )
@@ -20,12 +20,12 @@ export function SelectField({ schema, field, disabled }: SelectFieldProps) {
20
20
 
21
21
  return (
22
22
  <Select onValueChange={field.onChange} defaultValue={field.value} disabled={disabled}>
23
- <SelectTrigger className="h-12 rounded-xl border-border/40 bg-white/50 focus:ring-0 focus:ring-offset-0 focus:bg-white shadow-sm transition-all hover:shadow-md">
23
+ <SelectTrigger className="dy-h-12 dy-rounded-xl dy-border-border/40 dy-bg-white/50 focus:dy-ring-0 focus:dy-ring-offset-0 focus:dy-bg-white dy-shadow-sm dy-transition-all hover:dy-shadow-md">
24
24
  <SelectValue placeholder={schema.admin?.placeholder || `Select ${label.toLowerCase()}`} />
25
25
  </SelectTrigger>
26
- <SelectContent className="rounded-xl border-border/40 shadow-xl animate-in fade-in zoom-in-95">
26
+ <SelectContent className="dy-rounded-xl dy-border-border/40 dy-shadow-xl dy-animate-in dy-fade-in dy-zoom-in-95">
27
27
  {options.map((opt) => (
28
- <SelectItem key={opt.value} value={opt.value} className="rounded-lg focus:bg-primary/5 focus:text-primary transition-colors">
28
+ <SelectItem key={opt.value} value={opt.value} className="dy-rounded-lg focus:dy-bg-primary/5 focus:dy-text-primary dy-transition-colors">
29
29
  {opt.label}
30
30
  </SelectItem>
31
31
  ))}
@@ -62,8 +62,8 @@ export function FormEngine({
62
62
 
63
63
  return (
64
64
  <Form {...form}>
65
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
66
- <div className="grid gap-6">
65
+ <form onSubmit={form.handleSubmit(onSubmit)} className="dy-space-y-8">
66
+ <div className="dy-grid dy-gap-6">
67
67
  {fields.filter(f => !f.admin?.hidden).map((field) => (
68
68
  <FormFieldRenderer
69
69
  key={field.name}
@@ -74,7 +74,7 @@ export function FormEngine({
74
74
  />
75
75
  ))}
76
76
  </div>
77
- <div className="flex justify-end gap-4">
77
+ <div className="dy-flex dy-justify-end dy-gap-4">
78
78
  {!readOnly && (
79
79
  <Button type="submit" disabled={isLoading}>
80
80
  {isLoading ? "Saving..." : submitLabel}