From c2a5c589af0917a9d3cd7f052b36164cc8f2032d Mon Sep 17 00:00:00 2001 From: vibeasite-bot Date: Sat, 22 Aug 2026 05:49:33 +0100 Subject: [PATCH] bake canonical build harness (deterministic /ship for forks) [saas] --- src/components/DataForm.tsx | 195 ++++++++ src/components/DataTable.tsx | 214 +++++++++ src/components/KpiCard.tsx | 124 +++++ src/components/ScrollHeader.tsx | 125 +++++ src/components/ThemeToggle.tsx | 19 + src/components/ui/accordion.tsx | 72 +-- src/components/ui/alert-dialog.tsx | 104 +++++ src/components/ui/alert.tsx | 43 ++ src/components/ui/aspect-ratio.tsx | 5 + src/components/ui/avatar.tsx | 42 +- src/components/ui/badge.tsx | 50 +- src/components/ui/breadcrumb.tsx | 90 ++++ src/components/ui/button.tsx | 84 ++-- src/components/ui/calendar.tsx | 54 +++ src/components/ui/card.tsx | 44 +- src/components/ui/carousel.tsx | 224 +++++++++ src/components/ui/chart.tsx | 303 ++++++++++++ src/components/ui/checkbox.tsx | 39 +- src/components/ui/collapsible.tsx | 9 + src/components/ui/command.tsx | 132 ++++++ src/components/ui/context-menu.tsx | 178 +++++++ src/components/ui/dialog.tsx | 95 ++++ src/components/ui/drawer.tsx | 87 ++++ src/components/ui/dropdown-menu.tsx | 212 +++++++-- src/components/ui/form.tsx | 129 ++++++ src/components/ui/hover-card.tsx | 27 ++ src/components/ui/input-otp.tsx | 61 +++ src/components/ui/input.tsx | 29 +- src/components/ui/label.tsx | 17 +- src/components/ui/menubar.tsx | 207 +++++++++ src/components/ui/navigation-menu.tsx | 120 +++++ src/components/ui/pagination.tsx | 81 ++++ src/components/ui/popover.tsx | 29 ++ src/components/ui/progress.tsx | 23 + src/components/ui/radio-group.tsx | 36 ++ src/components/ui/resizable.tsx | 37 ++ src/components/ui/scroll-area.tsx | 38 ++ src/components/ui/select.tsx | 191 +++++--- src/components/ui/separator.tsx | 23 +- src/components/ui/sheet.tsx | 139 ++++-- src/components/ui/sidebar.tsx | 637 ++++++++++++++++++++++++++ src/components/ui/skeleton.tsx | 7 + src/components/ui/slider.tsx | 23 + src/components/ui/switch.tsx | 27 ++ src/components/ui/table.tsx | 72 ++- src/components/ui/tabs.tsx | 85 ++-- src/components/ui/textarea.tsx | 19 +- src/components/ui/toast.tsx | 111 +++++ src/components/ui/toaster.tsx | 24 + src/components/ui/toggle-group.tsx | 49 ++ src/components/ui/toggle.tsx | 37 ++ src/components/ui/tooltip.tsx | 28 ++ src/components/ui/use-toast.ts | 3 + src/contexts/AuthContext.tsx | 234 ++++++++++ src/hooks/use-anydb.ts | 292 ++++++++++++ src/hooks/use-mobile.tsx | 19 + src/hooks/use-oddsockets.ts | 161 +++++++ src/hooks/use-toast.ts | 186 ++++++++ src/hooks/use-tygarun.ts | 330 +++++++++++++ src/lib/anydb.ts | 21 + src/lib/oddsockets.ts | 121 +++++ src/lib/safe-icons.ts | 89 ++++ src/lib/tygarun.ts | 115 +++++ src/lib/utils.ts | 9 +- 64 files changed, 6045 insertions(+), 385 deletions(-) create mode 100644 src/components/DataForm.tsx create mode 100644 src/components/DataTable.tsx create mode 100644 src/components/KpiCard.tsx create mode 100644 src/components/ScrollHeader.tsx create mode 100644 src/components/ThemeToggle.tsx create mode 100644 src/components/ui/alert-dialog.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/aspect-ratio.tsx create mode 100644 src/components/ui/breadcrumb.tsx create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/carousel.tsx create mode 100644 src/components/ui/chart.tsx create mode 100644 src/components/ui/collapsible.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/context-menu.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/drawer.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/hover-card.tsx create mode 100644 src/components/ui/input-otp.tsx create mode 100644 src/components/ui/menubar.tsx create mode 100644 src/components/ui/navigation-menu.tsx create mode 100644 src/components/ui/pagination.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/resizable.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/components/ui/slider.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/toast.tsx create mode 100644 src/components/ui/toaster.tsx create mode 100644 src/components/ui/toggle-group.tsx create mode 100644 src/components/ui/toggle.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/components/ui/use-toast.ts create mode 100644 src/contexts/AuthContext.tsx create mode 100644 src/hooks/use-anydb.ts create mode 100644 src/hooks/use-mobile.tsx create mode 100644 src/hooks/use-oddsockets.ts create mode 100644 src/hooks/use-toast.ts create mode 100644 src/hooks/use-tygarun.ts create mode 100644 src/lib/anydb.ts create mode 100644 src/lib/oddsockets.ts create mode 100644 src/lib/safe-icons.ts create mode 100644 src/lib/tygarun.ts diff --git a/src/components/DataForm.tsx b/src/components/DataForm.tsx new file mode 100644 index 0000000..de1002e --- /dev/null +++ b/src/components/DataForm.tsx @@ -0,0 +1,195 @@ +// --------------------------------------------------------------------------- +// DataForm — pre-built form component wired to AnyDB insertOne +// --------------------------------------------------------------------------- +// PRE-INSTALLED. CodeWriterAgent just uses: +// import { DataForm } from "@/components/DataForm" +// +// --------------------------------------------------------------------------- + +import { useState } from "react"; +import { useMutation } from "@/hooks/use-anydb"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { useToast } from "@/hooks/use-toast"; +import { Loader2, CheckCircle } from "lucide-react"; + +interface FormField { + name: string; + label: string; + type?: "text" | "email" | "tel" | "number" | "date" | "time" | "url" | "textarea" | "select"; + placeholder?: string; + required?: boolean; + options?: { value: string; label: string }[]; // For select type + rows?: number; // For textarea + min?: number; + max?: number; + defaultValue?: string | number; + className?: string; + gridCol?: number; // 1 or 2 — for grid layout +} + +interface DataFormProps { + collection: string; + fields: FormField[]; + submitLabel?: string; + successMessage?: string; + onSuccess?: (data: any) => void; + className?: string; + layout?: "stack" | "grid"; // stack = vertical, grid = 2-col +} + +export function DataForm({ + collection, + fields, + submitLabel = "Submit", + successMessage = "Saved successfully!", + onSuccess, + className = "", + layout = "stack", +}: DataFormProps) { + const { toast } = useToast(); + const { mutate, loading } = useMutation(collection); + const [submitted, setSubmitted] = useState(false); + + // Build initial form state from fields + const initialState: Record = {}; + fields.forEach(f => { initialState[f.name] = f.defaultValue || ""; }); + const [formData, setFormData] = useState(initialState); + + const handleChange = (name: string, value: any) => { + setFormData(prev => ({ ...prev, [name]: value })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + // Basic validation + for (const field of fields) { + if (field.required && !formData[field.name]) { + toast({ + title: "Required field", + description: `Please fill in ${field.label}`, + variant: "destructive", + }); + return; + } + if (field.type === "email" && formData[field.name] && !formData[field.name].includes("@")) { + toast({ + title: "Invalid email", + description: "Please enter a valid email address", + variant: "destructive", + }); + return; + } + } + + try { + const result = await mutate(formData); + setSubmitted(true); + toast({ title: "Success", description: successMessage }); + setFormData(initialState); + onSuccess?.(result); + + // Reset submitted state after 3 seconds + setTimeout(() => setSubmitted(false), 3000); + } catch (err) { + toast({ + title: "Error", + description: "Something went wrong. Please try again.", + variant: "destructive", + }); + } + }; + + const renderField = (field: FormField) => { + const id = `form-${collection}-${field.name}`; + + return ( +
+ + + {field.type === "textarea" ? ( +