@fayz-ai/plugin-forms 0.9.0 → 0.9.2
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 +3 -3
- package/dist/{FormBuilder-7SWZQKFN.js → FormBuilder-YHJHF6HZ.js} +4 -4
- package/dist/{FormBuilder-7SWZQKFN.js.map → FormBuilder-YHJHF6HZ.js.map} +1 -1
- package/dist/{chunk-W2ZNJGQC.js → chunk-RBLXIKWB.js} +221 -8
- package/dist/chunk-RBLXIKWB.js.map +1 -0
- package/dist/components/AddDocumentDropdown.d.ts +1 -1
- package/dist/components/AddDocumentDropdown.d.ts.map +1 -1
- package/dist/components/DocumentFormDialog.d.ts.map +1 -1
- package/dist/components/DocumentSkeleton.d.ts +21 -0
- package/dist/components/DocumentSkeleton.d.ts.map +1 -0
- package/dist/components/FormRenderer.d.ts.map +1 -1
- package/dist/components/FormViewer.d.ts.map +1 -1
- package/dist/{CustomFormsContext.d.ts → context.d.ts} +1 -1
- package/dist/context.d.ts.map +1 -0
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/types.d.ts +4 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -17
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-tools.d.ts.map +1 -0
- package/dist/lib/document-types.d.ts.map +1 -0
- package/dist/lib/html-text.d.ts +21 -0
- package/dist/lib/html-text.d.ts.map +1 -0
- package/dist/lib/print.d.ts.map +1 -1
- package/dist/lib/timeline-source.d.ts +10 -0
- package/dist/lib/timeline-source.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/store.d.ts +5 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/TemplateListView.d.ts.map +1 -1
- package/package.json +5 -7
- package/dist/CustomFormsContext.d.ts.map +0 -1
- package/dist/agent-tools.d.ts.map +0 -1
- package/dist/chunk-W2ZNJGQC.js.map +0 -1
- package/dist/document-types.d.ts.map +0 -1
- package/src/CustomFormsContext.tsx +0 -28
- package/src/agent-tools.ts +0 -73
- package/src/components/AddDocumentDropdown.tsx +0 -92
- package/src/components/BuilderCanvas.tsx +0 -87
- package/src/components/BuilderField.tsx +0 -81
- package/src/components/DocumentFormDialog.tsx +0 -155
- package/src/components/DocumentList.tsx +0 -254
- package/src/components/FieldConfigDialog.tsx +0 -243
- package/src/components/FieldPalette.tsx +0 -81
- package/src/components/FormBuilder.tsx +0 -427
- package/src/components/FormRenderer.tsx +0 -256
- package/src/components/FormViewer.tsx +0 -94
- package/src/components/PersonDocumentsWidget.tsx +0 -229
- package/src/components/TemplateSelector.tsx +0 -91
- package/src/config.ts +0 -43
- package/src/data/index.ts +0 -3
- package/src/data/mock.ts +0 -193
- package/src/data/supabase.ts +0 -408
- package/src/data/tables.ts +0 -7
- package/src/data/types.ts +0 -33
- package/src/document-types.ts +0 -51
- package/src/index.ts +0 -234
- package/src/lib/person-fields.ts +0 -67
- package/src/lib/print.ts +0 -207
- package/src/lib/tenant.ts +0 -9
- package/src/locales/en.ts +0 -95
- package/src/locales/index.ts +0 -7
- package/src/locales/pt-BR.ts +0 -95
- package/src/migrations/000_plg_rename.sql +0 -21
- package/src/migrations/001_frm_base.sql +0 -174
- package/src/migrations/002_document_archetype.sql +0 -223
- package/src/migrations/003_agent_rpcs.sql +0 -174
- package/src/migrations/index.ts +0 -610
- package/src/store.ts +0 -167
- package/src/types.ts +0 -217
- package/src/views/CustomFormsSettingsTab.tsx +0 -105
- package/src/views/TemplateListView.tsx +0 -174
- /package/dist/{agent-tools.d.ts → lib/agent-tools.d.ts} +0 -0
- /package/dist/{document-types.d.ts → lib/document-types.d.ts} +0 -0
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react'
|
|
2
|
-
import { Plus, Trash2 } from 'lucide-react'
|
|
3
|
-
import {
|
|
4
|
-
Sheet,
|
|
5
|
-
SheetContent,
|
|
6
|
-
SheetHeader,
|
|
7
|
-
SheetTitle,
|
|
8
|
-
SheetBody,
|
|
9
|
-
SheetFooter,
|
|
10
|
-
} from '@fayz-ai/ui'
|
|
11
|
-
import { Button } from '@fayz-ai/ui'
|
|
12
|
-
import { Input } from '@fayz-ai/ui'
|
|
13
|
-
import {
|
|
14
|
-
Select,
|
|
15
|
-
SelectContent,
|
|
16
|
-
SelectItem,
|
|
17
|
-
SelectTrigger,
|
|
18
|
-
SelectValue,
|
|
19
|
-
} from '@fayz-ai/ui'
|
|
20
|
-
import { useTranslation } from '@fayz-ai/core'
|
|
21
|
-
import type { FormFieldDef } from '../types'
|
|
22
|
-
import { FIELD_TYPES } from './FieldPalette'
|
|
23
|
-
import { PERSON_FIELD_OPTIONS } from '../lib/person-fields'
|
|
24
|
-
|
|
25
|
-
/** Field types that can be auto-filled from a person record. */
|
|
26
|
-
const MAPPABLE_TYPES = ['text', 'memo', 'richtext', 'date']
|
|
27
|
-
const MAP_NONE = '__none__'
|
|
28
|
-
|
|
29
|
-
const SIZE_OPTIONS = [
|
|
30
|
-
{ span: 3, label: '1/4' },
|
|
31
|
-
{ span: 4, label: '1/3' },
|
|
32
|
-
{ span: 6, label: '1/2' },
|
|
33
|
-
{ span: 8, label: '2/3' },
|
|
34
|
-
{ span: 9, label: '3/4' },
|
|
35
|
-
{ span: 12, label: 'Full' },
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
interface FieldConfigDrawerProps {
|
|
39
|
-
field: FormFieldDef | null
|
|
40
|
-
onSave: (updated: FormFieldDef) => void
|
|
41
|
-
/** Live update without closing — used for visual changes like colSpan */
|
|
42
|
-
onUpdate?: (updated: FormFieldDef) => void
|
|
43
|
-
onClose: () => void
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function FieldConfigDrawer({ field, onSave, onUpdate, onClose }: FieldConfigDrawerProps) {
|
|
47
|
-
const t = useTranslation()
|
|
48
|
-
const [label, setLabel] = useState('')
|
|
49
|
-
const [placeholder, setPlaceholder] = useState('')
|
|
50
|
-
const [required, setRequired] = useState(false)
|
|
51
|
-
const [colSpan, setColSpan] = useState(12)
|
|
52
|
-
const [options, setOptions] = useState<Array<{ label: string; value: string }>>([])
|
|
53
|
-
const [map, setMap] = useState<string>('')
|
|
54
|
-
|
|
55
|
-
// Sync state when field changes
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (!field) return
|
|
58
|
-
setLabel(field.label)
|
|
59
|
-
setPlaceholder(field.placeholder ?? '')
|
|
60
|
-
setRequired(field.required ?? false)
|
|
61
|
-
setColSpan(field.colSpan)
|
|
62
|
-
setOptions(field.options ?? [])
|
|
63
|
-
setMap(field.map ?? '')
|
|
64
|
-
}, [field?.id])
|
|
65
|
-
|
|
66
|
-
if (!field) return null
|
|
67
|
-
|
|
68
|
-
const hasOptions = ['select', 'radio', 'tags'].includes(field.type)
|
|
69
|
-
const canMap = MAPPABLE_TYPES.includes(field.type)
|
|
70
|
-
const fieldTypeDef = FIELD_TYPES.find((ft) => ft.type === field.type)
|
|
71
|
-
const Icon = fieldTypeDef?.icon
|
|
72
|
-
|
|
73
|
-
const handleSave = () => {
|
|
74
|
-
onSave({
|
|
75
|
-
...field,
|
|
76
|
-
label,
|
|
77
|
-
placeholder: placeholder || undefined,
|
|
78
|
-
required,
|
|
79
|
-
colSpan: Math.max(1, Math.min(12, colSpan)),
|
|
80
|
-
options: hasOptions ? options.filter((o) => o.label.trim()) : field.options,
|
|
81
|
-
map: canMap && map ? map : undefined,
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const addOption = () => {
|
|
86
|
-
setOptions([...options, { label: '', value: '' }])
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const updateOption = (index: number, newLabel: string) => {
|
|
90
|
-
const updated = [...options]
|
|
91
|
-
updated[index] = { label: newLabel, value: newLabel.toLowerCase().replace(/\s+/g, '_') }
|
|
92
|
-
setOptions(updated)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const removeOption = (index: number) => {
|
|
96
|
-
setOptions(options.filter((_, i) => i !== index))
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return (
|
|
100
|
-
<Sheet open={!!field} onOpenChange={(open) => { if (!open) onClose() }}>
|
|
101
|
-
<SheetContent className="w-80 sm:w-96" overlay="none">
|
|
102
|
-
<SheetHeader>
|
|
103
|
-
<SheetTitle className="flex items-center gap-2 text-sm">
|
|
104
|
-
{Icon && <Icon className="h-4 w-4 text-muted-foreground" />}
|
|
105
|
-
{t(`customForms.fieldType.${field.type}`)}
|
|
106
|
-
</SheetTitle>
|
|
107
|
-
</SheetHeader>
|
|
108
|
-
|
|
109
|
-
<SheetBody className="space-y-4 py-4">
|
|
110
|
-
{/* Label */}
|
|
111
|
-
<div className="space-y-1.5">
|
|
112
|
-
<label className="text-xs font-medium">{t('customForms.builder.fieldLabel')}</label>
|
|
113
|
-
<Input
|
|
114
|
-
value={label}
|
|
115
|
-
onChange={(e) => setLabel(e.target.value)}
|
|
116
|
-
className="h-9 text-sm"
|
|
117
|
-
autoFocus
|
|
118
|
-
/>
|
|
119
|
-
</div>
|
|
120
|
-
|
|
121
|
-
{/* Placeholder */}
|
|
122
|
-
{!['title', 'checkbox', 'image', 'gallery'].includes(field.type) && (
|
|
123
|
-
<div className="space-y-1.5">
|
|
124
|
-
<label className="text-xs font-medium">{t('customForms.builder.fieldPlaceholder')}</label>
|
|
125
|
-
<Input
|
|
126
|
-
value={placeholder}
|
|
127
|
-
onChange={(e) => setPlaceholder(e.target.value)}
|
|
128
|
-
className="h-9 text-sm"
|
|
129
|
-
/>
|
|
130
|
-
</div>
|
|
131
|
-
)}
|
|
132
|
-
|
|
133
|
-
{/* Column span — visual skeleton selector */}
|
|
134
|
-
<div className="space-y-2">
|
|
135
|
-
<label className="text-xs font-medium">{t('customForms.builder.colSpan')}</label>
|
|
136
|
-
<div className="grid grid-cols-3 gap-2">
|
|
137
|
-
{SIZE_OPTIONS.map((opt) => (
|
|
138
|
-
<button
|
|
139
|
-
key={opt.span}
|
|
140
|
-
onClick={() => {
|
|
141
|
-
setColSpan(opt.span)
|
|
142
|
-
if (onUpdate && field) {
|
|
143
|
-
onUpdate({ ...field, colSpan: opt.span })
|
|
144
|
-
}
|
|
145
|
-
}}
|
|
146
|
-
className={`group relative rounded-lg border-2 p-2 transition-all ${
|
|
147
|
-
colSpan === opt.span
|
|
148
|
-
? 'border-primary bg-primary/5'
|
|
149
|
-
: 'border-border hover:border-primary/30'
|
|
150
|
-
}`}
|
|
151
|
-
>
|
|
152
|
-
{/* Mini 12-col skeleton preview */}
|
|
153
|
-
<div className="flex gap-px h-3 mb-1.5">
|
|
154
|
-
<div
|
|
155
|
-
className={`rounded-sm ${colSpan === opt.span ? 'bg-primary/60' : 'bg-muted-foreground/30 group-hover:bg-primary/30'}`}
|
|
156
|
-
style={{ width: `${(opt.span / 12) * 100}%` }}
|
|
157
|
-
/>
|
|
158
|
-
{opt.span < 12 && (
|
|
159
|
-
<div
|
|
160
|
-
className="rounded-sm bg-muted/60"
|
|
161
|
-
style={{ width: `${((12 - opt.span) / 12) * 100}%` }}
|
|
162
|
-
/>
|
|
163
|
-
)}
|
|
164
|
-
</div>
|
|
165
|
-
<span className={`text-[10px] font-medium ${colSpan === opt.span ? 'text-primary' : 'text-muted-foreground'}`}>
|
|
166
|
-
{opt.label}
|
|
167
|
-
</span>
|
|
168
|
-
</button>
|
|
169
|
-
))}
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
172
|
-
|
|
173
|
-
{/* Required */}
|
|
174
|
-
{field.type !== 'title' && (
|
|
175
|
-
<label className="flex items-center gap-2 cursor-pointer">
|
|
176
|
-
<input
|
|
177
|
-
type="checkbox"
|
|
178
|
-
checked={required}
|
|
179
|
-
onChange={(e) => setRequired(e.target.checked)}
|
|
180
|
-
className="rounded border-input accent-primary"
|
|
181
|
-
/>
|
|
182
|
-
<span className="text-sm">{t('customForms.builder.fieldRequired')}</span>
|
|
183
|
-
</label>
|
|
184
|
-
)}
|
|
185
|
-
|
|
186
|
-
{/* Auto-fill from person record */}
|
|
187
|
-
{canMap && (
|
|
188
|
-
<div className="space-y-1.5">
|
|
189
|
-
<label className="text-xs font-medium">{t('customForms.builder.mapToPerson')}</label>
|
|
190
|
-
<Select
|
|
191
|
-
value={map || MAP_NONE}
|
|
192
|
-
onValueChange={(v) => setMap(v === MAP_NONE ? '' : v)}
|
|
193
|
-
>
|
|
194
|
-
<SelectTrigger className="h-9 text-sm">
|
|
195
|
-
<SelectValue />
|
|
196
|
-
</SelectTrigger>
|
|
197
|
-
<SelectContent>
|
|
198
|
-
<SelectItem value={MAP_NONE}>{t('customForms.builder.mapNone')}</SelectItem>
|
|
199
|
-
{PERSON_FIELD_OPTIONS.map((opt) => (
|
|
200
|
-
<SelectItem key={opt.value} value={opt.value}>
|
|
201
|
-
{t(opt.labelKey)}
|
|
202
|
-
</SelectItem>
|
|
203
|
-
))}
|
|
204
|
-
</SelectContent>
|
|
205
|
-
</Select>
|
|
206
|
-
<p className="text-[10px] text-muted-foreground">{t('customForms.builder.mapHint')}</p>
|
|
207
|
-
</div>
|
|
208
|
-
)}
|
|
209
|
-
|
|
210
|
-
{/* Options */}
|
|
211
|
-
{hasOptions && (
|
|
212
|
-
<div className="space-y-2">
|
|
213
|
-
<label className="text-xs font-medium">{t('customForms.builder.fieldOptions')}</label>
|
|
214
|
-
{options.map((opt, i) => (
|
|
215
|
-
<div key={i} className="flex items-center gap-2">
|
|
216
|
-
<Input
|
|
217
|
-
value={opt.label}
|
|
218
|
-
onChange={(e) => updateOption(i, e.target.value)}
|
|
219
|
-
placeholder={`Option ${i + 1}`}
|
|
220
|
-
className="h-8 text-sm flex-1"
|
|
221
|
-
/>
|
|
222
|
-
<button onClick={() => removeOption(i)} className="p-1 hover:bg-destructive/10 rounded">
|
|
223
|
-
<Trash2 className="h-3.5 w-3.5 text-destructive" />
|
|
224
|
-
</button>
|
|
225
|
-
</div>
|
|
226
|
-
))}
|
|
227
|
-
<Button variant="outline" size="sm" onClick={addOption} className="h-8 text-xs w-full">
|
|
228
|
-
<Plus className="h-3 w-3 mr-1" />
|
|
229
|
-
{t('customForms.builder.addOption')}
|
|
230
|
-
</Button>
|
|
231
|
-
</div>
|
|
232
|
-
)}
|
|
233
|
-
</SheetBody>
|
|
234
|
-
|
|
235
|
-
<SheetFooter className="pt-4 border-t">
|
|
236
|
-
<Button size="sm" onClick={handleSave} className="w-full">
|
|
237
|
-
OK
|
|
238
|
-
</Button>
|
|
239
|
-
</SheetFooter>
|
|
240
|
-
</SheetContent>
|
|
241
|
-
</Sheet>
|
|
242
|
-
)
|
|
243
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { useDraggable } from '@dnd-kit/core'
|
|
3
|
-
import {
|
|
4
|
-
Heading, Type, AlignLeft, FileEdit, CalendarDays,
|
|
5
|
-
ChevronDown, CircleDot, Tags, CheckSquare,
|
|
6
|
-
Image, GalleryHorizontalEnd, Receipt,
|
|
7
|
-
} from 'lucide-react'
|
|
8
|
-
import { useTranslation } from '@fayz-ai/core'
|
|
9
|
-
import type { FormFieldType } from '../types'
|
|
10
|
-
|
|
11
|
-
export interface FieldTypeItem {
|
|
12
|
-
type: FormFieldType
|
|
13
|
-
icon: React.ElementType
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const DEFAULT_COL_SPAN: Record<string, number> = {
|
|
17
|
-
title: 12,
|
|
18
|
-
memo: 12,
|
|
19
|
-
richtext: 12,
|
|
20
|
-
gallery: 12,
|
|
21
|
-
budget: 12,
|
|
22
|
-
image: 6,
|
|
23
|
-
text: 6,
|
|
24
|
-
date: 4,
|
|
25
|
-
select: 6,
|
|
26
|
-
radio: 6,
|
|
27
|
-
tags: 8,
|
|
28
|
-
checkbox: 4,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const FIELD_TYPES: FieldTypeItem[] = [
|
|
32
|
-
{ type: 'title', icon: Heading },
|
|
33
|
-
{ type: 'text', icon: Type },
|
|
34
|
-
{ type: 'memo', icon: AlignLeft },
|
|
35
|
-
{ type: 'richtext', icon: FileEdit },
|
|
36
|
-
{ type: 'date', icon: CalendarDays },
|
|
37
|
-
{ type: 'select', icon: ChevronDown },
|
|
38
|
-
{ type: 'radio', icon: CircleDot },
|
|
39
|
-
{ type: 'tags', icon: Tags },
|
|
40
|
-
{ type: 'checkbox', icon: CheckSquare },
|
|
41
|
-
{ type: 'image', icon: Image },
|
|
42
|
-
{ type: 'gallery', icon: GalleryHorizontalEnd },
|
|
43
|
-
{ type: 'budget', icon: Receipt },
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
function DraggableFieldType({ item }: { item: FieldTypeItem }) {
|
|
47
|
-
const t = useTranslation()
|
|
48
|
-
const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
|
|
49
|
-
id: `palette-${item.type}`,
|
|
50
|
-
data: { source: 'palette', fieldType: item.type },
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
const Icon = item.icon
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<button
|
|
57
|
-
ref={setNodeRef}
|
|
58
|
-
{...listeners}
|
|
59
|
-
{...attributes}
|
|
60
|
-
className={`flex items-center gap-2 w-full px-3 py-2 rounded-lg border bg-card text-sm font-medium transition-all hover:shadow-sm hover:border-primary/30 cursor-grab active:cursor-grabbing ${isDragging ? 'opacity-30' : ''}`}
|
|
61
|
-
>
|
|
62
|
-
<Icon className="h-4 w-4 text-muted-foreground shrink-0" />
|
|
63
|
-
<span className="truncate">{t(`customForms.fieldType.${item.type}`)}</span>
|
|
64
|
-
</button>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function FieldPalette() {
|
|
69
|
-
const t = useTranslation()
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<div className="w-44 shrink-0 space-y-1.5">
|
|
73
|
-
<p className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground px-1 mb-2">
|
|
74
|
-
{t('customForms.builder.fields')}
|
|
75
|
-
</p>
|
|
76
|
-
{FIELD_TYPES.map((item) => (
|
|
77
|
-
<DraggableFieldType key={item.type} item={item} />
|
|
78
|
-
))}
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
81
|
-
}
|