Initial commit: SKEEN Derma Experts - Sistema Integral de Gestión Clínica
- Frontend React (SKEEN Brand) con Vite, TypeScript, Tailwind - Frontend Homenest (versión alternativa) - Módulos Odoo 17 custom (citas, pacientes, monedero, pagos, ventas, inventario, whatsapp) - WACRM fork (Next.js 16 + Supabase) - Hermes + Bridge + Skills (Qwen3.6 via Nan Builders) - Scripts de migración y operación - Documentación extensiva en docs/
This commit is contained in:
134
wacrm/src/components/flows/forms/fields.tsx
Normal file
134
wacrm/src/components/flows/forms/fields.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Reusable field components shared across every per-node form.
|
||||
*
|
||||
* `NodeKeySelect` — picks a node from the flow's node list, rendered
|
||||
* with the source node's icon so the dropdown reads as
|
||||
* "destination = ◇ menu" rather than an opaque slug.
|
||||
*
|
||||
* `NextNodeRow` — wraps NodeKeySelect with a label; the most common
|
||||
* per-node form row ("after this node, advance to…").
|
||||
*
|
||||
* `TextRow` — wraps Input or Textarea behind a label. Pure UI sugar
|
||||
* to keep per-node forms uncluttered.
|
||||
*
|
||||
* Lives in src/components/flows/forms/ so both the list view's
|
||||
* collapsed-card editor and the canvas view's side-panel editor
|
||||
* (introduced in this PR) mount the exact same form components.
|
||||
*/
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { NODE_META, type BuilderNode } from "../shared";
|
||||
|
||||
export function TextRow({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
rows = 1,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (v: string) => void;
|
||||
rows?: number;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<label className="mb-1 block text-xs text-muted-foreground">{label}</label>
|
||||
{rows > 1 ? (
|
||||
<Textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
rows={rows}
|
||||
className="bg-muted"
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="bg-muted"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NextNodeRow({
|
||||
value,
|
||||
allNodes,
|
||||
currentKey,
|
||||
onChange,
|
||||
label,
|
||||
}: {
|
||||
value: string;
|
||||
allNodes: BuilderNode[];
|
||||
currentKey: string;
|
||||
onChange: (v: string) => void;
|
||||
label: string;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<label className="mb-1 block text-xs text-muted-foreground">{label}</label>
|
||||
<NodeKeySelect
|
||||
value={value || null}
|
||||
nodes={allNodes}
|
||||
excludeKey={currentKey}
|
||||
onChange={(v) => onChange(v ?? "")}
|
||||
placeholder="Pick a next node…"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NodeKeySelect({
|
||||
value,
|
||||
nodes,
|
||||
excludeKey,
|
||||
onChange,
|
||||
placeholder,
|
||||
className,
|
||||
}: {
|
||||
value: string | null;
|
||||
nodes: BuilderNode[];
|
||||
excludeKey?: string;
|
||||
onChange: (v: string | null) => void;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const options = nodes.filter((n) => n.node_key !== excludeKey);
|
||||
return (
|
||||
<Select
|
||||
value={value ?? "__none__"}
|
||||
onValueChange={(v) => onChange(v === "__none__" ? null : v)}
|
||||
>
|
||||
<SelectTrigger className={cn("bg-muted", className)}>
|
||||
<SelectValue placeholder={placeholder ?? "—"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">— None —</SelectItem>
|
||||
{options.map((n) => {
|
||||
const Icon = NODE_META[n.node_type].icon;
|
||||
return (
|
||||
<SelectItem key={n.node_key} value={n.node_key}>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Icon
|
||||
className={cn("h-3 w-3", NODE_META[n.node_type].color)}
|
||||
/>
|
||||
{n.node_key}
|
||||
</span>
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user