@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @dyrected/admin
2
2
 
3
+ ## 2.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Infrastructure standardization, MySQL adapter improvements, and SDK robustness testing.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @dyrected/core@2.4.0
13
+ - @dyrected/sdk@2.4.0
14
+
15
+ ## 2.3.0
16
+
17
+ ### Minor Changes
18
+
19
+ - Standardize database infrastructure and implement field promotion.
20
+ - **Field Promotion**: Added 'promoted' option to Collection fields to extract JSON data into native SQL columns for indexing and performance.
21
+ - **Lazy Migrations**: Added 'renameTo' support for seamless field renames without breaking existing data.
22
+ - **Auto-Seeding**: Standardized 'initialData' seeding logic across all adapters.
23
+ - **MySQL Adapter**: New robust MySQL adapter implementation.
24
+ - **Strict Filtering**: Improved query translation parity across all SQL-based adapters.
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies
29
+ - @dyrected/core@2.3.0
30
+ - @dyrected/sdk@2.3.0
31
+
32
+ ## 2.0.1
33
+
34
+ ### Patch Changes
35
+
36
+ - 220818c: ### @dyrected/core
37
+ - **New Discovery Workflow**: Refined the AI setup prompt with a multi-step "Phase 0" discovery process to improve initial project scoping.
38
+ - **Nomenclature Standardization**: Updated all system prompts to use "Nuxt.js" nomenclature and improved schema definition examples.
39
+
40
+ ### @dyrected/db-postgres & @dyrected/db-sqlite
41
+ - **Architecture Documentation**: Added source-level documentation explaining the use of raw SQL drivers (postgres.js/better-sqlite3) alongside Drizzle to support dynamic runtime schemas.
42
+
43
+ ### @dyrected/admin
44
+ - **Internal Maintenance**: Synchronized internal documentation and field renderer context to support the latest core setup workflows.
45
+
46
+ - Updated dependencies [220818c]
47
+ - @dyrected/core@2.1.0
48
+
3
49
  ## 2.0.0
4
50
 
5
51
  ### Major Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/admin",
3
- "version": "2.0.0",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -60,8 +60,8 @@
60
60
  "tailwind-merge": "^3.5.0",
61
61
  "tailwindcss-animate": "^1.0.7",
62
62
  "zod": "^3.25.76",
63
- "@dyrected/sdk": "^2.0.0",
64
- "@dyrected/core": "^2.0.0"
63
+ "@dyrected/core": "^2.4.0",
64
+ "@dyrected/sdk": "^2.4.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@eslint/js": "^10.0.1",
@@ -76,7 +76,7 @@
76
76
  "globals": "^17.5.0",
77
77
  "postcss": "^8.5.14",
78
78
  "tailwindcss": "^3.4.19",
79
- "typescript": "~6.0.2",
79
+ "typescript": "^5.7.3",
80
80
  "typescript-eslint": "^8.58.2",
81
81
  "vite": "^8.0.10",
82
82
  "vite-plugin-dts": "^5.0.0",
@@ -0,0 +1,98 @@
1
+ import os
2
+ import re
3
+
4
+ EXCLUDED_WORDS = {
5
+ "default", "ghost", "outline", "secondary", "destructive", "link", "icon",
6
+ "sm", "md", "lg", "xl", "2xl", "top", "bottom", "left", "right",
7
+ "expanded", "collapsed", "sidebar", "floating", "inset", "offcanvas", "none",
8
+ "sidebar_state", "top-right", "asChild", "className", "variant", "size", "side",
9
+ "true", "false", "item", "index", "key", "id", "name", "type", "value", "variant"
10
+ }
11
+
12
+ def prefix_single_class(c):
13
+ if not c or c.startswith(("dy-", "{", "$", "--", "http")):
14
+ return c
15
+
16
+ if ":" in c:
17
+ parts = c.split(":")
18
+ modifiers = parts[:-1]
19
+ base = parts[-1]
20
+
21
+ new_modifiers = []
22
+ for m in modifiers:
23
+ if m.startswith(("group", "peer")):
24
+ new_modifiers.append(f"dy-{m}")
25
+ else:
26
+ new_modifiers.append(m)
27
+
28
+ if base and base not in EXCLUDED_WORDS and not base.startswith("["):
29
+ base = f"dy-{base}"
30
+
31
+ return ":".join(new_modifiers + [base])
32
+ else:
33
+ if c in EXCLUDED_WORDS or c.startswith("["):
34
+ return c
35
+ if c.startswith(("group", "peer")):
36
+ return f"dy-{c}"
37
+ return f"dy-{c}"
38
+
39
+ def prefix_classes_in_string(s):
40
+ if not s: return s
41
+ return " ".join([prefix_single_class(c) for c in s.split()])
42
+
43
+ def process_content(content):
44
+ # 1. className="literal"
45
+ def class_name_replacer(m):
46
+ quote = m.group(1)
47
+ classes = m.group(2)
48
+ return f'className={quote}{prefix_classes_in_string(classes)}{quote}'
49
+ content = re.sub(r'className=([\'"])(.*?)\1', class_name_replacer, content)
50
+
51
+ # 2. cn(...)
52
+ def cn_replacer(m):
53
+ inner = m.group(1)
54
+ def inner_string_replacer(sm):
55
+ quote = sm.group(1)
56
+ s = sm.group(2)
57
+ preceding = inner[:sm.start()]
58
+ # Only exclude if it's a comparison or arrow function
59
+ if preceding.strip().endswith(("===", "!==", "==", "!=", "=>")):
60
+ return sm.group(0)
61
+
62
+ return f'{quote}{prefix_classes_in_string(s)}{quote}'
63
+ new_inner = re.sub(r'([\'"])(.*?)\1', inner_string_replacer, inner)
64
+ return f'cn({new_inner})'
65
+ content = re.sub(r'cn\((.*?)\)', cn_replacer, content, flags=re.DOTALL)
66
+
67
+ # 3. cva(...)
68
+ def cva_replacer(m):
69
+ inner = m.group(1)
70
+ def cva_string_replacer(sm):
71
+ quote = sm.group(1)
72
+ s = sm.group(2)
73
+ following = inner[sm.end():]
74
+ if following.strip().startswith(":"):
75
+ return sm.group(0)
76
+ return f'{quote}{prefix_classes_in_string(s)}{quote}'
77
+ new_inner = re.sub(r'([\'"])(.*?)\1', cva_string_replacer, inner)
78
+ return f'cva({new_inner})'
79
+ content = re.sub(r'cva\((.*?)\)', cva_replacer, content, flags=re.DOTALL)
80
+
81
+ return content
82
+
83
+ def main():
84
+ src_dir = "packages/admin/src"
85
+ for root, dirs, files in os.walk(src_dir):
86
+ for file in files:
87
+ if file.endswith((".tsx", ".ts")):
88
+ filepath = os.path.join(root, file)
89
+ with open(filepath, 'r') as f:
90
+ content = f.read()
91
+ new_content = process_content(content)
92
+ if new_content != content:
93
+ with open(filepath, 'w') as f:
94
+ f.write(new_content)
95
+ print(f"Updated {filepath}")
96
+
97
+ if __name__ == "__main__":
98
+ main()
@@ -0,0 +1,67 @@
1
+ import os
2
+ import re
3
+
4
+ def prefix_classes_in_string(s):
5
+ # Skip empty strings or strings that already have the prefix
6
+ if not s or s.startswith("dy-"):
7
+ return s
8
+
9
+ # Split by whitespace, prefix each class, then rejoin
10
+ classes = s.split()
11
+ prefixed = []
12
+ for c in classes:
13
+ # Skip variables like {className} or strings that look like CSS variables
14
+ if c.startswith("{") or c.startswith("$") or c.startswith("--"):
15
+ prefixed.append(c)
16
+ # Skip already prefixed
17
+ elif c.startswith("dy-"):
18
+ prefixed.append(c)
19
+ # Prefix everything else (assuming they are tailwind classes)
20
+ else:
21
+ prefixed.append(f"dy-{c}")
22
+ return " ".join(prefixed)
23
+
24
+ def process_content(content):
25
+ # 1. Handle className="literal classes"
26
+ content = re.sub(r'className=([\'"])(.*?)\1',
27
+ lambda m: f'className={m.group(1)}{prefix_classes_in_string(m.group(2))}{m.group(1)}',
28
+ content)
29
+
30
+ # 2. Handle className={cn("literal classes", ...)}
31
+ # We look for strings inside cn(...)
32
+ def cn_match(m):
33
+ # This is a bit rough but works for simple cn calls
34
+ inner = m.group(1)
35
+ # Replace string literals inside the cn call
36
+ new_inner = re.sub(r'([\'"])(.*?)\1',
37
+ lambda sm: f'{sm.group(1)}{prefix_classes_in_string(sm.group(2))}{sm.group(1)}',
38
+ inner)
39
+ return f'cn({new_inner})'
40
+
41
+ content = re.sub(r'cn\((.*?)\)', cn_match, content, flags=re.DOTALL)
42
+
43
+ # 3. Handle @apply in CSS
44
+ content = re.sub(r'@apply (.*?);',
45
+ lambda m: f'@apply {prefix_classes_in_string(m.group(1))};',
46
+ content)
47
+
48
+ return content
49
+
50
+ def main():
51
+ src_dir = "packages/admin/src"
52
+ for root, dirs, files in os.walk(src_dir):
53
+ for file in files:
54
+ if file.endswith((".tsx", ".ts", ".css")):
55
+ filepath = os.path.join(root, file)
56
+ with open(filepath, 'r') as f:
57
+ content = f.read()
58
+
59
+ new_content = process_content(content)
60
+
61
+ if new_content != content:
62
+ with open(filepath, 'w') as f:
63
+ f.write(new_content)
64
+ print(f"Updated {filepath}")
65
+
66
+ if __name__ == "__main__":
67
+ main()
@@ -32,10 +32,10 @@ export function AuthGate({ children }: { children: React.ReactNode }) {
32
32
 
33
33
  if (isLoading) {
34
34
  return (
35
- <div className="flex h-screen items-center justify-center bg-background">
36
- <div className="flex flex-col items-center gap-4">
37
- <div className="h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent" />
38
- <p className="text-sm text-muted-foreground animate-pulse">Authenticating...</p>
35
+ <div className="dy-flex dy-h-screen dy-items-center dy-justify-center dy-bg-background">
36
+ <div className="dy-flex dy-flex-col dy-items-center dy-gap-4">
37
+ <div className="dy-h-8 dy-w-8 dy-animate-spin dy-rounded-full dy-border-2 dy-border-primary dy-border-t-transparent" />
38
+ <p className="dy-text-sm dy-text-muted-foreground dy-animate-pulse">Authenticating...</p>
39
39
  </div>
40
40
  </div>
41
41
  );
@@ -27,12 +27,12 @@ export class ErrorBoundary extends Component<Props, State> {
27
27
  public render() {
28
28
  if (this.state.hasError) {
29
29
  return this.props.fallback || (
30
- <div className="flex-1 flex flex-col items-center justify-center p-12 bg-destructive/5 text-destructive min-h-[400px]">
31
- <h2 className="text-xl font-bold mb-2">Something went wrong</h2>
32
- <p className="text-sm opacity-80 mb-4">{this.state.error?.message}</p>
30
+ <div className="dy-flex-1 dy-flex dy-flex-col dy-items-center dy-justify-center dy-p-12 dy-bg-destructive/5 dy-text-destructive dy-min-h-[400px]">
31
+ <h2 className="dy-text-xl dy-font-bold dy-mb-2">Something went wrong</h2>
32
+ <p className="dy-text-sm dy-opacity-80 dy-mb-4">{this.state.error?.message}</p>
33
33
  <button
34
34
  onClick={() => window.location.reload()}
35
- className="px-4 py-2 bg-destructive text-destructive-foreground rounded-md text-sm font-medium hover:bg-destructive/90 transition-colors"
35
+ className="dy-px-4 dy-py-2 dy-bg-destructive dy-text-destructive-foreground dy-rounded-md dy-text-sm dy-font-medium hover:dy-bg-destructive/90 dy-transition-colors"
36
36
  >
37
37
  Reload Page
38
38
  </button>
@@ -77,33 +77,33 @@ function SortableBlockItem({
77
77
  if (!blockConfig) return null
78
78
 
79
79
  return (
80
- <div ref={setNodeRef} style={style} className="relative group left-accent mb-4 py-4 animate-in">
80
+ <div ref={setNodeRef} style={style} className="dy-relative dy-group dy-left-accent dy-mb-4 dy-py-4 dy-animate-in">
81
81
  {/* Header / Drag Handle */}
82
- <div className="flex items-center justify-between pb-3">
83
- <div className="flex items-center justify-between gap-2">
84
- <div {...attributes} {...listeners} className="cursor-grab opacity-20 group-hover:opacity-100 hover:bg-muted p-1 rounded-md transition-all">
85
- <GripVertical className="w-3.5 h-3.5 text-muted-foreground" />
82
+ <div className="dy-flex dy-items-center dy-justify-between dy-pb-3">
83
+ <div className="dy-flex dy-items-center dy-justify-between dy-gap-2">
84
+ <div {...attributes} {...listeners} className="dy-cursor-grab dy-opacity-20 dy-group-hover:dy-opacity-100 hover:dy-bg-muted dy-p-1 dy-rounded-md dy-transition-all">
85
+ <GripVertical className="dy-w-3.5 dy-h-3.5 dy-text-muted-foreground" />
86
86
  </div>
87
- <span className="font-bold text-xs text-foreground/70 tracking-tight">
87
+ <span className="dy-font-bold dy-text-xs dy-text-foreground/70 dy-tracking-tight">
88
88
  {blockConfig.labels?.singular || blockConfig.slug}
89
89
  </span>
90
- <span className="text-[10px] text-muted-foreground/40 ml-2 uppercase tracking-widest font-semibold">
90
+ <span className="dy-text-[10px] dy-text-muted-foreground/40 dy-ml-2 dy-uppercase dy-tracking-widest dy-font-semibold">
91
91
  Item {index + 1}
92
92
  </span>
93
93
  </div>
94
- <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
95
- <Button type="button" variant="ghost" size="icon" className="h-7 w-7 text-muted-foreground/40" onClick={() => setIsExpanded(!isExpanded)}>
96
- {isExpanded ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
94
+ <div className="dy-flex dy-items-center dy-gap-1 dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-opacity">
95
+ <Button type="button" variant="ghost" size="icon" className="dy-h-7 dy-w-7 dy-text-muted-foreground/40" onClick={() => setIsExpanded(!isExpanded)}>
96
+ {isExpanded ? <ChevronUp className="dy-w-3.5 dy-h-3.5" /> : <ChevronDown className="dy-w-3.5 dy-h-3.5" />}
97
97
  </Button>
98
- <Button type="button" variant="ghost" size="icon" className="h-7 w-7 text-muted-foreground/30 hover:text-destructive hover:bg-destructive/10" onClick={() => remove(index)}>
99
- <X className="w-3.5 h-3.5" />
98
+ <Button type="button" variant="ghost" size="icon" className="dy-h-7 dy-w-7 dy-text-muted-foreground/30 hover:dy-text-destructive hover:dy-bg-destructive/10" onClick={() => remove(index)}>
99
+ <X className="dy-w-3.5 dy-h-3.5" />
100
100
  </Button>
101
101
  </div>
102
102
  </div>
103
103
 
104
104
  {/* Content */}
105
105
  {isExpanded && (
106
- <div className="space-y-6">
106
+ <div className="dy-space-y-6">
107
107
  {blockConfig.fields.map(subField => (
108
108
  <FormFieldRenderer
109
109
  key={subField.name}
@@ -148,26 +148,26 @@ export function BlockBuilder({ schema, basePath, control, collection }: BlockBui
148
148
  }
149
149
 
150
150
  return (
151
- <div className="space-y-4">
152
- <div className="flex justify-between items-center pb-2">
151
+ <div className="dy-space-y-4">
152
+ <div className="dy-flex dy-justify-between dy-items-center dy-pb-2">
153
153
  <div>
154
- <h4 className="font-bold text-sm text-foreground tracking-tight">{schema.label}</h4>
154
+ <h4 className="dy-font-bold dy-text-sm dy-text-foreground dy-tracking-tight">{schema.label}</h4>
155
155
  {schema.admin?.description && (
156
- <p className="text-[11px] text-muted-foreground/60 italic">{schema.admin.description}</p>
156
+ <p className="dy-text-[11px] dy-text-muted-foreground/60 dy-italic">{schema.admin.description}</p>
157
157
  )}
158
158
  </div>
159
159
 
160
160
  {schema.blocks && schema.blocks.length > 0 && (
161
161
  <DropdownMenu>
162
162
  <DropdownMenuTrigger asChild>
163
- <Button type="button" variant="outline" size="sm" className="h-7 text-[11px] rounded-md border-primary/20 hover:bg-primary/5 hover:text-primary">
163
+ <Button type="button" variant="outline" size="sm" className="dy-h-7 dy-text-[11px] dy-rounded-md dy-border-primary/20 hover:dy-bg-primary/5 hover:dy-text-primary">
164
164
  Add Block
165
- <ChevronDown className="w-3 h-3 ml-1.5" />
165
+ <ChevronDown className="dy-w-3 dy-h-3 dy-ml-1.5" />
166
166
  </Button>
167
167
  </DropdownMenuTrigger>
168
- <DropdownMenuContent align="end" className="rounded-lg border-border/40 shadow-xl">
168
+ <DropdownMenuContent align="end" className="dy-rounded-lg dy-border-border/40 dy-shadow-xl">
169
169
  {schema.blocks.map((block) => (
170
- <DropdownMenuItem key={block.slug} onClick={() => handleAddBlock(block)} className="text-[13px] rounded-md focus:bg-primary/5 focus:text-primary transition-colors">
170
+ <DropdownMenuItem key={block.slug} onClick={() => handleAddBlock(block)} className="dy-text-[13px] dy-rounded-md focus:dy-bg-primary/5 focus:dy-text-primary dy-transition-colors">
171
171
  {block.labels?.singular || block.slug}
172
172
  </DropdownMenuItem>
173
173
  ))}
@@ -177,8 +177,8 @@ export function BlockBuilder({ schema, basePath, control, collection }: BlockBui
177
177
  </div>
178
178
 
179
179
  {fields.length === 0 ? (
180
- <div className="text-center p-8 border border-dashed border-border/40 rounded-md">
181
- <p className="text-[11px] text-muted-foreground/50">No blocks added yet.</p>
180
+ <div className="dy-text-center dy-p-8 dy-border dy-border-dashed dy-border-border/40 dy-rounded-md">
181
+ <p className="dy-text-[11px] dy-text-muted-foreground/50">No blocks added yet.</p>
182
182
  </div>
183
183
  ) : (
184
184
  <DndContext
@@ -190,7 +190,7 @@ export function BlockBuilder({ schema, basePath, control, collection }: BlockBui
190
190
  items={fields.map(f => f.id)}
191
191
  strategy={verticalListSortingStrategy}
192
192
  >
193
- <div className="pt-2">
193
+ <div className="dy-pt-2">
194
194
  {fields.map((item, index) => (
195
195
  <SortableBlockItem
196
196
  key={item.id}
@@ -21,25 +21,25 @@ export function DatePicker({ value, onChange, label, disabled }: DatePickerProps
21
21
  const date = value ? new Date(value) : undefined
22
22
 
23
23
  return (
24
- <div className="flex flex-col gap-2">
25
- {label && <label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">{label}</label>}
24
+ <div className="dy-flex dy-flex-col dy-gap-2">
25
+ {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>}
26
26
  <Popover open={disabled ? false : undefined}>
27
27
  <PopoverTrigger asChild>
28
28
  <Button
29
29
  variant={"outline"}
30
30
  disabled={disabled}
31
31
  className={cn(
32
- "w-full justify-start text-left font-normal h-11 px-4 bg-white hover:bg-muted/50 border-border/60 shadow-sm transition-all hover:shadow-md",
33
- !date && "text-muted-foreground"
32
+ "dy-w-full dy-justify-start dy-text-left dy-font-normal dy-h-11 dy-px-4 dy-bg-white hover:dy-bg-muted/50 dy-border-border/60 dy-shadow-sm dy-transition-all hover:dy-shadow-md",
33
+ !date && "dy-text-muted-foreground"
34
34
  )}
35
35
  >
36
- <CalendarIcon className="mr-3 h-4 w-4 text-primary" />
37
- <span className="flex-1 truncate">
36
+ <CalendarIcon className="dy-mr-3 dy-h-4 dy-w-4 dy-text-primary" />
37
+ <span className="dy-flex-1 dy-truncate">
38
38
  {date ? format(date, "PPP") : "Pick a date..."}
39
39
  </span>
40
40
  </Button>
41
41
  </PopoverTrigger>
42
- <PopoverContent className="w-auto p-0 border-border/50 shadow-2xl rounded-xl" align="start">
42
+ <PopoverContent className="dy-w-auto dy-p-0 dy-border-border/50 dy-shadow-2xl dy-rounded-xl" align="start">
43
43
  <Calendar
44
44
  mode="single"
45
45
  selected={date}
@@ -44,19 +44,19 @@ export function JsonEditor({ value, onChange, label, disabled }: JsonEditorProps
44
44
  }
45
45
 
46
46
  return (
47
- <div className="flex flex-col gap-2">
48
- {label && <label className="text-sm font-medium leading-none">{label}</label>}
47
+ <div className="dy-flex dy-flex-col dy-gap-2">
48
+ {label && <label className="dy-text-sm dy-font-medium dy-leading-none">{label}</label>}
49
49
  <Textarea
50
50
  value={internalValue}
51
51
  onChange={handleChange}
52
52
  disabled={disabled}
53
53
  className={cn(
54
- "font-mono text-xs min-h-[150px]",
55
- error && "border-destructive focus-visible:ring-destructive"
54
+ "dy-font-mono dy-text-xs dy-min-h-[150px]",
55
+ error && "dy-border-destructive focus-visible:dy-ring-destructive"
56
56
  )}
57
57
  placeholder='{ "key": "value" }'
58
58
  />
59
- {error && <span className="text-xs text-destructive">{error}</span>}
59
+ {error && <span className="dy-text-xs dy-text-destructive">{error}</span>}
60
60
  </div>
61
61
  )
62
62
  }