diff --git a/.changeset/config.json b/.changeset/config.json
index 9e3483f..e60dc3d 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -1,11 +1,11 @@
{
- "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
- "changelog": "@changesets/cli/changelog",
- "commit": false,
- "fixed": [],
- "linked": [],
- "access": "restricted",
- "baseBranch": "main",
+ "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
+ "changelog": "@changesets/cli/changelog",
+ "commit": false,
+ "fixed": [],
+ "linked": [],
+ "access": "restricted",
+ "baseBranch": "main",
"privatePackages": {
"version": true,
"tag": true
diff --git a/.changeset/neat-icons-battle.md b/.changeset/neat-icons-battle.md
new file mode 100644
index 0000000..7f8e92c
--- /dev/null
+++ b/.changeset/neat-icons-battle.md
@@ -0,0 +1,5 @@
+---
+"web": minor
+---
+
+New UI
diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts
index 40c3d68..1b3be08 100644
--- a/apps/web/next-env.d.ts
+++ b/apps/web/next-env.d.ts
@@ -2,4 +2,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs
index 4e50d08..829a280 100644
--- a/apps/web/next.config.mjs
+++ b/apps/web/next.config.mjs
@@ -1,6 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
+ typescript: {
+ ignoreBuildErrors: true,
+ },
};
export default nextConfig;
diff --git a/apps/web/package.json b/apps/web/package.json
index 854819a..ceb4c72 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -11,51 +11,60 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@dnd-kit/core": "6.0.8",
+ "@dnd-kit/modifiers": "6.0.1",
+ "@dnd-kit/sortable": "7.0.2",
+ "@dnd-kit/utilities": "3.2.1",
"@hookform/resolvers": "^3.9.1",
"@nanostores/persistent": "^0.10.2",
- "@nanostores/react": "^0.8.2",
- "@radix-ui/react-accordion": "^1.2.1",
- "@radix-ui/react-checkbox": "^1.1.2",
- "@radix-ui/react-dialog": "^1.1.2",
- "@radix-ui/react-label": "^2.1.0",
- "@radix-ui/react-popover": "^1.1.2",
- "@radix-ui/react-radio-group": "^1.2.1",
- "@radix-ui/react-select": "^2.1.2",
- "@radix-ui/react-separator": "^1.1.0",
- "@radix-ui/react-slot": "^1.1.0",
- "@radix-ui/react-switch": "^1.1.1",
- "@radix-ui/react-tabs": "^1.1.1",
- "@radix-ui/react-toast": "^1.2.2",
+ "@nanostores/react": "^0.8.4",
+ "@radix-ui/react-accordion": "^1.2.2",
+ "@radix-ui/react-checkbox": "^1.1.3",
+ "@radix-ui/react-dialog": "^1.1.4",
+ "@radix-ui/react-label": "^2.1.1",
+ "@radix-ui/react-popover": "^1.1.4",
+ "@radix-ui/react-radio-group": "^1.2.2",
+ "@radix-ui/react-scroll-area": "^1.2.2",
+ "@radix-ui/react-select": "^2.1.4",
+ "@radix-ui/react-separator": "^1.1.1",
+ "@radix-ui/react-slot": "^1.1.1",
+ "@radix-ui/react-switch": "^1.1.2",
+ "@radix-ui/react-tabs": "^1.1.2",
+ "@radix-ui/react-toast": "^1.2.4",
"@vercel/analytics": "^1.4.1",
"class-variance-authority": "^0.7.1",
+ "classnames": "^2.5.1",
"clsx": "^2.1.1",
"cmdk": "^1.0.4",
+ "date-fns": "^4.1.0",
+ "formbuilder-core": "workspace:*",
"handlebars": "^4.7.8",
"immer": "^10.1.1",
- "json-schema-to-zod": "^2.4.1",
+ "json-schema-to-zod": "^2.5.0",
"lucide-react": "0.462.0",
"nanostores": "^0.11.3",
- "next": "^15.0.3",
- "next-themes": "^0.4.3",
+ "next": "15.1.2",
+ "next-themes": "^0.4.4",
"ramda": "^0.30.1",
"react": "^18.3.1",
- "react-day-picker": "^9.4.0",
+ "react-day-picker": "8.10.1",
"react-dom": "^18.3.1",
- "react-hook-form": "^7.53.2",
- "react-icons": "^5.3.0",
+ "react-hook-form": "^7.54.1",
+ "react-icons": "^5.4.0",
"sharp": "^0.33.5",
+ "swapy": "^0.4.2",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
- "zod": "^3.23.8"
+ "zod": "^3.24.1"
},
"devDependencies": {
- "@types/node": "^22.10.1",
+ "@types/node": "^22.10.2",
"@types/ramda": "^0.30.2",
- "@types/react": "^18.3.12",
- "@types/react-dom": "^18.3.1",
+ "@types/react": "^18.3.18",
+ "@types/react-dom": "^18.3.5",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.49",
- "tailwindcss": "^3.4.15",
+ "tailwindcss": "^3.4.17",
"typescript": "^5.7.2"
}
}
diff --git a/apps/web/src/app/builder/_components/AddNewFieldArrows.tsx b/apps/web/src/app/builder/_components/AddNewFieldArrows.tsx
new file mode 100644
index 0000000..5b8b37c
--- /dev/null
+++ b/apps/web/src/app/builder/_components/AddNewFieldArrows.tsx
@@ -0,0 +1,51 @@
+import { Button } from "@/components/ui/button";
+import { addItem } from "@/state/state";
+import {
+ ArrowBigUpDash,
+ ArrowBigDownDash,
+ ArrowBigLeftDash,
+ ArrowBigRightDash,
+} from "lucide-react";
+
+export function AddNewFieldArrows({ id }: { id: string }) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/builder/_components/CustomSensor.ts b/apps/web/src/app/builder/_components/CustomSensor.ts
new file mode 100644
index 0000000..9b82132
--- /dev/null
+++ b/apps/web/src/app/builder/_components/CustomSensor.ts
@@ -0,0 +1,40 @@
+import type { MouseEvent, KeyboardEvent } from 'react'
+import {
+ MouseSensor as LibMouseSensor,
+ KeyboardSensor as LibKeyboardSensor
+} from '@dnd-kit/core'
+
+export class MouseSensor extends LibMouseSensor {
+ static activators = [
+ {
+ eventName: 'onMouseDown' as const,
+ handler: ({ nativeEvent: event }: MouseEvent) => {
+ return shouldHandleEvent(event.target as HTMLElement)
+ }
+ }
+ ]
+}
+
+// export class KeyboardSensor extends LibKeyboardSensor {
+// static activators = [
+// {
+// eventName: 'onKeyDown' as const,
+// handler: ({ nativeEvent: event }: KeyboardEvent) => {
+// return shouldHandleEvent(event.target as HTMLElement)
+// }
+// }
+// ]
+// }
+
+function shouldHandleEvent(element: HTMLElement | null) {
+ let cur = element
+
+ while (cur) {
+ if (cur.dataset?.noDnd) {
+ return false
+ }
+ cur = cur.parentElement
+ }
+
+ return true
+}
\ No newline at end of file
diff --git a/apps/web/src/app/builder/_components/FormFieldContent.tsx b/apps/web/src/app/builder/_components/FormFieldContent.tsx
new file mode 100644
index 0000000..49f184f
--- /dev/null
+++ b/apps/web/src/app/builder/_components/FormFieldContent.tsx
@@ -0,0 +1,54 @@
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import { Settings, Trash } from "lucide-react";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/components/ui/accordion";
+import { cn } from "@/lib/utils";
+import { useState } from "react";
+import { removeItem } from "@/state/state";
+
+export function FormFieldContent({ id }: { id: string }) {
+ const [isOpen, setIsOpen] = useState(false);
+
+ return (
+
+
+
+
+ Task
+
+
+
+
+
+
+
+
+
+ Yes. It adheres to the WAI-ARIA design pattern.
+
+
+
+ );
+}
diff --git a/apps/web/src/app/builder/_components/FormSettings/FrameworkCombobox.tsx b/apps/web/src/app/builder/_components/FormSettings/FrameworkCombobox.tsx
new file mode 100644
index 0000000..67f35f8
--- /dev/null
+++ b/apps/web/src/app/builder/_components/FormSettings/FrameworkCombobox.tsx
@@ -0,0 +1,107 @@
+"use client"
+
+import * as React from "react"
+import { Check, ChevronsUpDown } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover"
+import type { FormFramework } from "formbuilder-core"
+import { useAppState } from "@/state/state";
+
+const frameworks: { value: FormFramework; label: string }[] = [
+ {
+ value: "next",
+ label: "Next.js",
+ },
+ {
+ value: "react",
+ label: "React",
+ },
+ {
+ value: "svelte",
+ label: "Svelte",
+ },
+ {
+ value: "vue",
+ label: "Vue",
+ },
+ {
+ value: "solid",
+ label: "Solid",
+ },
+ {
+ value: "astro",
+ label: "Astro",
+ },
+]
+
+export function FrameworkCombobox() {
+ const { currentForm, updateFormFramework } = useAppState();
+ const [open, setOpen] = React.useState(false)
+ const [value, setValue] = React.useState(currentForm.framework)
+
+// React.useEffect(() => {
+// setValue(currentForm.framework);
+// }, [currentForm]);
+
+ return (
+
+
+
+
+
+
+
+
+ No framework found.
+
+ {frameworks.map((framework) => (
+ {
+ setValue(currentValue === value ? "" : currentValue)
+ updateFormFramework(currentValue)
+ setOpen(false)
+ }}
+ >
+ {framework.label}
+
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/web/src/app/builder/_components/FormSettings/SettingsToggle.tsx b/apps/web/src/app/builder/_components/FormSettings/SettingsToggle.tsx
new file mode 100644
index 0000000..667f79f
--- /dev/null
+++ b/apps/web/src/app/builder/_components/FormSettings/SettingsToggle.tsx
@@ -0,0 +1,27 @@
+import React from "react";
+import { Button } from "@/components/ui/button";
+import { Settings } from "lucide-react";
+import { useAppState } from "@/state/state";
+
+interface SettingsToggleProps {
+ showSettings: boolean;
+ setShowSettings: (value: boolean) => void;
+}
+
+export function SettingsToggle({
+ showSettings,
+ setShowSettings,
+}: SettingsToggleProps) {
+ const state = useAppState();
+ return (
+
+
+ {state.currentForm.name}
+
+
+
+ );
+}
diff --git a/apps/web/src/app/builder/_components/FormSettings/index.tsx b/apps/web/src/app/builder/_components/FormSettings/index.tsx
new file mode 100644
index 0000000..a7a8d5b
--- /dev/null
+++ b/apps/web/src/app/builder/_components/FormSettings/index.tsx
@@ -0,0 +1,114 @@
+import { Input } from "@/components/ui/input";
+
+import { Label } from "@/components/ui/label";
+import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
+import { updateFormSettings, useAppState } from "@/state/state";
+import { FrameworkCombobox } from "./FrameworkCombobox";
+import { Switch } from "@/components/ui/switch";
+
+export default function SettingsForm() {
+ const state = useAppState();
+ return (
+
+
state.updateFormName(newValue)}
+ />
+
+
+
+ Form Mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ state.updateFormSettings({ importAlias: newValue })
+ }
+ />
+
+
+
+
No placeholder
+
Don't place a placeholder
+
+
+
+ updateFormSettings({
+ noPlaceholder: !state.currentForm.settings.noPlaceholder,
+ })
+ }
+ />
+
+
+
+
+
+
No description
+
No description
+
+
+
+ updateFormSettings({
+ noDescription: !state.currentForm.settings.noDescription,
+ })
+ }
+ />
+
+
+
+
+
+ Framework
+
+
+
+
+ );
+}
+
+type FormInput = {
+ label: string;
+ value: string;
+ onChange: (value: string) => void;
+};
+
+function FormInput({ label, value, onChange }: FormInput) {
+ return (
+
+
+ {label}
+
+ {
+ const newValue = e.currentTarget.value;
+ onChange(newValue);
+ }}
+ />
+
+ );
+}
diff --git a/apps/web/src/app/builder/_components/NewField.tsx b/apps/web/src/app/builder/_components/NewField.tsx
new file mode 100644
index 0000000..3b60258
--- /dev/null
+++ b/apps/web/src/app/builder/_components/NewField.tsx
@@ -0,0 +1,17 @@
+import { Button } from "@/components/ui/button";
+import { useAppState } from "@/state/state";
+
+export function NewField({ text }: { text: string }) {
+ const state = useAppState();
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/apps/web/src/app/builder/_components/SortableGrid.tsx b/apps/web/src/app/builder/_components/SortableGrid.tsx
new file mode 100644
index 0000000..0cd2067
--- /dev/null
+++ b/apps/web/src/app/builder/_components/SortableGrid.tsx
@@ -0,0 +1,136 @@
+import { useState } from "react";
+import {
+ DndContext,
+ closestCenter,
+ KeyboardSensor,
+ PointerSensor,
+ useSensor,
+ useSensors,
+ DragOverlay,
+ closestCorners,
+ rectIntersection,
+ type DragEndEvent,
+ type DragStartEvent,
+ type UniqueIdentifier,
+} from "@dnd-kit/core";
+import {
+ arrayMove,
+ SortableContext,
+ sortableKeyboardCoordinates,
+ rectSwappingStrategy,
+ rectSortingStrategy,
+ arraySwap,
+} from "@dnd-kit/sortable";
+
+import { SortableItem } from "./SortableItem";
+import { NewField } from "./NewField";
+import { MouseSensor } from "./CustomSensor";
+import { useAppState } from "@/state/state";
+
+export const SortableGrid = () => {
+ const [activeId, setActiveId] = useState();
+ const state = useAppState();
+ const items = state.temp_items;
+ function setItems(items: string[][]) {
+ state.setAppState({ temp_items: items });
+ }
+ // const [items, setItems] = useState(state.temp_items);
+ const sensors = useSensors(
+ useSensor(MouseSensor),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ }),
+ );
+
+ const handleDragStart = (event: DragStartEvent) => {
+ console.log("active", event.active.id);
+ setActiveId(event.active.id);
+ };
+
+ const handleDragEnd = (event: DragEndEvent) => {
+ // setActiveId(null);
+ const { active, over } = event;
+ if (!over) return;
+
+ if (active.id !== over.id) {
+ setItems((items) => {
+ let overRowIndex = -1;
+ let overColIndex = -1;
+ let activeRowIndex = -1;
+ let activeColIndex = -1;
+
+ // Find indices
+ for (let i = 0; i < items.length; i++) {
+ const overIdx = items[i].indexOf(over.id as string);
+ const activeIdx = items[i].indexOf(active.id as string);
+
+ if (overIdx !== -1) {
+ overRowIndex = i;
+ overColIndex = overIdx;
+ }
+ if (activeIdx !== -1) {
+ activeRowIndex = i;
+ activeColIndex = activeIdx;
+ }
+ }
+
+ // If active is not in the list but we found over position
+ if (activeRowIndex === -1 && overRowIndex !== -1) {
+ const newItems = items.map((row) => [...row]);
+ // Insert active.id before over.id
+ newItems[overRowIndex].splice(overColIndex, 0, active.id as string);
+ return newItems;
+ }
+
+ // Normal swap if both items are in the list
+ if (overRowIndex !== -1 && activeRowIndex !== -1) {
+ const newItems = items.map((row) => [...row]);
+ // @ts-ignore
+ newItems[overRowIndex][overColIndex] = active.id;
+ // @ts-ignore
+ newItems[activeRowIndex][activeColIndex] = over.id;
+ return newItems;
+ }
+
+ return items;
+ });
+ }
+ console.log("items", items);
+ };
+
+ return (
+
+
+
+
+ {items.map((row, idx) => (
+ // biome-ignore lint/suspicious/noArrayIndexKey:
+
+ {row.map((id) => (
+
+ ))}
+
+ ))}
+
+ {activeId ? (
+ id === activeId)}
+ // @ts-ignore
+ value={items.filter((id) => id === activeId)}
+ />
+ ) : null}
+
+
+
+
+
+ );
+};
diff --git a/apps/web/src/app/builder/_components/SortableItem.tsx b/apps/web/src/app/builder/_components/SortableItem.tsx
new file mode 100644
index 0000000..aaf129a
--- /dev/null
+++ b/apps/web/src/app/builder/_components/SortableItem.tsx
@@ -0,0 +1,75 @@
+import { useSortable } from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+import { cva } from "class-variance-authority";
+import { cn } from "@/lib/utils";
+import { useAppState } from "@/state/state";
+import { FormFieldContent } from "./FormFieldContent";
+import { AddNewFieldArrows } from "./AddNewFieldArrows";
+
+interface TaskCardProps {
+ id: string;
+ value: string;
+ isOverlay?: boolean;
+}
+
+export type TaskType = "Task";
+
+// export interface TaskDragData {
+// type: TaskType;
+// task: Task;
+// }
+
+export function SortableItem({ id, value, isOverlay }: TaskCardProps) {
+ const {
+ setNodeRef,
+ attributes,
+ listeners,
+ transform,
+ transition,
+ isDragging,
+ } = useSortable({
+ id: id,
+ animateLayoutChanges: () => false,
+ });
+
+ const style = {
+ transition,
+ transform: CSS.Translate.toString(transform),
+ };
+
+ const state = useAppState();
+ const variants = cva("h-[53px] h-fit min-w-[380px] rounded-lg border-2", {
+ variants: {
+ drop: { default: "" },
+ dragging: {
+ over: "opacity-30 ring-2",
+ overlay: "ring-2 ring-primary",
+ },
+ },
+ });
+
+ return (
+ <>
+ {state.renderContent ? (
+
+
+
+ ) : (
+
+ )}
+ >
+ );
+}
diff --git a/apps/web/src/app/builder/page.tsx b/apps/web/src/app/builder/page.tsx
new file mode 100644
index 0000000..71006b9
--- /dev/null
+++ b/apps/web/src/app/builder/page.tsx
@@ -0,0 +1,49 @@
+"use client";
+
+import React, { useState } from "react";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { FormList } from "@/core/FormList";
+import { Preview } from "@/core/Preview";
+import { SortableGrid } from "./_components/SortableGrid";
+import { NewField } from "./_components/NewField";
+import { SettingsToggle } from "./_components/FormSettings/SettingsToggle";
+import SettingsForm from "./_components/FormSettings";
+
+export default function Builder() {
+ const [showSettings, setShowSettings] = useState(false);
+
+ return (
+
+
+
+
+
+ Editor
+ Preview
+ Code
+
+
+
+ {showSettings ? : }
+
+
+
+
+
+
+
+
+
+
+ Add new fields
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 0020aa9..f23d6e3 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -1,5 +1,5 @@
import "@/styles/globals.css";
-import type { Metadata } from "next";
+import type { Metadata, Viewport } from "next";
import { Analytics } from "@vercel/analytics/react";
import { siteConfig } from "@/config/site";
import { fontSans } from "@/lib/fonts";
@@ -9,6 +9,13 @@ import { SiteHeader } from "@/components/site-header";
import { TailwindIndicator } from "@/components/tailwind-indicator";
import { ThemeProvider } from "@/components/theme-provider";
+export const viewport: Viewport = {
+ themeColor: [
+ { media: "(prefers-color-scheme: light)", color: "white" },
+ { media: "(prefers-color-scheme: dark)", color: "black" },
+ ],
+};
+
export const metadata: Metadata = {
openGraph: { images: "/demo.png" },
@@ -17,10 +24,6 @@ export const metadata: Metadata = {
template: `%s - ${siteConfig.name}`,
},
description: siteConfig.description,
- themeColor: [
- { media: "(prefers-color-scheme: light)", color: "white" },
- { media: "(prefers-color-scheme: dark)", color: "black" },
- ],
icons: {
icon: "/favicon.ico",
shortcut: "/favicon-16x16.png",
diff --git a/apps/web/src/app/test/page.tsx b/apps/web/src/app/test/page.tsx
deleted file mode 100644
index 1f9e703..0000000
--- a/apps/web/src/app/test/page.tsx
+++ /dev/null
@@ -1,326 +0,0 @@
-
-'use client'
-import { zodResolver } from "@hookform/resolvers/zod"
-import * as z from "zod"
-import { useForm } from "react-hook-form"
-import { Button } from "@/components/ui/button"
-import {
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form"
-import { cn } from "@/lib/utils"
-import { Input } from "@/components/ui/input"
-
-import { Switch } from "@/components/ui/switch"
-
-import { format } from "date-fns"
-import { CalendarIcon } from "lucide-react"
-import { Calendar } from "@/components/ui/calendar"
-
-import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
-
-import { Check, ChevronsUpDown } from "lucide-react"
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
-} from "@/components/ui/command"
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover"
-
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
-const formSchema = z.object({ "username": z.string().min(1).max(255), "myNumber": z.coerce.number().gte(1).lte(9999), "email": z.string().email().min(1).max(255), "securityEmails": z.boolean(), "dateOfBirth": z.string().datetime({ offset: true }), "notify": z.string(), "language": z.string(), "languageSelect": z.string() }).strict()
-
-const language = [
- { label: "English", value: "en" },
- { label: "Arabic", value: "ar" },
- { label: "Kurdish", value: "ku" },
-]
-export default function MyForm() {
- const form = useForm>({
- resolver: zodResolver(formSchema),
- defaultValues: {
-username: "",
-myNumber: 1,
-email: "",
-},
- })
-
- function onSubmit(values: z.infer) {
- console.log(values)
- }
-
- return (
-
-
- )
-}
diff --git a/apps/web/src/codegen/generate-code.ts b/apps/web/src/codegen/generate-code.ts
deleted file mode 100644
index 428380e..0000000
--- a/apps/web/src/codegen/generate-code.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import type { FormField, FormSchema } from "@/schema";
-import { $appState } from "@/state/state";
-import Handlebars from "handlebars";
-
-import { generateImports } from "./generateImports";
-import {
- booleanInputTemplate,
- comboboxInputTemplate,
- dateInputTemplate,
- mainTemplate,
- numberInputTemplate,
- radioInputTemplate,
- selectInputTemplate,
- stringInputTemplate,
- textareaInputTemplate,
-} from "./templates";
-import { formToZodSchema } from "./utils";
-
-registerPartials();
-
-Handlebars.registerHelper("ifEquals", function (arg1, arg2, options) {
- //@ts-ignore
- return arg1 === arg2 ? options.fn(this) : options.inverse(this);
-});
-Handlebars.registerHelper("ifNotEquals", function (arg1, arg2, options) {
- //@ts-ignore
- return arg1 !== arg2 ? options.fn(this) : options.inverse(this);
-});
-
-Handlebars.registerHelper("defaultValues", (fields) => {
- let output = "{\n";
- fields.forEach((field: FormField) => {
- if (field.kind === "string") {
- output += `${field.key}: "${field.defaultValue || ""}",\n`;
- } else if (field.kind === "number") {
- output += `${field.key}: ${field.defaultValue || 1},\n`;
- }
- });
- output += "}";
- return new Handlebars.SafeString(output);
-});
-
-const main = Handlebars.compile(mainTemplate);
-
-export function generateCode(form: FormSchema) {
- const zodFormSchema = formToZodSchema(form);
- const mainCode = main({ ...form, zodFormSchema });
-
- console.log("ff", form, "main", mainCode);
- const generatedCode =
- generateImports(
- $appState.get().forms[$appState.get().selectedForm].fields,
- ) + mainCode;
- return generatedCode;
-}
-
-function registerPartials() {
- Handlebars.registerPartial("numberInput", numberInputTemplate);
- Handlebars.registerPartial("dateInput", dateInputTemplate);
- Handlebars.registerPartial("booleanInput", booleanInputTemplate);
- Handlebars.registerPartial("stringInput", stringInputTemplate);
- Handlebars.registerPartial("radioInput", radioInputTemplate);
- Handlebars.registerPartial("selectInput", selectInputTemplate);
- Handlebars.registerPartial("comboboxInput", comboboxInputTemplate);
- Handlebars.registerPartial("textareaInput", textareaInputTemplate);
-}
diff --git a/apps/web/src/codegen/generateImports.ts b/apps/web/src/codegen/generateImports.ts
deleted file mode 100644
index 558155f..0000000
--- a/apps/web/src/codegen/generateImports.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import type { FormField } from "@/schema";
-import { getRequiredComponents } from "@/lib/utils";
-
-export function generateImports(fields: FormField[]) {
- const initialImports = `
-'use client'
-import { zodResolver } from "@hookform/resolvers/zod"
-import * as z from "zod"
-import { useForm } from "react-hook-form"
-import { Button } from "@/components/ui/button"
-import {
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form"
-import { Input } from "@/components/ui/input"
-`;
- const switchImport = `
-import { Switch } from "@/components/ui/switch"
-`;
- const dateImport = `
-import { format } from "date-fns"
-import { CalendarIcon } from "lucide-react"
-import { Calendar } from "@/components/ui/calendar"
-`;
- const selectImport = `
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
-`;
- const comboboxImport = `
-import { Check, ChevronsUpDown } from "lucide-react"
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
-} from "@/components/ui/command"
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover"
-`;
- const radioImport = `
-import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
-`;
- const textareaImport = `
-import { Textarea } from "@/components/ui/textarea"
-`;
-
- let imports = initialImports;
- for (const i of getRequiredComponents(fields)) {
- if (i === "date") imports += dateImport;
- if (i === "boolean") imports += switchImport;
- if (i === "radio-group") imports += radioImport;
- if (i === "select") imports += selectImport;
- if (i === "popover") imports += comboboxImport;
- if (i === "textarea") imports += textareaImport;
- // if (i === "command") imports += comboboxImport
- }
- return imports;
-}
diff --git a/apps/web/src/codegen/imports.ts b/apps/web/src/codegen/imports.ts
deleted file mode 100644
index 6fff1e2..0000000
--- a/apps/web/src/codegen/imports.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-export const imports = `"use client"
-
-import { Check, ChevronsUpDown } from "lucide-react"
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
-} from "@/components/ui/command"
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover"
-
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
-
-import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
-
-import { format } from "date-fns"
-import { CalendarIcon } from "lucide-react"
-import { Calendar } from "@/components/ui/calendar"
-
-import { Switch } from "@/components/ui/switch"
-
-//maybe?
-import { cn } from "@/lib/utils"
-
-import { zodResolver } from "@hookform/resolvers/zod"
-import * as z from "zod"
-import { useForm } from "react-hook-form"
-import { Button } from "@/components/ui/button"
-import {
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form"
-import { Input } from "@/components/ui/input"
-`;
diff --git a/apps/web/src/codegen/templates/boolean-input.ts b/apps/web/src/codegen/templates/boolean-input.ts
deleted file mode 100644
index 2bc79d4..0000000
--- a/apps/web/src/codegen/templates/boolean-input.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-export const booleanInputTemplate = `
- (
-
-
- {{label}}
-
-{{desc}}
-
-
-
-
-
-
- )}
- />
-`;
diff --git a/apps/web/src/codegen/templates/combobox-input.ts b/apps/web/src/codegen/templates/combobox-input.ts
deleted file mode 100644
index bbca489..0000000
--- a/apps/web/src/codegen/templates/combobox-input.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-export const comboboxInputTemplate = `
- (
-
- {{label}}
-
-
-
-
-
-
-
-
-
- No {{enumName}} found.
-
- { {{enumName}}.map((item) => (
- {
- form.setValue("{{key}}", item.value)
- }}
- >
-
- {item.label}
-
- ))}
-
-
-
-
-
- {{desc}}
-
-
-
- )}
- />
- `;
diff --git a/apps/web/src/codegen/templates/date-input.ts b/apps/web/src/codegen/templates/date-input.ts
deleted file mode 100644
index 8d6210e..0000000
--- a/apps/web/src/codegen/templates/date-input.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-export const dateInputTemplate = `
- (
-
- {{label}}
-
-
-
-
-
-
-
-
- // date > new Date() || date < new Date("1900-01-01")
- // }
- initialFocus
- />
-
-
-
-{{desc}}
-
-
-
- )}
- />
-`;
diff --git a/apps/web/src/codegen/templates/index.ts b/apps/web/src/codegen/templates/index.ts
deleted file mode 100644
index 4100df9..0000000
--- a/apps/web/src/codegen/templates/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export { mainTemplate } from "./main";
-export { stringInputTemplate } from "./string-input";
-export { dateInputTemplate } from "./date-input";
-export { booleanInputTemplate } from "./boolean-input";
-export { selectInputTemplate } from "./select-input";
-export { radioInputTemplate } from "./radio-input";
-export { comboboxInputTemplate } from "./combobox-input";
-export { numberInputTemplate } from "./number-input";
-export { textareaInputTemplate } from "./textarea-input";
diff --git a/apps/web/src/codegen/templates/main.ts b/apps/web/src/codegen/templates/main.ts
deleted file mode 100644
index f2f4fc7..0000000
--- a/apps/web/src/codegen/templates/main.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-export const mainTemplate = `const formSchema = {{{zodFormSchema}}}
-
- {{#each fields}}
- {{#ifEquals kind "enum"}}
- {{#ifEquals style "combobox"}}
-const {{enumName}} = [
- {{#each enumValues}}
- { label: "{{label}}", value: "{{value}}" },
- {{/each}}
-]
- {{/ifEquals}}
- {{/ifEquals}}
- {{/each}}
-export function MyForm() {
- const form = useForm>({
- resolver: zodResolver(formSchema),
- defaultValues: {{{defaultValues fields}}},
- })
-
- function onSubmit(values: z.infer) {
- console.log(values)
- }
-
- return (
-
-
- )
-}
-`;
diff --git a/apps/web/src/codegen/templates/number-input.ts b/apps/web/src/codegen/templates/number-input.ts
deleted file mode 100644
index 96b56b8..0000000
--- a/apps/web/src/codegen/templates/number-input.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export const numberInputTemplate = `
- (
-
- {{label}}
-
-
-
-
- {{desc}}
-
-
-
- )}
-/>
-`;
diff --git a/apps/web/src/codegen/templates/radio-input.ts b/apps/web/src/codegen/templates/radio-input.ts
deleted file mode 100644
index c51b30c..0000000
--- a/apps/web/src/codegen/templates/radio-input.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-export const radioInputTemplate = `
- (
-
- {{label}}
-
-
- {{#each enumValues}}
-
-
-
-
-
- {{label}}
-
-
- {{/each}}
-
-
-
- {{desc}}
-
-
-
- )}
- />
-`;
diff --git a/apps/web/src/codegen/templates/select-input.ts b/apps/web/src/codegen/templates/select-input.ts
deleted file mode 100644
index 336ce55..0000000
--- a/apps/web/src/codegen/templates/select-input.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-export const selectInputTemplate = `
- (
-
- {{label}}
-
-
- {{desc}}
-
-
-
- )}
- />`;
diff --git a/apps/web/src/codegen/templates/string-input.ts b/apps/web/src/codegen/templates/string-input.ts
deleted file mode 100644
index 750e06f..0000000
--- a/apps/web/src/codegen/templates/string-input.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-export const stringInputTemplate = `
- (
-
- {{label}}
-
- {{#ifEquals validation.format "email"}}
-
- {{else ifEquals validation.format "password"}}
-
- {{else}}
-
- {{/ifEquals}}
-
-
- {{desc}}
-
-
-
- )}
-/>
-`;
diff --git a/apps/web/src/codegen/templates/textarea-input.ts b/apps/web/src/codegen/templates/textarea-input.ts
deleted file mode 100644
index 0960ae1..0000000
--- a/apps/web/src/codegen/templates/textarea-input.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-export const textareaInputTemplate = `
- (
-
- {{label}}
-
-
-
-
- {{desc}}
-
-
-
- )}
-/>
-`;
diff --git a/apps/web/src/codegen/utils.ts b/apps/web/src/codegen/utils.ts
deleted file mode 100644
index 2455943..0000000
--- a/apps/web/src/codegen/utils.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import type { FormField, FormSchema } from "@/schema";
-import { parseSchema } from "json-schema-to-zod";
-
-export function formToZodSchema(form: FormSchema) {
- const jsonSchema = formToJsonSchema(form);
- const formSchema = jsonSchemaToZod(jsonSchema);
- return formSchema;
-}
-
-function jsonSchemaToZod(obj: any) {
- let schema = parseSchema(obj);
- const catchallPattern = /\.catchall\(z\.never\(\)\)/;
- schema = schema.replace(catchallPattern, "");
- schema = schema.replace(".number()", ".coerce.number()");
- schema = schema.replace(".string().datetime()", ".date()");
- return schema;
-}
-
-function formToJsonSchema(form: FormSchema) {
- const definitions: { [key: string]: any } = {};
- const properties: { [key: string]: any } = {};
- const required: string[] = [];
-
- form.fields.forEach((field: FormField) => {
- let property: any;
- if (field.kind === "string")
- property = {
- type: field.kind,
- minLength: field.validation?.min
- ? Number.parseInt(field.validation?.min.toString())
- : 1,
- maxLength: field.validation?.max
- ? Number.parseInt(field.validation?.max.toString())
- : 255,
- };
- else if (field.kind === "number")
- property = {
- type: field.kind,
- minimum: field.validation?.min,
- maximum: field.validation?.max,
- };
- else if (field.kind === "boolean")
- property = {
- type: field.kind,
- };
- else if (field.kind === "date")
- property = {
- type: "string",
- format: "date-time",
- };
- else if (field.kind === "enum")
- property = {
- type: "string",
- };
- else if (field.kind === "textarea")
- property = {
- type: "string",
- minLength: field.validation?.min,
- maxLength: field.validation?.max,
- };
- if (field.kind === "string" && field.validation?.format) {
- property.format = field.validation.format;
- }
- properties[field.key] = property;
- if (field.required) {
- required.push(field.key);
- }
- });
-
- definitions.form = {
- type: "object",
- properties,
- required,
- additionalProperties: false,
- // description: "My neat object schema",
- };
-
- // const schema = {
- // // $ref: `#/definitions/${form.id}`,
- // definitions,
- // $schema: "http://json-schema.org/draft-07/schema#",
- // }
-
- return definitions.form;
-}
diff --git a/apps/web/src/components/icons.tsx b/apps/web/src/components/icons.tsx
index 877645c..a43ff84 100644
--- a/apps/web/src/components/icons.tsx
+++ b/apps/web/src/components/icons.tsx
@@ -6,7 +6,7 @@ import {
type Icon as LucideIcon,
} from "lucide-react";
-export type Icon = LucideIcon;
+export type Icon = typeof LucideIcon;
export const Icons = {
sun: SunMedium,
diff --git a/apps/web/src/components/theme-provider.tsx b/apps/web/src/components/theme-provider.tsx
index 3029f07..7810b05 100644
--- a/apps/web/src/components/theme-provider.tsx
+++ b/apps/web/src/components/theme-provider.tsx
@@ -2,7 +2,7 @@
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
-import type { ThemeProviderProps } from "next-themes/dist/types";
+import type { ThemeProviderProps } from "next-themes/dist/index";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return {children};
diff --git a/apps/web/src/components/ui/accordion.tsx b/apps/web/src/components/ui/accordion.tsx
index 6dcd4ef..0d3b1e1 100644
--- a/apps/web/src/components/ui/accordion.tsx
+++ b/apps/web/src/components/ui/accordion.tsx
@@ -12,7 +12,7 @@ const AccordionItem = React.forwardRef<
>(({ className, ...props }, ref) => (
));
@@ -32,7 +32,7 @@ const AccordionTrigger = React.forwardRef<
{...props}
>
{children}
-
+ {/* */}
));
@@ -50,7 +50,7 @@ const AccordionContent = React.forwardRef<
)}
{...props}
>
- {children}
+ {children}
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
diff --git a/apps/web/src/components/ui/badge.tsx b/apps/web/src/components/ui/badge.tsx
new file mode 100644
index 0000000..340b549
--- /dev/null
+++ b/apps/web/src/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import type * as React from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ );
+}
+
+export { Badge, badgeVariants };
diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx
index b59108c..2b067fd 100644
--- a/apps/web/src/components/ui/button.tsx
+++ b/apps/web/src/components/ui/button.tsx
@@ -54,4 +54,4 @@ const Button = React.forwardRef(
);
Button.displayName = "Button";
-export { Button, buttonVariants };
+export { Button, buttonVariants };
\ No newline at end of file
diff --git a/apps/web/src/components/ui/calendar.tsx b/apps/web/src/components/ui/calendar.tsx
index 4ecf40a..71d8968 100644
--- a/apps/web/src/components/ui/calendar.tsx
+++ b/apps/web/src/components/ui/calendar.tsx
@@ -1,62 +1,68 @@
-import type * as React from "react";
-import { ChevronLeft, ChevronRight } from "lucide-react";
-import { DayPicker } from "react-day-picker";
+import type * as React from "react"
+import { ChevronLeft, ChevronRight } from "lucide-react"
+import { DayPicker } from "react-day-picker"
-import { cn } from "@/lib/utils";
-import { buttonVariants } from "@/components/ui/button";
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
-export type CalendarProps = React.ComponentProps;
+export type CalendarProps = React.ComponentProps
function Calendar({
- className,
- classNames,
- showOutsideDays = true,
- ...props
+ className,
+ classNames,
+ showOutsideDays = true,
+ ...props
}: CalendarProps) {
- return (
- ,
- IconRight: ({ ...props }) => ,
- }}
- {...props}
- />
- );
+ return (
+ (
+
+ ),
+ IconRight: ({ className, ...props }) => (
+
+ ),
+ }}
+ {...props}
+ />
+ )
}
-Calendar.displayName = "Calendar";
+Calendar.displayName = "Calendar"
-export { Calendar };
+export { Calendar }
diff --git a/apps/web/src/components/ui/scroll-area.tsx b/apps/web/src/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..188f139
--- /dev/null
+++ b/apps/web/src/components/ui/scroll-area.tsx
@@ -0,0 +1,46 @@
+import * as React from "react";
+import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
+
+import { cn } from "@/lib/utils";
+
+const ScrollArea = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+ {children}
+
+
+
+
+));
+ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
+
+const ScrollBar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, orientation = "vertical", ...props }, ref) => (
+
+
+
+));
+ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
+
+export { ScrollArea, ScrollBar };
diff --git a/apps/web/src/core/FormBuilder/CopyCodeDialog.tsx b/apps/web/src/core/FormBuilder/CopyCodeDialog.tsx
index 0d0734a..a8b3ed9 100644
--- a/apps/web/src/core/FormBuilder/CopyCodeDialog.tsx
+++ b/apps/web/src/core/FormBuilder/CopyCodeDialog.tsx
@@ -1,8 +1,8 @@
import { useState } from "react";
-import { generateCode } from "@/codegen/generate-code";
-import type { FormSchema } from "@/schema";
+import { generateCode } from "formbuilder-core";
+import type { FormSchema } from "formbuilder-core";
import { useAppState } from "@/state/state";
-import { checkDuplicates } from "@/utils/checkDuplicates";
+import { checkDuplicates } from "formbuilder-core";
import type { UseFormReturn } from "react-hook-form";
import { getRequiredComponents } from "@/lib/utils";
diff --git a/apps/web/src/core/FormBuilder/FieldKindComboBox.tsx b/apps/web/src/core/FormBuilder/FieldKindComboBox.tsx
index 0346c12..8919027 100644
--- a/apps/web/src/core/FormBuilder/FieldKindComboBox.tsx
+++ b/apps/web/src/core/FormBuilder/FieldKindComboBox.tsx
@@ -1,6 +1,6 @@
"use client";
-import type { FieldKind, FormSchema } from "@/schema";
+import type { FieldKind, FormSchema } from "formbuilder-core";
import { CommandList } from "cmdk";
import { useFormContext } from "react-hook-form";
import { AiOutlineCheck } from "react-icons/ai";
diff --git a/apps/web/src/core/FormBuilder/FormBuilder.tsx b/apps/web/src/core/FormBuilder/FormBuilder.tsx
index d4786d7..2b1ea2f 100644
--- a/apps/web/src/core/FormBuilder/FormBuilder.tsx
+++ b/apps/web/src/core/FormBuilder/FormBuilder.tsx
@@ -1,7 +1,7 @@
"use client";
import { useEffect } from "react";
-import { type FormSchema, formBuilderSchema } from "@/schema";
+import type { FormSchema } from "formbuilder-core";
import { useAppState } from "@/state/state";
import {
newBooleanField,
@@ -10,7 +10,7 @@ import {
newNumberField,
newStringField,
newTextAreaField,
-} from "@/utils/newField";
+} from "formbuilder-core";
import { zodResolver } from "@hookform/resolvers/zod";
import { useFieldArray, useForm } from "react-hook-form";
import type { z } from "zod";
@@ -25,35 +25,35 @@ import { FormTableBody } from "./FormTableBody";
export function FormBuilder() {
const { forms, selectedForm, updateFormFields } = useAppState();
- const form = useForm({
- resolver: zodResolver(formBuilderSchema),
- defaultValues: {
- name: forms[selectedForm]?.name || "",
- fields: forms[selectedForm]?.fields || [],
- },
- });
+ // const form = useForm({
+ // resolver: zodResolver(formBuilderSchema),
+ // defaultValues: {
+ // name: forms[selectedForm]?.name || "",
+ // fields: forms[selectedForm]?.fields || [],
+ // },
+ // });
// Important!, watching the form for any changes
- form.watch();
+ // form.watch();
// Thanks to react-hook-form, we can easily update the nested FieldArray
- const { append } = useFieldArray({
- control: form.control,
- name: "fields",
- });
+ // const { append } = useFieldArray({
+ // control: form.control,
+ // name: "fields",
+ // });
- function onSubmit(values: z.infer) {
- console.log("values", values);
- }
+ // function onSubmit(values: z.infer) {
+ // console.log("values", values);
+ // }
- useEffect(() => {
- form.setValue("name", forms[selectedForm].name);
- form.setValue("fields", forms[selectedForm].fields);
- }, [selectedForm]);
+ // useEffect(() => {
+ // form.setValue("name", forms[selectedForm].name);
+ // form.setValue("fields", forms[selectedForm].fields);
+ // }, [selectedForm]);
- useEffect(() => {
- updateFormFields(form.getValues("fields"));
- }, [form.getValues("fields")]);
+ // useEffect(() => {
+ // updateFormFields(form.getValues("fields"));
+ // }, [form.getValues("fields")]);
const tableHeaders = [
"Move",
@@ -68,7 +68,7 @@ export function FormBuilder() {
return (
-
-
+ */}
Add Field
-
+ {/*
-
+ */}
diff --git a/apps/web/src/core/FormBuilder/FormTableBody.tsx b/apps/web/src/core/FormBuilder/FormTableBody.tsx
index 76deb85..90aa82c 100644
--- a/apps/web/src/core/FormBuilder/FormTableBody.tsx
+++ b/apps/web/src/core/FormBuilder/FormTableBody.tsx
@@ -1,5 +1,5 @@
import React, { useState } from "react";
-import type { FormSchema } from "@/schema";
+import type { FormSchema } from "formbuilder-core";
import { useFieldArray, useFormContext } from "react-hook-form";
import { FiArrowDown, FiArrowUp, FiTrash } from "react-icons/fi";
import { HiChevronUpDown } from "react-icons/hi2";
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/BooleanFieldKind.tsx b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/BooleanFieldKind.tsx
index 7449aa6..9a46ffd 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/BooleanFieldKind.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/BooleanFieldKind.tsx
@@ -1,6 +1,6 @@
"use client";
-import type { FormSchema } from "@/schema";
+import type { FormSchema } from "formbuilder-core";
import { useFormContext } from "react-hook-form";
import {
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/DateFieldKind.tsx b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/DateFieldKind.tsx
index cfd1bb3..a6323f7 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/DateFieldKind.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/DateFieldKind.tsx
@@ -1,4 +1,4 @@
-import type { FormSchema } from "@/schema";
+import type { FormSchema } from "formbuilder-core";
import { useFormContext } from "react-hook-form";
import {
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumFieldKind.tsx b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumFieldKind.tsx
index 802d52b..39fadb1 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumFieldKind.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumFieldKind.tsx
@@ -1,4 +1,4 @@
-import type { EnumStyleValues, FormSchema } from "@/schema";
+import type { EnumStyleValues, FormSchema } from "formbuilder-core";
import { Check } from "lucide-react";
import { useFormContext } from "react-hook-form";
import { HiChevronUpDown } from "react-icons/hi2";
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumValues.tsx b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumValues.tsx
index d96c760..b1fece9 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumValues.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/EnumFieldKind/EnumValues.tsx
@@ -1,4 +1,4 @@
-import type { EnumValue, FormSchema } from "@/schema";
+import type { EnumValue, FormSchema } from "formbuilder-core";
import { useFieldArray, useFormContext } from "react-hook-form";
import { Button } from "@/components/ui/button";
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/NumberFieldKind.tsx b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/NumberFieldKind.tsx
index 77578aa..01ab86c 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/NumberFieldKind.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/NumberFieldKind.tsx
@@ -1,4 +1,4 @@
-import type { FormSchema } from "@/schema";
+import type { FormSchema } from "formbuilder-core";
import { useFormContext } from "react-hook-form";
import {
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/StringFieldKind.tsx b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/StringFieldKind.tsx
index 2530e74..c8d243c 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/StringFieldKind.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/StringFieldKind.tsx
@@ -1,4 +1,4 @@
-import type { FormSchema } from "@/schema";
+import type { FormSchema } from "formbuilder-core";
import { useFormContext } from "react-hook-form";
import {
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/TextAreaFieldKind.tsx b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/TextAreaFieldKind.tsx
index 397e8f8..7e7950c 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/TextAreaFieldKind.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/FieldKinds/TextAreaFieldKind.tsx
@@ -1,4 +1,4 @@
-import type { FormSchema } from "@/schema";
+import type { FormSchema } from "formbuilder-core";
import { useFormContext } from "react-hook-form";
import {
diff --git a/apps/web/src/core/FormBuilder/MoreInfo/MoreInfo.tsx b/apps/web/src/core/FormBuilder/MoreInfo/MoreInfo.tsx
index 4861274..5730599 100644
--- a/apps/web/src/core/FormBuilder/MoreInfo/MoreInfo.tsx
+++ b/apps/web/src/core/FormBuilder/MoreInfo/MoreInfo.tsx
@@ -1,5 +1,5 @@
import type React from "react";
-import type { FieldKind } from "@/schema";
+import type { FieldKind } from "formbuilder-core";
import { BooleanFieldKind } from "./FieldKinds/BooleanFieldKind";
import { DateFieldKind } from "./FieldKinds/DateFieldKind";
diff --git a/apps/web/src/core/FormBuilder/showCodeDialog.tsx b/apps/web/src/core/FormBuilder/showCodeDialog.tsx
index 0ada07d..c07db45 100644
--- a/apps/web/src/core/FormBuilder/showCodeDialog.tsx
+++ b/apps/web/src/core/FormBuilder/showCodeDialog.tsx
@@ -1,14 +1,11 @@
import type { Dispatch, SetStateAction } from "react";
-import { generateCode } from "@/codegen/generate-code";
-import type { FormSchema } from "@/schema";
-import { checkDuplicates } from "@/utils/checkDuplicates";
+import { checkDuplicates, generateCode, type FormSchema } from "formbuilder-core";
import { UseFormReturn, useFormContext } from "react-hook-form";
-import { useToast } from "@/components/ui/use-toast";
-
type ShowCodeDialogProps = {
setDialogOpen: Dispatch>;
setGeneratedCode: Dispatch>;
+ // biome-ignore lint/suspicious/noExplicitAny:
toast: any;
};
diff --git a/apps/web/src/core/FormList.tsx b/apps/web/src/core/FormList.tsx
index b9fcfca..c61c88e 100644
--- a/apps/web/src/core/FormList.tsx
+++ b/apps/web/src/core/FormList.tsx
@@ -11,8 +11,9 @@ export function FormList() {
{forms?.map((f, idx) => (
-
-