-
-
-
account_tree
-
No process is currently open
-
-
-
-
- Create New Process
-
-
-
- Go to Process Management
-
+
+
+
+
+
+
+
You have unsaved changes that will be lost if you leave the page.
+
-
+
+
+
+ Stay on this Page
+
+
+ Discard Changes
+
+
+
+
\ No newline at end of file
diff --git a/prisma/json/json-schema.json b/prisma/json/json-schema.json
index d1dadc6..ff0d695 100644
--- a/prisma/json/json-schema.json
+++ b/prisma/json/json-schema.json
@@ -141,6 +141,24 @@
"items": {
"$ref": "#/definitions/userrole"
}
+ },
+ "processes": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/process"
+ }
+ },
+ "forms": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/form"
+ }
+ },
+ "assignedTasks": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/task"
+ }
}
}
},
@@ -265,6 +283,200 @@
"$ref": "#/definitions/user"
}
}
+ },
+ "form": {
+ "type": "object",
+ "properties": {
+ "formID": {
+ "type": "integer"
+ },
+ "formUUID": {
+ "type": "string"
+ },
+ "formName": {
+ "type": "string"
+ },
+ "formDescription": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "formComponents": {
+ "type": [
+ "number",
+ "string",
+ "boolean",
+ "object",
+ "array",
+ "null"
+ ]
+ },
+ "formStatus": {
+ "type": "string",
+ "default": "active"
+ },
+ "formCreatedDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "formModifiedDate": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "creator": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/user"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "formTasks": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/task"
+ }
+ }
+ }
+ },
+ "process": {
+ "type": "object",
+ "properties": {
+ "processID": {
+ "type": "integer"
+ },
+ "processUUID": {
+ "type": "string"
+ },
+ "processName": {
+ "type": "string"
+ },
+ "processDescription": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "processDefinition": {
+ "type": [
+ "number",
+ "string",
+ "boolean",
+ "object",
+ "array",
+ "null"
+ ]
+ },
+ "processVersion": {
+ "type": "integer",
+ "default": 1
+ },
+ "processStatus": {
+ "type": "string",
+ "default": "draft"
+ },
+ "processCreatedDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "processModifiedDate": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "creator": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/user"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "tasks": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/task"
+ }
+ }
+ }
+ },
+ "task": {
+ "type": "object",
+ "properties": {
+ "taskID": {
+ "type": "integer"
+ },
+ "taskUUID": {
+ "type": "string"
+ },
+ "taskNodeId": {
+ "type": "string"
+ },
+ "taskName": {
+ "type": "string"
+ },
+ "taskType": {
+ "type": "string"
+ },
+ "taskData": {
+ "type": [
+ "number",
+ "string",
+ "boolean",
+ "object",
+ "array",
+ "null"
+ ]
+ },
+ "taskStatus": {
+ "type": "string",
+ "default": "pending"
+ },
+ "taskCreatedDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "taskModifiedDate": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "process": {
+ "$ref": "#/definitions/process"
+ },
+ "form": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/form"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "assignee": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/user"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
}
},
"type": "object",
@@ -283,6 +495,15 @@
},
"userrole": {
"$ref": "#/definitions/userrole"
+ },
+ "form": {
+ "$ref": "#/definitions/form"
+ },
+ "process": {
+ "$ref": "#/definitions/process"
+ },
+ "task": {
+ "$ref": "#/definitions/task"
}
}
}
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 0ee0959..d9323cc 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -41,6 +41,9 @@ model user {
userModifiedDate DateTime? @db.DateTime(0)
audit audit[]
userrole userrole[]
+ processes process[] @relation("ProcessCreator")
+ forms form[] @relation("FormCreator")
+ assignedTasks task[] @relation("TaskAssignee")
}
model role {
@@ -76,3 +79,66 @@ model userrole {
@@index([userRoleRoleID], map: "FK_userrole_role")
@@index([userRoleUserID], map: "FK_userrole_user")
}
+
+// New models for Form Builder
+model form {
+ formID Int @id @default(autoincrement())
+ formUUID String @unique @db.VarChar(36)
+ formName String @db.VarChar(255)
+ formDescription String? @db.Text
+ formComponents Json @db.Json
+ formStatus String @default("active") @db.VarChar(50)
+ formCreatedBy Int?
+ formCreatedDate DateTime @default(now()) @db.DateTime(0)
+ formModifiedDate DateTime? @updatedAt @db.DateTime(0)
+
+ // Relations
+ creator user? @relation("FormCreator", fields: [formCreatedBy], references: [userID])
+ formTasks task[] @relation("FormTask")
+
+ @@index([formCreatedBy], map: "FK_form_creator")
+}
+
+// New models for Process Builder
+model process {
+ processID Int @id @default(autoincrement())
+ processUUID String @unique @db.VarChar(36)
+ processName String @db.VarChar(255)
+ processDescription String? @db.Text
+ processDefinition Json @db.Json
+ processVersion Int @default(1)
+ processStatus String @default("draft") @db.VarChar(50)
+ processCreatedBy Int?
+ processCreatedDate DateTime @default(now()) @db.DateTime(0)
+ processModifiedDate DateTime? @updatedAt @db.DateTime(0)
+
+ // Relations
+ creator user? @relation("ProcessCreator", fields: [processCreatedBy], references: [userID])
+ tasks task[]
+
+ @@index([processCreatedBy], map: "FK_process_creator")
+}
+
+model task {
+ taskID Int @id @default(autoincrement())
+ taskUUID String @unique @db.VarChar(36)
+ taskNodeId String @db.VarChar(255)
+ taskName String @db.VarChar(255)
+ taskType String @db.VarChar(50)
+ taskData Json? @db.Json
+ taskProcessId Int
+ taskFormId Int?
+ taskAssigneeId Int?
+ taskStatus String @default("pending") @db.VarChar(50)
+ taskCreatedDate DateTime @default(now()) @db.DateTime(0)
+ taskModifiedDate DateTime? @updatedAt @db.DateTime(0)
+
+ // Relations
+ process process @relation(fields: [taskProcessId], references: [processID])
+ form form? @relation("FormTask", fields: [taskFormId], references: [formID])
+ assignee user? @relation("TaskAssignee", fields: [taskAssigneeId], references: [userID])
+
+ @@index([taskProcessId], map: "FK_task_process")
+ @@index([taskFormId], map: "FK_task_form")
+ @@index([taskAssigneeId], map: "FK_task_assignee")
+}
diff --git a/server/api/forms/[id].delete.js b/server/api/forms/[id].delete.js
new file mode 100644
index 0000000..34b5127
--- /dev/null
+++ b/server/api/forms/[id].delete.js
@@ -0,0 +1,57 @@
+import { PrismaClient } from '@prisma/client';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ // Get the form ID from the route params
+ const id = event.context.params.id;
+
+ try {
+ // Try to delete by UUID first
+ let form;
+ try {
+ form = await prisma.form.delete({
+ where: { formUUID: id }
+ });
+ } catch (e) {
+ // If UUID not found, try numeric ID
+ if (!isNaN(parseInt(id))) {
+ form = await prisma.form.delete({
+ where: { formID: parseInt(id) }
+ });
+ } else {
+ throw e;
+ }
+ }
+
+ return {
+ success: true,
+ message: 'Form deleted successfully'
+ };
+ } catch (error) {
+ console.error(`Error deleting form ${id}:`, error);
+
+ // Handle specific errors
+ if (error.code === 'P2025') {
+ return {
+ success: false,
+ error: 'Form not found'
+ };
+ }
+
+ // Handle cases where the form has associated tasks
+ if (error.code === 'P2003') {
+ return {
+ success: false,
+ error: 'Cannot delete form because it is associated with one or more tasks'
+ };
+ }
+
+ return {
+ success: false,
+ error: 'Failed to delete form',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/forms/[id].get.js b/server/api/forms/[id].get.js
new file mode 100644
index 0000000..d7fa577
--- /dev/null
+++ b/server/api/forms/[id].get.js
@@ -0,0 +1,64 @@
+import { PrismaClient } from '@prisma/client';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ // Get the form ID from the route params
+ const id = event.context.params.id;
+
+ try {
+ // Try to find by UUID first
+ let form = await prisma.form.findUnique({
+ where: {
+ formUUID: id
+ },
+ include: {
+ creator: {
+ select: {
+ userID: true,
+ userFullName: true
+ }
+ }
+ }
+ });
+
+ // If not found, try to find by numeric ID
+ if (!form && !isNaN(parseInt(id))) {
+ form = await prisma.form.findUnique({
+ where: {
+ formID: parseInt(id)
+ },
+ include: {
+ creator: {
+ select: {
+ userID: true,
+ userFullName: true
+ }
+ }
+ }
+ });
+ }
+
+ // If form not found, return 404
+ if (!form) {
+ return {
+ success: false,
+ error: 'Form not found'
+ };
+ }
+
+ return {
+ success: true,
+ form
+ };
+ } catch (error) {
+ console.error(`Error fetching form ${id}:`, error);
+
+ return {
+ success: false,
+ error: 'Failed to fetch form',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/forms/[id].put.js b/server/api/forms/[id].put.js
new file mode 100644
index 0000000..b5752ec
--- /dev/null
+++ b/server/api/forms/[id].put.js
@@ -0,0 +1,78 @@
+import { PrismaClient } from '@prisma/client';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ // Get the form ID from the route params
+ const id = event.context.params.id;
+
+ try {
+ // Parse the request body
+ const body = await readBody(event);
+
+ // Validate required fields
+ if (!body.formName) {
+ return {
+ success: false,
+ error: 'Form name is required'
+ };
+ }
+
+ // Prepare update data
+ const updateData = {
+ formName: body.formName,
+ formComponents: body.components || [],
+ formModifiedDate: new Date()
+ };
+
+ // Add optional fields if provided
+ if (body.formDescription !== undefined) {
+ updateData.formDescription = body.formDescription;
+ }
+
+ if (body.status !== undefined) {
+ updateData.formStatus = body.status;
+ }
+
+ // Try to update by UUID first
+ let form;
+ try {
+ form = await prisma.form.update({
+ where: { formUUID: id },
+ data: updateData
+ });
+ } catch (e) {
+ // If UUID not found, try numeric ID
+ if (!isNaN(parseInt(id))) {
+ form = await prisma.form.update({
+ where: { formID: parseInt(id) },
+ data: updateData
+ });
+ } else {
+ throw e;
+ }
+ }
+
+ return {
+ success: true,
+ form
+ };
+ } catch (error) {
+ console.error(`Error updating form ${id}:`, error);
+
+ // Handle specific errors
+ if (error.code === 'P2025') {
+ return {
+ success: false,
+ error: 'Form not found'
+ };
+ }
+
+ return {
+ success: false,
+ error: 'Failed to update form',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/forms/create.post.js b/server/api/forms/create.post.js
new file mode 100644
index 0000000..3b6144c
--- /dev/null
+++ b/server/api/forms/create.post.js
@@ -0,0 +1,45 @@
+import { PrismaClient } from '@prisma/client';
+import { v4 as uuidv4 } from 'uuid';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ try {
+ // Parse the request body
+ const body = await readBody(event);
+
+ // Validate required fields
+ if (!body.formName) {
+ return {
+ success: false,
+ error: 'Form name is required'
+ };
+ }
+
+ // Create a new form in the database
+ const form = await prisma.form.create({
+ data: {
+ formUUID: uuidv4(),
+ formName: body.formName,
+ formDescription: body.formDescription || null,
+ formComponents: body.components || [],
+ formStatus: body.status || 'active',
+ formCreatedBy: body.createdBy || null // In a real app, this would come from the authenticated user
+ }
+ });
+
+ return {
+ success: true,
+ form
+ };
+ } catch (error) {
+ console.error('Error creating form:', error);
+
+ return {
+ success: false,
+ error: 'Failed to create form',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/forms/index.get.js b/server/api/forms/index.get.js
new file mode 100644
index 0000000..86a5d69
--- /dev/null
+++ b/server/api/forms/index.get.js
@@ -0,0 +1,47 @@
+import { PrismaClient } from '@prisma/client';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ try {
+ // Get all active forms
+ const forms = await prisma.form.findMany({
+ where: {
+ formStatus: 'active'
+ },
+ orderBy: {
+ formCreatedDate: 'desc'
+ },
+ select: {
+ formID: true,
+ formUUID: true,
+ formName: true,
+ formDescription: true,
+ formStatus: true,
+ formCreatedDate: true,
+ formModifiedDate: true,
+ // Don't include the full components data to keep response size small
+ creator: {
+ select: {
+ userID: true,
+ userFullName: true
+ }
+ }
+ }
+ });
+
+ return {
+ success: true,
+ forms
+ };
+ } catch (error) {
+ console.error('Error fetching forms:', error);
+
+ return {
+ success: false,
+ error: 'Failed to fetch forms',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/processes/[id].put.js b/server/api/processes/[id].put.js
new file mode 100644
index 0000000..94c59fd
--- /dev/null
+++ b/server/api/processes/[id].put.js
@@ -0,0 +1,85 @@
+import { PrismaClient } from '@prisma/client';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ // Get the process ID from the route params
+ const id = event.context.params.id;
+
+ try {
+ // Parse the request body
+ const body = await readBody(event);
+
+ // Validate required fields
+ if (!body.processName) {
+ return {
+ success: false,
+ error: 'Process name is required'
+ };
+ }
+
+ // Prepare update data
+ const updateData = {
+ processName: body.processName,
+ processModifiedDate: new Date()
+ };
+
+ // Add optional fields if provided
+ if (body.processDescription !== undefined) {
+ updateData.processDescription = body.processDescription;
+ }
+
+ if (body.definition !== undefined) {
+ updateData.processDefinition = body.definition;
+ }
+
+ if (body.processStatus !== undefined) {
+ updateData.processStatus = body.processStatus;
+ }
+
+ if (body.processVersion !== undefined) {
+ updateData.processVersion = body.processVersion;
+ }
+
+ // Try to update by UUID first
+ let process;
+ try {
+ process = await prisma.process.update({
+ where: { processUUID: id },
+ data: updateData
+ });
+ } catch (e) {
+ // If UUID not found, try numeric ID
+ if (!isNaN(parseInt(id))) {
+ process = await prisma.process.update({
+ where: { processID: parseInt(id) },
+ data: updateData
+ });
+ } else {
+ throw e;
+ }
+ }
+
+ return {
+ success: true,
+ process
+ };
+ } catch (error) {
+ console.error(`Error updating process ${id}:`, error);
+
+ // Handle specific errors
+ if (error.code === 'P2025') {
+ return {
+ success: false,
+ error: 'Process not found'
+ };
+ }
+
+ return {
+ success: false,
+ error: 'Failed to update process',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/processes/[id]/tasks/create.post.js b/server/api/processes/[id]/tasks/create.post.js
new file mode 100644
index 0000000..b96aff6
--- /dev/null
+++ b/server/api/processes/[id]/tasks/create.post.js
@@ -0,0 +1,71 @@
+import { PrismaClient } from '@prisma/client';
+import { v4 as uuidv4 } from 'uuid';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ // Get the process ID from the route params
+ const processId = event.context.params.id;
+
+ try {
+ // Parse the request body
+ const body = await readBody(event);
+
+ // Validate required fields
+ if (!body.taskName || !body.taskType || !body.taskNodeId) {
+ return {
+ success: false,
+ error: 'Task name, type, and node ID are required'
+ };
+ }
+
+ // Find the process
+ let process;
+
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(processId)) {
+ // If it looks like a UUID
+ process = await prisma.process.findUnique({
+ where: { processUUID: processId }
+ });
+ } else if (!isNaN(parseInt(processId))) {
+ // If it's a numeric ID
+ process = await prisma.process.findUnique({
+ where: { processID: parseInt(processId) }
+ });
+ }
+
+ if (!process) {
+ return {
+ success: false,
+ error: 'Process not found'
+ };
+ }
+
+ // Create a new task
+ const task = await prisma.task.create({
+ data: {
+ taskUUID: uuidv4(),
+ taskNodeId: body.taskNodeId,
+ taskName: body.taskName,
+ taskType: body.taskType,
+ taskData: body.taskData || {},
+ taskProcessId: process.processID,
+ taskAssigneeId: body.assigneeId || null
+ }
+ });
+
+ return {
+ success: true,
+ task
+ };
+ } catch (error) {
+ console.error(`Error creating task for process ${processId}:`, error);
+
+ return {
+ success: false,
+ error: 'Failed to create task',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/processes/create.post.js b/server/api/processes/create.post.js
new file mode 100644
index 0000000..b6d9e10
--- /dev/null
+++ b/server/api/processes/create.post.js
@@ -0,0 +1,46 @@
+import { PrismaClient } from '@prisma/client';
+import { v4 as uuidv4 } from 'uuid';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ try {
+ // Parse the request body
+ const body = await readBody(event);
+
+ // Validate required fields
+ if (!body.processName) {
+ return {
+ success: false,
+ error: 'Process name is required'
+ };
+ }
+
+ // Create a new process in the database
+ const process = await prisma.process.create({
+ data: {
+ processUUID: uuidv4(),
+ processName: body.processName,
+ processDescription: body.processDescription || null,
+ processDefinition: body.definition || { nodes: [], edges: [] },
+ processVersion: 1,
+ processStatus: body.status || 'draft',
+ processCreatedBy: body.createdBy || null // In a real app, this would come from the authenticated user
+ }
+ });
+
+ return {
+ success: true,
+ process
+ };
+ } catch (error) {
+ console.error('Error creating process:', error);
+
+ return {
+ success: false,
+ error: 'Failed to create process',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/tasks/[id]/form.delete.js b/server/api/tasks/[id]/form.delete.js
new file mode 100644
index 0000000..fdcb7c6
--- /dev/null
+++ b/server/api/tasks/[id]/form.delete.js
@@ -0,0 +1,63 @@
+import { PrismaClient } from '@prisma/client';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ // Get the task ID from the route params
+ const taskId = event.context.params.id;
+
+ try {
+ // Find the task
+ let task;
+
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(taskId)) {
+ // If it looks like a UUID
+ task = await prisma.task.findUnique({
+ where: { taskUUID: taskId }
+ });
+ } else if (!isNaN(parseInt(taskId))) {
+ // If it's a numeric ID
+ task = await prisma.task.findUnique({
+ where: { taskID: parseInt(taskId) }
+ });
+ }
+
+ if (!task) {
+ return {
+ success: false,
+ error: 'Task not found'
+ };
+ }
+
+ // Prepare task data without form information
+ let taskData = task.taskData || {};
+ delete taskData.formName;
+ delete taskData.formId;
+ delete taskData.formUuid;
+
+ // Unlink the form from the task
+ const updatedTask = await prisma.task.update({
+ where: {
+ taskID: task.taskID
+ },
+ data: {
+ taskFormId: null,
+ taskData: taskData
+ }
+ });
+
+ return {
+ success: true,
+ task: updatedTask
+ };
+ } catch (error) {
+ console.error(`Error removing form from task ${taskId}:`, error);
+
+ return {
+ success: false,
+ error: 'Failed to remove form from task',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/server/api/tasks/[id]/form.post.js b/server/api/tasks/[id]/form.post.js
new file mode 100644
index 0000000..ee74d6d
--- /dev/null
+++ b/server/api/tasks/[id]/form.post.js
@@ -0,0 +1,99 @@
+import { PrismaClient } from '@prisma/client';
+
+// Initialize Prisma client
+const prisma = new PrismaClient();
+
+export default defineEventHandler(async (event) => {
+ // Get the task ID from the route params
+ const taskId = event.context.params.id;
+
+ try {
+ // Parse the request body
+ const body = await readBody(event);
+
+ // Validate required fields
+ if (!body.formId) {
+ return {
+ success: false,
+ error: 'Form ID is required'
+ };
+ }
+
+ // Find the task
+ let task;
+
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(taskId)) {
+ // If it looks like a UUID
+ task = await prisma.task.findUnique({
+ where: { taskUUID: taskId }
+ });
+ } else if (!isNaN(parseInt(taskId))) {
+ // If it's a numeric ID
+ task = await prisma.task.findUnique({
+ where: { taskID: parseInt(taskId) }
+ });
+ }
+
+ if (!task) {
+ return {
+ success: false,
+ error: 'Task not found'
+ };
+ }
+
+ // Find the form
+ let form;
+ const formId = body.formId;
+
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(formId)) {
+ // If it looks like a UUID
+ form = await prisma.form.findUnique({
+ where: { formUUID: formId }
+ });
+ } else if (!isNaN(parseInt(formId))) {
+ // If it's a numeric ID
+ form = await prisma.form.findUnique({
+ where: { formID: parseInt(formId) }
+ });
+ }
+
+ if (!form) {
+ return {
+ success: false,
+ error: 'Form not found'
+ };
+ }
+
+ // Link the form to the task
+ const updatedTask = await prisma.task.update({
+ where: {
+ taskID: task.taskID
+ },
+ data: {
+ taskFormId: form.formID,
+ taskData: {
+ ...task.taskData,
+ formName: form.formName,
+ formId: form.formID,
+ formUuid: form.formUUID
+ }
+ },
+ include: {
+ form: true
+ }
+ });
+
+ return {
+ success: true,
+ task: updatedTask
+ };
+ } catch (error) {
+ console.error(`Error connecting form to task ${taskId}:`, error);
+
+ return {
+ success: false,
+ error: 'Failed to connect form to task',
+ details: process.env.NODE_ENV === 'development' ? error.message : undefined
+ };
+ }
+});
\ No newline at end of file
diff --git a/stores/formBuilder.js b/stores/formBuilder.js
index 0988c62..9f09c5c 100644
--- a/stores/formBuilder.js
+++ b/stores/formBuilder.js
@@ -12,7 +12,8 @@ export const useFormBuilderStore = defineStore('formBuilder', {
hasUnsavedChanges: false,
actionHistory: [],
currentHistoryIndex: -1,
- maxHistoryLength: 30 // Maximum number of history entries to keep
+ maxHistoryLength: 30, // Maximum number of history entries to keep
+ currentFormId: null
}),
getters: {
@@ -414,32 +415,184 @@ export const useFormBuilderStore = defineStore('formBuilder', {
this.hasUnsavedChanges = false;
},
- saveForm() {
- const formData = this.formConfig;
-
- // Add to saved forms array
- const existingIndex = this.savedForms.findIndex(f => f.id === formData.id);
- if (existingIndex !== -1) {
- this.savedForms[existingIndex] = formData;
- } else {
- this.savedForms.push(formData);
+ // Get forms from the backend
+ async getForms() {
+ try {
+ // Use the API endpoint to fetch forms
+ const response = await fetch('/api/forms');
+ const result = await response.json();
+
+ if (result.success && Array.isArray(result.forms)) {
+ return result.forms;
+ } else {
+ console.error('Error in API response:', result.error || 'Unknown error');
+ return [];
+ }
+ } catch (error) {
+ console.error('Error fetching forms:', error);
+ return [];
}
-
- // Save to localStorage for persistence
- localStorage.setItem('savedForms', JSON.stringify(this.savedForms));
-
- this.hasUnsavedChanges = false;
-
- // Record in history
- this.recordHistory('save_form', {
- formName: this.formName,
- formDescription: this.formDescription,
- componentCount: this.formComponents.length
- });
-
- return formData;
},
+ // Load saved forms from the API
+ async loadSavedForms() {
+ try {
+ // Fetch forms from the API
+ const forms = await this.getForms();
+
+ // Transform to the format expected by the UI
+ this.savedForms = forms.map(form => ({
+ id: form.formUUID,
+ name: form.formName,
+ description: form.formDescription || '',
+ components: form.formComponents || [],
+ createdAt: form.formCreatedDate,
+ updatedAt: form.formModifiedDate
+ }));
+
+ return this.savedForms;
+ } catch (error) {
+ console.error('Error loading saved forms:', error);
+ this.savedForms = [];
+ return [];
+ }
+ },
+
+ // Save form to the database
+ async saveForm() {
+ try {
+ const formData = {
+ formName: this.formName,
+ formDescription: this.formDescription,
+ components: this.formComponents.map(c => ({
+ type: c.type,
+ props: c.props
+ }))
+ };
+
+ // Determine if this is a new form or an update
+ const isNewForm = !this.currentFormId;
+ let response;
+
+ if (isNewForm) {
+ // Create a new form
+ response = await fetch('/api/forms/create', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(formData)
+ });
+ } else {
+ // Update existing form
+ response = await fetch(`/api/forms/${this.currentFormId}`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(formData)
+ });
+ }
+
+ const result = await response.json();
+
+ if (result.success) {
+ // Update store state with the saved form
+ this.currentFormId = result.form.formUUID;
+ this.hasUnsavedChanges = false;
+
+ // Record in history
+ this.recordHistory('save_form', {
+ formName: this.formName,
+ formDescription: this.formDescription,
+ componentCount: this.formComponents.length
+ });
+
+ return result.form;
+ } else {
+ throw new Error(result.error || 'Failed to save form');
+ }
+ } catch (error) {
+ console.error('Error saving form:', error);
+ throw error;
+ }
+ },
+
+ // Load a form from the database
+ async loadForm(formId) {
+ if (!formId) {
+ throw new Error('Form ID is required');
+ }
+
+ try {
+ const response = await fetch(`/api/forms/${formId}`);
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(errorData.error || `HTTP error ${response.status}`);
+ }
+
+ const result = await response.json();
+
+ if (result.success && result.form) {
+ // Clear existing data
+ this.formComponents = [];
+ this.selectedComponentId = null;
+
+ // Set form data
+ this.formName = result.form.formName;
+ this.formDescription = result.form.formDescription || '';
+ this.currentFormId = result.form.formUUID;
+
+ // Transform components from DB format to store format
+ if (Array.isArray(result.form.formComponents)) {
+ this.formComponents = result.form.formComponents.map(c => ({
+ ...c,
+ id: uuidv4() // Assign a new UUID for each component
+ }));
+ }
+
+ // Clear and initialize history when loading a form
+ this.actionHistory = [];
+ this.currentHistoryIndex = -1;
+
+ // Record initial state in history
+ this.recordHistory('load_form', {
+ formName: result.form.formName,
+ formId: formId
+ });
+
+ return result.form;
+ } else {
+ throw new Error(result.error || 'Failed to load form');
+ }
+ } catch (error) {
+ console.error(`Error loading form ${formId}:`, error);
+ throw error;
+ }
+ },
+
+ // Delete a form from the database
+ async deleteForm(formId) {
+ try {
+ const response = await fetch(`/api/forms/${formId}`, {
+ method: 'DELETE'
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ return true;
+ } else {
+ throw new Error(result.error || 'Failed to delete form');
+ }
+ } catch (error) {
+ console.error(`Error deleting form ${formId}:`, error);
+ throw error;
+ }
+ },
+
+ // Clear the current form
clearForm() {
// Capture the current state before clearing
const oldComponents = [...this.formComponents];
@@ -451,6 +604,7 @@ export const useFormBuilderStore = defineStore('formBuilder', {
this.selectedComponentId = null;
this.formName = 'New Form';
this.formDescription = '';
+ this.currentFormId = null;
this.hasUnsavedChanges = false;
// Clear history when starting a new form and add initial state
@@ -463,40 +617,6 @@ export const useFormBuilderStore = defineStore('formBuilder', {
});
},
- loadForm(formId) {
- const savedForms = JSON.parse(localStorage.getItem('savedForms') || '[]');
- const form = savedForms.find(f => f.id === formId);
-
- if (form) {
- // Clear existing data
- this.formComponents = [];
- this.selectedComponentId = null;
-
- // Set form data
- this.formName = form.name;
- this.formDescription = form.description;
- this.formComponents = form.components.map(c => ({
- ...c,
- id: uuidv4()
- }));
-
- // Clear and initialize history when loading a form
- this.actionHistory = [];
- this.currentHistoryIndex = -1;
-
- // Record initial state in history
- this.recordHistory('load_form', {
- formName: form.name,
- formId: formId
- });
- }
- },
-
- loadSavedForms() {
- const savedForms = JSON.parse(localStorage.getItem('savedForms') || '[]');
- this.savedForms = savedForms;
- },
-
// Optimize the grid layout by analyzing the current components
// and adjusting their sizes to fill available spaces
optimizeGridLayout() {
diff --git a/stores/processBuilder.js b/stores/processBuilder.js
index fa398b2..21c0264 100644
--- a/stores/processBuilder.js
+++ b/stores/processBuilder.js
@@ -181,22 +181,36 @@ export const useProcessBuilderStore = defineStore('processBuilder', {
deleteNode(nodeId) {
if (!this.currentProcess) return;
+ // Find the node index
const index = this.currentProcess.nodes.findIndex(n => n.id === nodeId);
if (index !== -1) {
+ // Remove the node
this.currentProcess.nodes.splice(index, 1);
// Remove any edges connected to this node
- this.currentProcess.edges = this.currentProcess.edges.filter(
- edge => edge.source !== nodeId && edge.target !== nodeId
+ const edgesToRemove = this.currentProcess.edges.filter(
+ edge => edge.source === nodeId || edge.target === nodeId
);
+
+ edgesToRemove.forEach(edge => {
+ const edgeIndex = this.currentProcess.edges.findIndex(e => e.id === edge.id);
+ if (edgeIndex !== -1) {
+ this.currentProcess.edges.splice(edgeIndex, 1);
+ }
+ });
+ // Clear selection if the deleted node was selected
if (this.selectedNodeId === nodeId) {
this.selectedNodeId = null;
}
this.saveToHistory('Delete node');
this.unsavedChanges = true;
+
+ return true; // Return success
}
+
+ return false; // Return failure
},
/**
@@ -226,12 +240,35 @@ export const useProcessBuilderStore = defineStore('processBuilder', {
/**
* Update an edge in the current process
*/
- updateEdge(edgeId, updates) {
+ updateEdge(edgeIdOrObject, updates) {
if (!this.currentProcess) return;
+ // Handle different parameter formats
+ let edgeId, edgeUpdates;
+
+ if (typeof edgeIdOrObject === 'string') {
+ // Called with (id, updates)
+ edgeId = edgeIdOrObject;
+ edgeUpdates = updates || {};
+ } else if (typeof edgeIdOrObject === 'object') {
+ // Called with an edge object
+ edgeId = edgeIdOrObject.id;
+
+ if (updates) {
+ // Called with (edge, updates)
+ edgeUpdates = updates;
+ } else {
+ // Called with just the edge object containing updates
+ edgeUpdates = { ...edgeIdOrObject };
+ delete edgeUpdates.id; // Don't update the ID
+ }
+ } else {
+ return; // Invalid parameters
+ }
+
const edge = this.currentProcess.edges.find(e => e.id === edgeId);
if (edge) {
- Object.assign(edge, updates);
+ Object.assign(edge, edgeUpdates);
this.saveToHistory('Update edge');
this.unsavedChanges = true;
}