import { Router } from "express"; import { db } from "../../db/index"; import { z } from "zod"; import { sql, eq, and, asc } from "drizzle-orm"; const router = Router(); const createDocTypeSchema = z.object({ name: z.string().min(1), label: z.string().min(1), module: z.string().optional(), description: z.string().optional(), icon: z.string().default("FileText"), color: z.string().default("blue"), isSubmittable: z.boolean().default(false), isSingle: z.boolean().default(false), isTree: z.boolean().default(false), hasWebView: z.boolean().default(true), }); const createFieldSchema = z.object({ docTypeId: z.number(), fieldName: z.string().min(1), label: z.string().min(1), fieldType: z.string().min(1), options: z.string().optional(), defaultValue: z.string().optional(), mandatory: z.boolean().default(false), inListView: z.boolean().default(false), inFilter: z.boolean().default(false), sortOrder: z.number().default(0), section: z.string().optional(), placeholder: z.string().optional(), helpText: z.string().optional(), }); router.get("/doctypes", async (req, res) => { try { const result = await db.execute(sql` SELECT * FROM arc_doctypes WHERE status = 'active' ORDER BY module, label `); res.json(result.rows); } catch (error) { console.error("Error fetching doctypes:", error); res.status(500).json({ error: "Failed to fetch doctypes" }); } }); router.get("/doctypes/:id", async (req, res) => { try { const { id } = req.params; const result = await db.execute(sql` SELECT * FROM arc_doctypes WHERE id = ${parseInt(id)} `); if (result.rows.length === 0) { return res.status(404).json({ error: "DocType not found" }); } res.json(result.rows[0]); } catch (error) { console.error("Error fetching doctype:", error); res.status(500).json({ error: "Failed to fetch doctype" }); } }); router.post("/doctypes", async (req, res) => { try { const data = createDocTypeSchema.parse(req.body); const result = await db.execute(sql` INSERT INTO arc_doctypes (name, label, module, description, icon, color, is_submittable, is_single, is_tree, has_web_view) VALUES (${data.name}, ${data.label}, ${data.module || null}, ${data.description || null}, ${data.icon}, ${data.color}, ${data.isSubmittable}, ${data.isSingle}, ${data.isTree}, ${data.hasWebView}) RETURNING * `); res.json(result.rows[0]); } catch (error) { console.error("Error creating doctype:", error); res.status(500).json({ error: "Failed to create doctype" }); } }); router.put("/doctypes/:id", async (req, res) => { try { const { id } = req.params; const data = createDocTypeSchema.partial().parse(req.body); const result = await db.execute(sql` UPDATE arc_doctypes SET name = COALESCE(${data.name}, name), label = COALESCE(${data.label}, label), module = COALESCE(${data.module}, module), description = COALESCE(${data.description}, description), icon = COALESCE(${data.icon}, icon), color = COALESCE(${data.color}, color), updated_at = CURRENT_TIMESTAMP WHERE id = ${parseInt(id)} RETURNING * `); res.json(result.rows[0]); } catch (error) { console.error("Error updating doctype:", error); res.status(500).json({ error: "Failed to update doctype" }); } }); router.delete("/doctypes/:id", async (req, res) => { try { const { id } = req.params; await db.execute(sql` UPDATE arc_doctypes SET status = 'deleted' WHERE id = ${parseInt(id)} `); res.json({ success: true }); } catch (error) { console.error("Error deleting doctype:", error); res.status(500).json({ error: "Failed to delete doctype" }); } }); router.get("/doctypes/:id/fields", async (req, res) => { try { const { id } = req.params; const result = await db.execute(sql` SELECT * FROM arc_fields WHERE doctype_id = ${parseInt(id)} ORDER BY sort_order, id `); res.json(result.rows); } catch (error) { console.error("Error fetching fields:", error); res.status(500).json({ error: "Failed to fetch fields" }); } }); router.post("/fields", async (req, res) => { try { const data = createFieldSchema.parse(req.body); const result = await db.execute(sql` INSERT INTO arc_fields ( doctype_id, field_name, label, field_type, options, default_value, mandatory, in_list_view, in_filter, sort_order, section, placeholder, help_text ) VALUES ( ${data.docTypeId}, ${data.fieldName}, ${data.label}, ${data.fieldType}, ${data.options || null}, ${data.defaultValue || null}, ${data.mandatory}, ${data.inListView}, ${data.inFilter}, ${data.sortOrder}, ${data.section || null}, ${data.placeholder || null}, ${data.helpText || null} ) RETURNING * `); res.json(result.rows[0]); } catch (error) { console.error("Error creating field:", error); res.status(500).json({ error: "Failed to create field" }); } }); router.put("/fields/:id", async (req, res) => { try { const { id } = req.params; const data = createFieldSchema.partial().parse(req.body); const result = await db.execute(sql` UPDATE arc_fields SET field_name = COALESCE(${data.fieldName}, field_name), label = COALESCE(${data.label}, label), field_type = COALESCE(${data.fieldType}, field_type), options = COALESCE(${data.options}, options), mandatory = COALESCE(${data.mandatory}, mandatory), in_list_view = COALESCE(${data.inListView}, in_list_view), in_filter = COALESCE(${data.inFilter}, in_filter), sort_order = COALESCE(${data.sortOrder}, sort_order), section = COALESCE(${data.section}, section), placeholder = COALESCE(${data.placeholder}, placeholder), help_text = COALESCE(${data.helpText}, help_text) WHERE id = ${parseInt(id)} RETURNING * `); res.json(result.rows[0]); } catch (error) { console.error("Error updating field:", error); res.status(500).json({ error: "Failed to update field" }); } }); router.delete("/fields/:id", async (req, res) => { try { const { id } = req.params; await db.execute(sql`DELETE FROM arc_fields WHERE id = ${parseInt(id)}`); res.json({ success: true }); } catch (error) { console.error("Error deleting field:", error); res.status(500).json({ error: "Failed to delete field" }); } }); router.get("/doctypes/:name/schema", async (req, res) => { try { const { name } = req.params; const doctypeResult = await db.execute(sql` SELECT * FROM arc_doctypes WHERE name = ${name} AND status = 'active' `); if (doctypeResult.rows.length === 0) { return res.status(404).json({ error: "DocType not found" }); } const doctype = doctypeResult.rows[0] as any; const fieldsResult = await db.execute(sql` SELECT * FROM arc_fields WHERE doctype_id = ${doctype.id} ORDER BY sort_order, id `); res.json({ doctype, fields: fieldsResult.rows }); } catch (error) { console.error("Error fetching schema:", error); res.status(500).json({ error: "Failed to fetch schema" }); } }); router.get("/pages", async (req, res) => { try { const result = await db.execute(sql` SELECT p.*, d.label as doctype_label FROM arc_pages p LEFT JOIN arc_doctypes d ON p.doctype_id = d.id WHERE p.status = 'active' ORDER BY p.module, p.title `); res.json(result.rows); } catch (error) { console.error("Error fetching pages:", error); res.status(500).json({ error: "Failed to fetch pages" }); } }); router.post("/pages", async (req, res) => { try { const { name, title, route, pageType, docTypeId, icon, module, layout } = req.body; const result = await db.execute(sql` INSERT INTO arc_pages (name, title, route, page_type, doctype_id, icon, module, layout) VALUES (${name}, ${title}, ${route}, ${pageType || 'page'}, ${docTypeId || null}, ${icon || null}, ${module || null}, ${layout ? JSON.stringify(layout) : null}) RETURNING * `); res.json(result.rows[0]); } catch (error) { console.error("Error creating page:", error); res.status(500).json({ error: "Failed to create page" }); } }); router.get("/widgets", async (req, res) => { try { const result = await db.execute(sql` SELECT * FROM arc_widgets WHERE status = 'active' ORDER BY category, label `); res.json(result.rows); } catch (error) { console.error("Error fetching widgets:", error); res.status(500).json({ error: "Failed to fetch widgets" }); } }); router.get("/modules", async (req, res) => { try { const result = await db.execute(sql` SELECT DISTINCT module FROM arc_doctypes WHERE module IS NOT NULL AND status = 'active' ORDER BY module `); res.json(result.rows.map((r: any) => r.module)); } catch (error) { console.error("Error fetching modules:", error); res.status(500).json({ error: "Failed to fetch modules" }); } }); const toCamelCase = (row: any) => ({ id: row.id, name: row.name, description: row.description, nodes: row.nodes || [], status: row.status, createdBy: row.created_by, createdAt: row.created_at, updatedAt: row.updated_at }); router.get("/workflows", async (req, res) => { try { const result = await db.execute(sql` SELECT * FROM arc_workflows WHERE status != 'deleted' ORDER BY created_at DESC `); res.json(result.rows.map(toCamelCase)); } catch (error) { console.error("Error fetching workflows:", error); res.status(500).json({ error: "Failed to fetch workflows" }); } }); router.post("/workflows", async (req, res) => { try { const { name, description, nodes, status } = req.body; const result = await db.execute(sql` INSERT INTO arc_workflows (name, description, nodes, status, created_at, updated_at) VALUES (${name}, ${description}, ${JSON.stringify(nodes || [])}, ${status || 'draft'}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING * `); res.json(toCamelCase(result.rows[0])); } catch (error) { console.error("Error creating workflow:", error); res.status(500).json({ error: "Failed to create workflow" }); } }); router.put("/workflows/:id", async (req, res) => { try { const { id } = req.params; const { name, description, nodes, status } = req.body; const result = await db.execute(sql` UPDATE arc_workflows SET name = COALESCE(${name}, name), description = COALESCE(${description}, description), nodes = COALESCE(${nodes ? JSON.stringify(nodes) : null}, nodes), status = COALESCE(${status}, status), updated_at = CURRENT_TIMESTAMP WHERE id = ${parseInt(id)} RETURNING * `); res.json(toCamelCase(result.rows[0])); } catch (error) { console.error("Error updating workflow:", error); res.status(500).json({ error: "Failed to update workflow" }); } }); router.delete("/workflows/:id", async (req, res) => { try { const { id } = req.params; await db.execute(sql` UPDATE arc_workflows SET status = 'deleted' WHERE id = ${parseInt(id)} `); res.json({ success: true }); } catch (error) { console.error("Error deleting workflow:", error); res.status(500).json({ error: "Failed to delete workflow" }); } }); export default router;