- Calculs en pied de tableau : Somme/Moyenne/Min/Max/Compte, cliquable pour changer - Link Preview : métadonnées persistées + texte indexable pour recherche/embeddings - Fix: bg-memento-paper → bg-card (dark mode) sur dialogs Structured Views - Fix: bouton Ajouter un champ → brand-accent au lieu de primary - Calendar view retiré du sélecteur (non pertinent)
129 lines
4.6 KiB
TypeScript
129 lines
4.6 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { X } from 'lucide-react'
|
|
import { Button } from '@/components/ui/button'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
import type { PropertyType } from '@/lib/structured-views/types'
|
|
import { PROPERTY_TYPES } from '@/lib/structured-views/types'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
type AddPropertyDialogProps = {
|
|
open: boolean
|
|
onClose: () => void
|
|
onSubmit: (name: string, type: PropertyType, options: string[]) => Promise<void>
|
|
}
|
|
|
|
export function AddPropertyDialog({ open, onClose, onSubmit }: AddPropertyDialogProps) {
|
|
const { t } = useLanguage()
|
|
const [name, setName] = useState('')
|
|
const [type, setType] = useState<PropertyType>('text')
|
|
const [optionsText, setOptionsText] = useState('')
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
if (!open) return null
|
|
|
|
const needsOptions = type === 'select' || type === 'multiselect'
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
const trimmed = name.trim()
|
|
if (!trimmed) return
|
|
const options = needsOptions
|
|
? optionsText.split('\n').map((l) => l.trim()).filter(Boolean)
|
|
: []
|
|
if (needsOptions && options.length === 0) return
|
|
setSaving(true)
|
|
try {
|
|
await onSubmit(trimmed, type, options)
|
|
setName('')
|
|
setType('text')
|
|
setOptionsText('')
|
|
onClose()
|
|
} finally {
|
|
setSaving(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm">
|
|
<div
|
|
role="dialog"
|
|
aria-modal
|
|
className="w-full max-w-md rounded-2xl border border-border bg-card shadow-xl p-6 space-y-5"
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<h2 className="font-memento-serif text-lg">{t('structuredViews.addPropertyTitle')}</h2>
|
|
<button type="button" onClick={onClose} className="p-1 rounded-lg hover:bg-foreground/5">
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<p className="text-[12px] leading-relaxed text-muted-foreground rounded-lg bg-foreground/[0.03] px-3 py-2">
|
|
{t('structuredViews.addPropertyHint')}
|
|
</p>
|
|
<div>
|
|
<label className="text-[10px] uppercase tracking-widest font-bold text-muted-foreground">
|
|
{t('structuredViews.propertyName')}
|
|
</label>
|
|
<input
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
className="mt-1 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm"
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-[10px] uppercase tracking-widest font-bold text-muted-foreground">
|
|
{t('structuredViews.propertyType')}
|
|
</label>
|
|
<div className="mt-2 flex flex-wrap gap-2">
|
|
{PROPERTY_TYPES.map((pt) => (
|
|
<button
|
|
key={pt}
|
|
type="button"
|
|
onClick={() => setType(pt)}
|
|
className={cn(
|
|
'px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider border transition-colors',
|
|
type === pt
|
|
? 'bg-foreground text-background border-foreground'
|
|
: 'border-border text-muted-foreground hover:border-foreground/30',
|
|
)}
|
|
>
|
|
{t(`structuredViews.propertyTypes.${pt}`)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{needsOptions && (
|
|
<div>
|
|
<label className="text-[10px] uppercase tracking-widest font-bold text-muted-foreground">
|
|
{t('structuredViews.selectOptions')}
|
|
</label>
|
|
<textarea
|
|
value={optionsText}
|
|
onChange={(e) => setOptionsText(e.target.value)}
|
|
placeholder={t('structuredViews.selectOptionsPlaceholder')}
|
|
rows={4}
|
|
className="mt-1 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm resize-none"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex justify-end gap-2 pt-2">
|
|
<Button type="button" variant="ghost" onClick={onClose}>
|
|
{t('general.cancel')}
|
|
</Button>
|
|
<Button type="submit" disabled={saving || !name.trim()} className="bg-brand-accent text-white hover:bg-brand-accent/90">
|
|
{t('structuredViews.addProperty')}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|