@linktr.ee/linkapp 0.0.48 → 0.0.49

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 (203) hide show
  1. package/README.md +1 -1
  2. package/dev-server/components/form/array-field.tsx +115 -0
  3. package/dev-server/components/form/file-field.tsx +48 -0
  4. package/dev-server/components/form/form-element.tsx +304 -0
  5. package/dev-server/components/form/link-behavior-field.tsx +68 -0
  6. package/dev-server/components/form/location-field.tsx +60 -0
  7. package/dev-server/components/settings-preview.tsx +138 -302
  8. package/dev-server/components/ui/checkbox.tsx +29 -0
  9. package/dev-server/components/ui/dialog.tsx +2 -10
  10. package/dev-server/components/ui/field.tsx +24 -49
  11. package/dev-server/components/ui/input.tsx +20 -21
  12. package/dev-server/components/ui/label.tsx +4 -4
  13. package/dev-server/components/ui/radio-group.tsx +37 -0
  14. package/dev-server/components/ui/select.tsx +153 -0
  15. package/dev-server/components/ui/switch.tsx +31 -0
  16. package/dev-server/components/ui/tabs.tsx +1 -1
  17. package/dev-server/components/ui/textarea.tsx +18 -19
  18. package/dev-server/env.d.ts +4 -1
  19. package/dev-server/expanded/main.tsx +20 -22
  20. package/dev-server/expanded.html +0 -1
  21. package/dev-server/featured/main.tsx +29 -36
  22. package/dev-server/featured-carousel.html +0 -1
  23. package/dev-server/featured.html +0 -1
  24. package/dev-server/index.html +1 -7
  25. package/dev-server/lib/utils.ts +3 -3
  26. package/dev-server/package.json +3 -3
  27. package/dev-server/postcss/tailwind-source-fallback.js +2 -7
  28. package/dev-server/postcss.config.mjs +2 -2
  29. package/dev-server/preview/Preview.tsx +310 -350
  30. package/dev-server/preview/main.tsx +8 -8
  31. package/dev-server/preview/preview.css +0 -1
  32. package/dev-server/public/site.webmanifest +1 -1
  33. package/dev-server/rsbuild.config.ts +1 -1
  34. package/dev-server/shared/dev-parent-simulator.ts +219 -0
  35. package/dev-server/shared/theme-presets.ts +71 -75
  36. package/dev-server/shared/theme-utils.ts +11 -11
  37. package/dist/cli.js +18 -12
  38. package/dist/cli.js.map +1 -1
  39. package/dist/commands/add.d.ts.map +1 -1
  40. package/dist/commands/add.js +27 -42
  41. package/dist/commands/add.js.map +1 -1
  42. package/dist/commands/build.d.ts.map +1 -1
  43. package/dist/commands/build.js +26 -16
  44. package/dist/commands/build.js.map +1 -1
  45. package/dist/commands/deploy.d.ts +1 -11
  46. package/dist/commands/deploy.d.ts.map +1 -1
  47. package/dist/commands/deploy.js +3 -13
  48. package/dist/commands/deploy.js.map +1 -1
  49. package/dist/commands/dev.d.ts.map +1 -1
  50. package/dist/commands/dev.js +132 -388
  51. package/dist/commands/dev.js.map +1 -1
  52. package/dist/commands/login.d.ts.map +1 -1
  53. package/dist/commands/login.js +17 -29
  54. package/dist/commands/login.js.map +1 -1
  55. package/dist/commands/logout.d.ts.map +1 -1
  56. package/dist/commands/logout.js +6 -11
  57. package/dist/commands/logout.js.map +1 -1
  58. package/dist/commands/rollback.d.ts +10 -0
  59. package/dist/commands/rollback.d.ts.map +1 -0
  60. package/dist/commands/rollback.js +148 -0
  61. package/dist/commands/rollback.js.map +1 -0
  62. package/dist/commands/status.d.ts +8 -0
  63. package/dist/commands/status.d.ts.map +1 -0
  64. package/dist/commands/status.js +96 -0
  65. package/dist/commands/status.js.map +1 -0
  66. package/dist/commands/test-url-match-rules.d.ts.map +1 -1
  67. package/dist/commands/test-url-match-rules.js +20 -26
  68. package/dist/commands/test-url-match-rules.js.map +1 -1
  69. package/dist/lib/auth/device-flow.d.ts +1 -1
  70. package/dist/lib/auth/device-flow.d.ts.map +1 -1
  71. package/dist/lib/auth/device-flow.js +3 -3
  72. package/dist/lib/auth/device-flow.js.map +1 -1
  73. package/dist/lib/auth/token-storage.d.ts.map +1 -1
  74. package/dist/lib/auth/token-storage.js +14 -37
  75. package/dist/lib/auth/token-storage.js.map +1 -1
  76. package/dist/lib/build/detect-layouts.d.ts.map +1 -1
  77. package/dist/lib/build/detect-layouts.js +27 -13
  78. package/dist/lib/build/detect-layouts.js.map +1 -1
  79. package/dist/lib/config/load-config.d.ts.map +1 -1
  80. package/dist/lib/config/load-config.js +0 -2
  81. package/dist/lib/config/load-config.js.map +1 -1
  82. package/dist/lib/deploy/deploy-output.d.ts +2 -1
  83. package/dist/lib/deploy/deploy-output.d.ts.map +1 -1
  84. package/dist/lib/deploy/deploy-output.js +9 -1
  85. package/dist/lib/deploy/deploy-output.js.map +1 -1
  86. package/dist/lib/deploy/deploy-phases.d.ts +2 -0
  87. package/dist/lib/deploy/deploy-phases.d.ts.map +1 -1
  88. package/dist/lib/deploy/deploy-phases.js +9 -23
  89. package/dist/lib/deploy/deploy-phases.js.map +1 -1
  90. package/dist/lib/deploy/deploy-utils.d.ts +15 -7
  91. package/dist/lib/deploy/deploy-utils.d.ts.map +1 -1
  92. package/dist/lib/deploy/deploy-utils.js +49 -36
  93. package/dist/lib/deploy/deploy-utils.js.map +1 -1
  94. package/dist/lib/deploy/generate-manifest-files.d.ts.map +1 -1
  95. package/dist/lib/deploy/generate-manifest-files.js +13 -39
  96. package/dist/lib/deploy/generate-manifest-files.js.map +1 -1
  97. package/dist/lib/deploy/pack-project.d.ts.map +1 -1
  98. package/dist/lib/deploy/pack-project.js +34 -20
  99. package/dist/lib/deploy/pack-project.js.map +1 -1
  100. package/dist/lib/deploy/slot-manager.d.ts +54 -0
  101. package/dist/lib/deploy/slot-manager.d.ts.map +1 -0
  102. package/dist/lib/deploy/slot-manager.js +72 -0
  103. package/dist/lib/deploy/slot-manager.js.map +1 -0
  104. package/dist/lib/deploy/test-url-match-rules.d.ts +10 -2
  105. package/dist/lib/deploy/test-url-match-rules.d.ts.map +1 -1
  106. package/dist/lib/deploy/test-url-match-rules.js +1 -1
  107. package/dist/lib/deploy/test-url-match-rules.js.map +1 -1
  108. package/dist/lib/deploy/upload.d.ts +1 -0
  109. package/dist/lib/deploy/upload.d.ts.map +1 -1
  110. package/dist/lib/deploy/upload.js +15 -24
  111. package/dist/lib/deploy/upload.js.map +1 -1
  112. package/dist/lib/deploy/validation.d.ts.map +1 -1
  113. package/dist/lib/deploy/validation.js +43 -48
  114. package/dist/lib/deploy/validation.js.map +1 -1
  115. package/dist/lib/rsbuild/config-factory.d.ts.map +1 -1
  116. package/dist/lib/rsbuild/config-factory.js +10 -17
  117. package/dist/lib/rsbuild/config-factory.js.map +1 -1
  118. package/dist/lib/rsbuild/plugins/asset-versioning.d.ts.map +1 -1
  119. package/dist/lib/rsbuild/plugins/asset-versioning.js +4 -14
  120. package/dist/lib/rsbuild/plugins/asset-versioning.js.map +1 -1
  121. package/dist/lib/rsbuild/plugins/brotli-compression.d.ts.map +1 -1
  122. package/dist/lib/rsbuild/plugins/brotli-compression.js +4 -4
  123. package/dist/lib/rsbuild/plugins/brotli-compression.js.map +1 -1
  124. package/dist/lib/rsbuild/plugins/copy-public.d.ts.map +1 -1
  125. package/dist/lib/rsbuild/plugins/copy-public.js.map +1 -1
  126. package/dist/lib/rsbuild/postcss/tailwind-source-fallback.d.ts.map +1 -1
  127. package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js +1 -3
  128. package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js.map +1 -1
  129. package/dist/lib/utils/console.d.ts +8 -0
  130. package/dist/lib/utils/console.d.ts.map +1 -0
  131. package/dist/lib/utils/console.js +10 -0
  132. package/dist/lib/utils/console.js.map +1 -0
  133. package/dist/lib/utils/filesystem.d.ts +9 -0
  134. package/dist/lib/utils/filesystem.d.ts.map +1 -0
  135. package/dist/lib/utils/filesystem.js +30 -0
  136. package/dist/lib/utils/filesystem.js.map +1 -0
  137. package/dist/lib/utils/formatters.d.ts +8 -0
  138. package/dist/lib/utils/formatters.d.ts.map +1 -0
  139. package/dist/lib/utils/formatters.js +22 -0
  140. package/dist/lib/utils/formatters.js.map +1 -0
  141. package/dist/lib/utils/index.d.ts +7 -0
  142. package/dist/lib/utils/index.d.ts.map +1 -0
  143. package/dist/lib/utils/index.js +7 -0
  144. package/dist/lib/utils/index.js.map +1 -0
  145. package/dist/lib/utils/setup-runtime.d.ts.map +1 -1
  146. package/dist/lib/utils/setup-runtime.js +22 -63
  147. package/dist/lib/utils/setup-runtime.js.map +1 -1
  148. package/dist/schema/config.schema.d.ts +9 -48
  149. package/dist/schema/config.schema.d.ts.map +1 -1
  150. package/dist/schema/config.schema.js +119 -120
  151. package/dist/schema/config.schema.js.map +1 -1
  152. package/dist/sdk/hooks/mocks.d.ts +9 -0
  153. package/dist/sdk/hooks/mocks.d.ts.map +1 -0
  154. package/dist/sdk/hooks/mocks.js +17 -0
  155. package/dist/sdk/hooks/mocks.js.map +1 -0
  156. package/dist/sdk/hooks/use-audience-manager.d.ts +44 -0
  157. package/dist/sdk/hooks/use-audience-manager.d.ts.map +1 -0
  158. package/dist/sdk/hooks/use-audience-manager.js +109 -0
  159. package/dist/sdk/hooks/use-audience-manager.js.map +1 -0
  160. package/dist/sdk/hooks/use-ip.d.ts +45 -0
  161. package/dist/sdk/hooks/use-ip.d.ts.map +1 -0
  162. package/dist/sdk/hooks/use-ip.js +46 -0
  163. package/dist/sdk/hooks/use-ip.js.map +1 -0
  164. package/dist/sdk/hooks/use-sdk-request.d.ts +46 -0
  165. package/dist/sdk/hooks/use-sdk-request.d.ts.map +1 -0
  166. package/dist/sdk/hooks/use-sdk-request.js +65 -0
  167. package/dist/sdk/hooks/use-sdk-request.js.map +1 -0
  168. package/dist/sdk/hooks/use-theme.d.ts +45 -0
  169. package/dist/sdk/hooks/use-theme.d.ts.map +1 -0
  170. package/dist/sdk/hooks/use-theme.js +97 -0
  171. package/dist/sdk/hooks/use-theme.js.map +1 -0
  172. package/dist/sdk/hooks/use-visitor.d.ts +41 -0
  173. package/dist/sdk/hooks/use-visitor.d.ts.map +1 -0
  174. package/dist/sdk/hooks/use-visitor.js +42 -0
  175. package/dist/sdk/hooks/use-visitor.js.map +1 -0
  176. package/dist/sdk/hooks/validation.d.ts +8 -0
  177. package/dist/sdk/hooks/validation.d.ts.map +1 -0
  178. package/dist/sdk/hooks/validation.js +13 -0
  179. package/dist/sdk/hooks/validation.js.map +1 -0
  180. package/dist/sdk/index.d.ts +17 -5
  181. package/dist/sdk/index.d.ts.map +1 -1
  182. package/dist/sdk/index.js +16 -5
  183. package/dist/sdk/index.js.map +1 -1
  184. package/dist/sdk/message-bus.d.ts +59 -0
  185. package/dist/sdk/message-bus.d.ts.map +1 -0
  186. package/dist/sdk/message-bus.js +152 -0
  187. package/dist/sdk/message-bus.js.map +1 -0
  188. package/dist/sdk/messages.d.ts +121 -0
  189. package/dist/sdk/messages.d.ts.map +1 -0
  190. package/dist/sdk/messages.js +9 -0
  191. package/dist/sdk/messages.js.map +1 -0
  192. package/dist/sdk/send-message.d.ts +1 -1
  193. package/dist/sdk/send-message.js +18 -18
  194. package/dist/sdk/send-message.js.map +1 -1
  195. package/dist/sdk/use-expand-link-app.d.ts +3 -3
  196. package/dist/sdk/use-expand-link-app.d.ts.map +1 -1
  197. package/dist/sdk/use-expand-link-app.js +9 -5
  198. package/dist/sdk/use-expand-link-app.js.map +1 -1
  199. package/dist/types.d.ts +235 -55
  200. package/dist/types.d.ts.map +1 -1
  201. package/dist/types.js +8 -3
  202. package/dist/types.js.map +1 -1
  203. package/package.json +3 -9
@@ -1,330 +1,166 @@
1
- import { LinkAppSettings, SettingsElement } from "../../src/types";
1
+ import { Settings } from 'lucide-react'
2
+ import { useMemo, useState } from 'react'
3
+
4
+ import type { LinkAppManifest, LinkAppSettings, SettingsElement } from '../../src/types'
5
+ import { FormElement } from './form/form-element'
2
6
 
3
7
  interface SettingsPreviewProps {
4
- settings: LinkAppSettings;
8
+ settings: LinkAppSettings
9
+ manifest?: Partial<LinkAppManifest>
10
+ previewProps?: Record<string, unknown>
5
11
  }
6
12
 
7
- export function SettingsPreview({ settings }: SettingsPreviewProps) {
8
- return (
9
- <div className="max-w-4xl mx-auto p-6 space-y-6">
10
- {/* Header */}
11
- <div className="space-y-2">
12
- <h2 className="text-2xl font-bold">{settings.title}</h2>
13
- {settings.overview && (
14
- <div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
15
- {settings.overview.title && (
16
- <h3 className="font-semibold text-blue-900 mb-1">
17
- {settings.overview.title}
18
- </h3>
19
- )}
20
- <p className="text-blue-800 text-sm">
21
- {settings.overview.description}
22
- </p>
23
- </div>
24
- )}
25
- </div>
13
+ export function SettingsPreview({ settings, manifest, previewProps = {} }: SettingsPreviewProps) {
14
+ const [iconError, setIconError] = useState(false)
26
15
 
27
- {/* Metadata */}
28
- <div className="grid grid-cols-2 gap-4 bg-gray-50 border border-gray-200 rounded-lg p-4">
29
- <div>
30
- <span className="text-sm font-medium text-gray-600">Uses URL:</span>
31
- <span className="ml-2 text-sm">
32
- {settings.uses_url || settings.has_url ? "Yes" : "No"}
33
- </span>
34
- </div>
35
- <div>
36
- <span className="text-sm font-medium text-gray-600">
37
- Supports Featured Layout:
38
- </span>
39
- <span className="ml-2 text-sm">
40
- {settings.supports_featured_layout ? "Yes" : "No"}
41
- </span>
42
- </div>
43
- {settings.icon && (
44
- <div>
45
- <span className="text-sm font-medium text-gray-600">Icon:</span>
46
- <span className="ml-2 text-sm">{settings.icon}</span>
47
- </div>
48
- )}
49
- {settings.settings_tab_title && (
50
- <div>
51
- <span className="text-sm font-medium text-gray-600">
52
- Settings Tab Title:
53
- </span>
54
- <span className="ml-2 text-sm">{settings.settings_tab_title}</span>
55
- </div>
56
- )}
57
- </div>
16
+ // Build values map from preview_props merged with defaultValues
17
+ const values = useMemo(() => {
18
+ const result: Record<string, unknown> = {}
58
19
 
59
- {/* Setup Instructions */}
60
- {settings.setup_instructions && (
61
- <div className="bg-green-50 border border-green-200 rounded-lg p-4">
62
- {settings.setup_instructions.title && (
63
- <h3 className="font-semibold text-green-900 mb-1">
64
- {settings.setup_instructions.title}
65
- </h3>
66
- )}
67
- <p className="text-green-800 text-sm">
68
- {settings.setup_instructions.description}
69
- </p>
70
- </div>
71
- )}
20
+ // First, set default values from elements
21
+ for (const element of settings.elements) {
22
+ if (element.defaultValue !== undefined) {
23
+ result[element.id] = element.defaultValue
24
+ }
25
+ }
72
26
 
73
- {/* Settings Elements */}
74
- <div className="space-y-4">
75
- <h3 className="text-xl font-semibold">Settings Elements</h3>
76
- {settings.elements.length === 0 ? (
77
- <p className="text-gray-500 italic">No settings elements defined</p>
78
- ) : (
79
- <div className="space-y-4">
80
- {settings.elements.map((element, index) => (
81
- <SettingsElementCard key={element.id} element={element} index={index} />
82
- ))}
83
- </div>
84
- )}
85
- </div>
86
- </div>
87
- );
88
- }
27
+ // Then, override with preview props
28
+ return { ...result, ...previewProps }
29
+ }, [settings.elements, previewProps])
89
30
 
90
- interface SettingsElementCardProps {
91
- element: SettingsElement;
92
- index: number;
93
- }
31
+ // Filter elements based on conditional display
32
+ const visibleElements = useMemo(() => {
33
+ return settings.elements.filter((element) => shouldDisplayElement(element, values))
34
+ }, [settings.elements, values])
94
35
 
95
- function SettingsElementCard({ element, index }: SettingsElementCardProps) {
96
36
  return (
97
- <div className="border border-gray-300 rounded-lg p-4 bg-white shadow-sm">
98
- <div className="flex items-start justify-between mb-3">
99
- <div className="flex-1">
100
- <div className="flex items-center gap-2">
101
- <span className="text-xs font-mono bg-gray-100 px-2 py-1 rounded">
102
- {index + 1}
103
- </span>
104
- <h4 className="font-semibold text-lg">{element.title || element.label}</h4>
105
- </div>
106
- <p className="text-sm text-gray-600 mt-1">
107
- <span className="font-mono text-xs bg-blue-100 text-blue-800 px-1.5 py-0.5 rounded">
108
- {element.id}
109
- </span>
110
- </p>
111
- </div>
112
- <span className="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-800">
113
- {element.inputType}
114
- </span>
115
- </div>
116
-
117
- {element.description && (
118
- <p className="text-sm text-gray-700 mb-3">{element.description}</p>
119
- )}
120
-
121
- <div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
122
- {element.placeholder && (
123
- <div>
124
- <span className="font-medium text-gray-600">Placeholder:</span>
125
- <span className="ml-2 text-gray-800">{element.placeholder}</span>
126
- </div>
127
- )}
128
-
129
- {element.defaultValue !== undefined && (
37
+ <div className="min-h-screen bg-gray-50">
38
+ <div className="max-w-2xl mx-auto p-6">
39
+ {/* Header */}
40
+ <div className="mb-6 flex items-start gap-3">
41
+ {!iconError && (
42
+ <img src="/app-icon.svg" alt="" className="h-8 w-8 shrink-0" onError={() => setIconError(true)} />
43
+ )}
130
44
  <div>
131
- <span className="font-medium text-gray-600">Default Value:</span>
132
- <span className="ml-2 text-gray-800 font-mono text-xs">
133
- {typeof element.defaultValue === "boolean"
134
- ? element.defaultValue.toString()
135
- : Array.isArray(element.defaultValue)
136
- ? `[${element.defaultValue.join(", ")}]`
137
- : element.defaultValue}
138
- </span>
45
+ <h1 className="text-xl font-semibold text-gray-900">{manifest?.name || settings.title}</h1>
46
+ {(manifest?.tagline || settings.overview?.description) && (
47
+ <p className="mt-1 text-sm text-gray-500">{manifest?.tagline || settings.overview?.description}</p>
48
+ )}
139
49
  </div>
140
- )}
50
+ </div>
141
51
 
142
- {element.validation && (
143
- <div className="col-span-2">
144
- <span className="font-medium text-gray-600">Validation:</span>
145
- <div className="ml-2 mt-1 space-y-1">
146
- {element.validation.required && (
147
- <div className="text-xs">
148
- <span className="bg-red-100 text-red-800 px-2 py-0.5 rounded">
149
- Required
150
- </span>
151
- </div>
152
- )}
153
- {element.validation.minLength !== undefined && (
154
- <div className="text-xs text-gray-600">
155
- Min Length: {element.validation.minLength}
156
- </div>
157
- )}
158
- {element.validation.maxLength !== undefined && (
159
- <div className="text-xs text-gray-600">
160
- Max Length: {element.validation.maxLength}
161
- </div>
162
- )}
163
- {element.validation.min !== undefined && (
164
- <div className="text-xs text-gray-600">
165
- Min: {element.validation.min}
166
- </div>
167
- )}
168
- {element.validation.max !== undefined && (
169
- <div className="text-xs text-gray-600">
170
- Max: {element.validation.max}
171
- </div>
172
- )}
173
- {element.validation.maxSize !== undefined && (
174
- <div className="text-xs text-gray-600">
175
- Max Size: {element.validation.maxSize}
176
- </div>
177
- )}
178
- {element.validation.pattern && (
179
- <div className="text-xs text-gray-600 font-mono">
180
- Pattern: {element.validation.pattern}
181
- </div>
182
- )}
52
+ {/* Form Card */}
53
+ <div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
54
+ {/* Form Header */}
55
+ <div className="px-6 py-4 border-b border-gray-100 bg-gray-50/50">
56
+ <div className="flex items-center justify-between">
57
+ <h2 className="font-semibold text-gray-900">Settings</h2>
58
+ <span className="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-500">
59
+ Preview Mode
60
+ </span>
183
61
  </div>
184
62
  </div>
185
- )}
186
63
 
187
- {element.options && element.options.length > 0 && (
188
- <div className="col-span-2">
189
- <span className="font-medium text-gray-600">Options:</span>
190
- <div className="ml-2 mt-1 space-y-1">
191
- {element.options.map((option) => (
192
- <div key={option.value} className="text-xs bg-gray-50 px-2 py-1 rounded">
193
- <span className="font-mono text-blue-700">{option.value}</span>
194
- <span className="mx-1 text-gray-400">→</span>
195
- <span>{option.label}</span>
64
+ {/* Form Content */}
65
+ <div className="p-6">
66
+ {visibleElements.length === 0 ? (
67
+ <div className="text-center py-12">
68
+ <div className="w-12 h-12 mx-auto mb-4 rounded-full bg-gray-100 flex items-center justify-center">
69
+ <Settings className="h-6 w-6 text-gray-400" />
196
70
  </div>
197
- ))}
198
- </div>
199
- </div>
200
- )}
201
-
202
- {element.linkBehaviorLabels && (
203
- <div className="col-span-2">
204
- <span className="font-medium text-gray-600">Link Behavior Labels:</span>
205
- <div className="ml-2 mt-1 space-y-1">
206
- <div className="text-xs">
207
- <span className="font-medium">Embed:</span> {element.linkBehaviorLabels.embedLabel}
71
+ <p className="text-sm text-gray-500 font-medium">No settings defined</p>
72
+ <p className="text-xs text-gray-400 mt-1">Add elements to your config to preview the form</p>
208
73
  </div>
209
- <div className="text-xs">
210
- <span className="font-medium">Link Off:</span> {element.linkBehaviorLabels.linkOffLabel}
74
+ ) : (
75
+ <div className="space-y-6">
76
+ {visibleElements.map((element) => (
77
+ <FormElement key={element.id} element={element} value={values[element.id]} />
78
+ ))}
211
79
  </div>
212
- </div>
213
- </div>
214
- )}
215
-
216
- {element.accept && element.accept.length > 0 && (
217
- <div className="col-span-2">
218
- <span className="font-medium text-gray-600">Accepted Files:</span>
219
- <span className="ml-2 text-xs font-mono">
220
- {element.accept.join(", ")}
221
- </span>
222
- </div>
223
- )}
224
-
225
- {element.multiple && (
226
- <div>
227
- <span className="bg-orange-100 text-orange-800 px-2 py-0.5 rounded text-xs">
228
- Multiple values allowed
229
- </span>
230
- </div>
231
- )}
232
-
233
- {element.capability && (
234
- <div>
235
- <span className="font-medium text-gray-600">Integration Capability:</span>
236
- <span className="ml-2 text-xs">{element.capability}</span>
237
- </div>
238
- )}
239
-
240
- {element.vendor && (
241
- <div>
242
- <span className="font-medium text-gray-600">Vendor:</span>
243
- <span className="ml-2 text-xs">{element.vendor}</span>
80
+ )}
244
81
  </div>
245
- )}
82
+ </div>
246
83
 
247
- {element.array_options && (
248
- <div className="col-span-2">
249
- <span className="font-medium text-gray-600">Array Options:</span>
250
- <div className="ml-2 mt-1 space-y-1 text-xs">
251
- {element.array_options.add_item_button_text && (
252
- <div>Add Button: "{element.array_options.add_item_button_text}"</div>
253
- )}
254
- {element.array_options.add_item_title && (
255
- <div>Add Title: "{element.array_options.add_item_title}"</div>
256
- )}
257
- {element.array_options.edit_item_title && (
258
- <div>Edit Title: "{element.array_options.edit_item_title}"</div>
259
- )}
260
- {element.array_options.item_format && (
261
- <div>Item Format: "{element.array_options.item_format}"</div>
262
- )}
263
- {element.array_options.min !== undefined && (
264
- <div>Min Items: {element.array_options.min}</div>
265
- )}
266
- {element.array_options.max !== undefined && (
267
- <div>Max Items: {element.array_options.max}</div>
268
- )}
269
- </div>
270
- </div>
271
- )}
84
+ {/* Metadata Card */}
85
+ <MetadataSummary settings={settings} />
86
+ </div>
87
+ </div>
88
+ )
89
+ }
272
90
 
273
- {element.array_elements && element.array_elements.length > 0 && (
274
- <div className="col-span-2">
275
- <span className="font-medium text-gray-600">Array Elements:</span>
276
- <div className="ml-2 mt-2 space-y-2">
277
- {element.array_elements.map((arrayElement, arrayIndex) => (
278
- <div key={arrayElement.id} className="bg-gray-50 border border-gray-200 rounded p-3">
279
- <div className="text-xs font-mono text-blue-700 mb-1">
280
- {arrayElement.id}
281
- </div>
282
- <div className="text-xs">
283
- <span className="font-medium">Type:</span> {arrayElement.inputType}
284
- </div>
285
- {arrayElement.label && (
286
- <div className="text-xs">
287
- <span className="font-medium">Label:</span> {arrayElement.label}
288
- </div>
289
- )}
290
- {arrayElement.placeholder && (
291
- <div className="text-xs">
292
- <span className="font-medium">Placeholder:</span> {arrayElement.placeholder}
293
- </div>
294
- )}
295
- </div>
296
- ))}
297
- </div>
298
- </div>
299
- )}
91
+ interface MetadataSummaryProps {
92
+ settings: LinkAppSettings
93
+ }
300
94
 
301
- {element.conditionalDisplay && (
302
- <div className="col-span-2">
303
- <span className="font-medium text-gray-600">Conditional Display:</span>
304
- <div className="ml-2 mt-1 text-xs bg-yellow-50 border border-yellow-200 rounded p-2">
305
- Depends on <span className="font-mono">{element.conditionalDisplay.dependsOn}</span>
306
- {" "}
307
- {element.conditionalDisplay.operator || "equals"}
308
- {" "}
309
- <span className="font-mono">{String(element.conditionalDisplay.value)}</span>
310
- </div>
311
- </div>
312
- )}
95
+ function MetadataSummary({ settings }: MetadataSummaryProps) {
96
+ const metadata = [
97
+ {
98
+ label: 'Uses URL',
99
+ value: settings.uses_url || settings.has_url ? 'Yes' : 'No',
100
+ },
101
+ settings.featured_chin_position && {
102
+ label: 'Chin Position',
103
+ value: settings.featured_chin_position,
104
+ },
105
+ settings.sheet_behavior && {
106
+ label: 'Sheet Behavior',
107
+ value: settings.sheet_behavior,
108
+ },
109
+ settings.featured_head_click_behavior && {
110
+ label: 'Click Behavior',
111
+ value: settings.featured_head_click_behavior,
112
+ },
113
+ settings.icon && { label: 'Icon', value: settings.icon },
114
+ ].filter(Boolean) as Array<{ label: string; value: string }>
115
+
116
+ if (metadata.length === 0) return null
313
117
 
314
- {element.action && (
315
- <div className="col-span-2">
316
- <span className="font-medium text-gray-600">Action:</span>
317
- <div className="ml-2 mt-1 text-xs">
318
- On {element.action.on}: {element.action.type}
319
- {element.action.data && (
320
- <div className="font-mono text-xs mt-1 bg-gray-50 p-1 rounded">
321
- {JSON.stringify(element.action.data, null, 2)}
322
- </div>
323
- )}
118
+ return (
119
+ <div className="mt-6 bg-white rounded-xl border border-gray-200 overflow-hidden">
120
+ <div className="px-6 py-4 border-b border-gray-100 bg-gray-50/50">
121
+ <h3 className="font-semibold text-gray-900 text-sm">Configuration</h3>
122
+ </div>
123
+ <div className="p-6">
124
+ <div className="grid grid-cols-2 gap-4">
125
+ {metadata.map(({ label, value }) => (
126
+ <div key={label} className="flex items-center justify-between py-2 px-3 rounded-lg bg-gray-50">
127
+ <span className="text-sm text-gray-500">{label}</span>
128
+ <span className="text-sm font-medium text-gray-900">{value}</span>
324
129
  </div>
325
- </div>
326
- )}
130
+ ))}
131
+ </div>
327
132
  </div>
328
133
  </div>
329
- );
134
+ )
135
+ }
136
+
137
+ /**
138
+ * Evaluates conditional display rules to determine if an element should be shown
139
+ */
140
+ function shouldDisplayElement(element: SettingsElement, values: Record<string, unknown>): boolean {
141
+ // New format (preferred): conditional with fieldId and values array
142
+ if (element.conditional) {
143
+ const { fieldId, values: allowedValues } = element.conditional
144
+ const currentValue = values[fieldId]
145
+ return allowedValues.some((v) => v === currentValue)
146
+ }
147
+
148
+ // Legacy format (deprecated): conditionalDisplay with dependsOn, value, operator
149
+ if (element.conditionalDisplay) {
150
+ const { dependsOn, value, operator = 'equals' } = element.conditionalDisplay
151
+ const currentValue = values[dependsOn]
152
+
153
+ switch (operator) {
154
+ case 'equals':
155
+ return currentValue === value
156
+ case 'notEquals':
157
+ return currentValue !== value
158
+ case 'contains':
159
+ return typeof currentValue === 'string' && typeof value === 'string' && currentValue.includes(value)
160
+ default:
161
+ return true
162
+ }
163
+ }
164
+
165
+ return true
330
166
  }
@@ -0,0 +1,29 @@
1
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
2
+ import { Check } from 'lucide-react'
3
+ import * as React from 'react'
4
+
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const Checkbox = React.forwardRef<
8
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
10
+ >(({ className, ...props }, ref) => (
11
+ <CheckboxPrimitive.Root
12
+ ref={ref}
13
+ className={cn(
14
+ 'peer h-4 w-4 shrink-0 rounded-sm border border-gray-300 shadow-sm',
15
+ 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-blue-500',
16
+ 'disabled:cursor-not-allowed disabled:opacity-50',
17
+ 'data-[state=checked]:bg-blue-500 data-[state=checked]:border-blue-500 data-[state=checked]:text-white',
18
+ className
19
+ )}
20
+ {...props}
21
+ >
22
+ <CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
23
+ <Check className="h-3.5 w-3.5" strokeWidth={3} />
24
+ </CheckboxPrimitive.Indicator>
25
+ </CheckboxPrimitive.Root>
26
+ ))
27
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName
28
+
29
+ export { Checkbox }
@@ -50,9 +50,7 @@ function DialogContent({
50
50
  )}
51
51
  {...props}
52
52
  >
53
- <div className="flex h-full flex-col">
54
- {children}
55
- </div>
53
+ <div className="flex h-full flex-col">{children}</div>
56
54
 
57
55
  {showCloseButton && (
58
56
  <DialogPrimitive.Close
@@ -69,13 +67,7 @@ function DialogContent({
69
67
  }
70
68
 
71
69
  function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
72
- return (
73
- <div
74
- data-slot="dialog-header"
75
- className={cn(className)}
76
- {...props}
77
- />
78
- )
70
+ return <div data-slot="dialog-header" className={cn(className)} {...props} />
79
71
  }
80
72
 
81
73
  function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
@@ -1,62 +1,37 @@
1
- import * as React from "react"
1
+ import * as React from 'react'
2
2
 
3
- import { cn } from "../../lib/utils"
4
- import { Label } from "./label"
3
+ import { cn } from '../../lib/utils'
4
+ import { Label } from './label'
5
5
 
6
6
  interface FieldProps extends React.HTMLAttributes<HTMLDivElement> {
7
- orientation?: "vertical" | "horizontal"
7
+ orientation?: 'vertical' | 'horizontal'
8
8
  }
9
9
 
10
- const Field = React.forwardRef<HTMLDivElement, FieldProps>(
11
- ({ className, orientation = "vertical", ...props }, ref) => (
12
- <div
13
- ref={ref}
14
- role="group"
15
- className={cn(
16
- "space-y-2",
17
- orientation === "horizontal" && "flex items-start gap-3 space-y-0",
18
- className
19
- )}
20
- {...props}
21
- />
22
- )
23
- )
24
- Field.displayName = "Field"
25
-
26
- const FieldLabel = React.forwardRef<
27
- React.ElementRef<typeof Label>,
28
- React.ComponentPropsWithoutRef<typeof Label>
29
- >(({ className, ...props }, ref) => (
30
- <Label
10
+ const Field = React.forwardRef<HTMLDivElement, FieldProps>(({ className, orientation = 'vertical', ...props }, ref) => (
11
+ <div
31
12
  ref={ref}
32
- className={cn("text-sm font-medium text-gray-900", className)}
13
+ role="group"
14
+ className={cn('space-y-2', orientation === 'horizontal' && 'flex items-start gap-3 space-y-0', className)}
33
15
  {...props}
34
16
  />
35
17
  ))
36
- FieldLabel.displayName = "FieldLabel"
18
+ Field.displayName = 'Field'
37
19
 
38
- const FieldDescription = React.forwardRef<
39
- HTMLParagraphElement,
40
- React.HTMLAttributes<HTMLParagraphElement>
41
- >(({ className, ...props }, ref) => (
42
- <p
43
- ref={ref}
44
- className={cn("text-sm text-gray-500", className)}
45
- {...props}
46
- />
47
- ))
48
- FieldDescription.displayName = "FieldDescription"
20
+ const FieldLabel = React.forwardRef<React.ElementRef<typeof Label>, React.ComponentPropsWithoutRef<typeof Label>>(
21
+ ({ className, ...props }, ref) => (
22
+ <Label ref={ref} className={cn('text-sm font-medium text-gray-900', className)} {...props} />
23
+ )
24
+ )
25
+ FieldLabel.displayName = 'FieldLabel'
49
26
 
50
- const FieldError = React.forwardRef<
51
- HTMLParagraphElement,
52
- React.HTMLAttributes<HTMLParagraphElement>
53
- >(({ className, ...props }, ref) => (
54
- <p
55
- ref={ref}
56
- className={cn("text-sm text-red-500", className)}
57
- {...props}
58
- />
59
- ))
60
- FieldError.displayName = "FieldError"
27
+ const FieldDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
28
+ ({ className, ...props }, ref) => <p ref={ref} className={cn('text-sm text-gray-500', className)} {...props} />
29
+ )
30
+ FieldDescription.displayName = 'FieldDescription'
31
+
32
+ const FieldError = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
33
+ ({ className, ...props }, ref) => <p ref={ref} className={cn('text-sm text-red-500', className)} {...props} />
34
+ )
35
+ FieldError.displayName = 'FieldError'
61
36
 
62
37
  export { Field, FieldLabel, FieldDescription, FieldError }