diff --git a/dms-api.md b/dms-api.md index d0ad944..ea20c79 100644 --- a/dms-api.md +++ b/dms-api.md @@ -10,7 +10,7 @@ ```json { "status": 200, - "message": "Hello from the backend", + "message": "Backend data sent successfully", "folders": [...] } ``` @@ -30,6 +30,8 @@ } ``` - **Required Fields**: cabinet_name, cabinet_sector +- **Validation**: + - Parent folder must exist (cabinet_parent_id must be valid) - **Response Example**: ```json { @@ -38,6 +40,25 @@ "folder": {...} } ``` +- **Error Responses**: + ```json + { + "status": 400, + "message": "cabinet_name and cabinet_sector are required" + } + ``` + ```json + { + "status": 400, + "message": "Parent folder does not exist" + } + ``` + ```json + { + "status": 400, + "message": "Body was not received" + } + ``` ### PATCH /api/dms/folder - **Description**: Updates an existing folder (rename or move) diff --git a/pages/dms/index.vue b/pages/dms/index.vue index 09d4153..623c8cc 100644 --- a/pages/dms/index.vue +++ b/pages/dms/index.vue @@ -100,128 +100,128 @@ const dropTarget = ref(null); // Enhanced hierarchical structure (Cabinet > Drawer > Folder structure) const documentStructure = ref([ - { - id: 'cabinet-1', - name: 'JKR Document Cabinet', - type: 'cabinet', - path: '/jkr-cabinet', - accessLevel: 'department', - itemCount: 234, - children: [ - { - id: 'drawer-1-1', - name: 'Administrative Drawer', - type: 'drawer', - path: '/jkr-cabinet/administrative', - accessLevel: 'department', - itemCount: 89, - children: [ - { - id: 'folder-1-1-1', - name: 'Personnel Files', - type: 'folder', - path: '/jkr-cabinet/administrative/personnel', - accessLevel: 'private', - itemCount: 45, - children: [] - }, - { - id: 'folder-1-1-2', - name: 'Budget Reports', - type: 'folder', - path: '/jkr-cabinet/administrative/budget', - accessLevel: 'department', - itemCount: 23, - children: [] - } - ] - }, - { - id: 'drawer-1-2', - name: 'Project Drawer', - type: 'drawer', - path: '/jkr-cabinet/projects', - accessLevel: 'department', - itemCount: 145, - children: [ - { - id: 'folder-1-2-1', - name: 'Highway Projects', - type: 'folder', - path: '/jkr-cabinet/projects/highway', - accessLevel: 'department', - itemCount: 67, - children: [] - }, - { - id: 'folder-1-2-2', - name: 'Building Projects', - type: 'folder', - path: '/jkr-cabinet/projects/building', - accessLevel: 'department', - itemCount: 78, - children: [] - } - ] - } - ] - }, - { - id: 'cabinet-2', - name: 'Public Documents Cabinet', - type: 'cabinet', - path: '/public-cabinet', - accessLevel: 'public', - itemCount: 156, - children: [ - { - id: 'drawer-2-1', - name: 'Forms & Applications', - type: 'drawer', - path: '/public-cabinet/forms', - accessLevel: 'public', - itemCount: 89, - children: [] - }, - { - id: 'drawer-2-2', - name: 'Public Announcements', - type: 'drawer', - path: '/public-cabinet/announcements', - accessLevel: 'public', - itemCount: 67, - children: [] - } - ] - }, - { - id: 'cabinet-3', - name: 'Personal Documents', - type: 'cabinet', - path: '/personal-cabinet', - accessLevel: 'personal', - itemCount: 45, - children: [ - { - id: 'drawer-3-1', - name: 'My Documents', - type: 'drawer', - path: '/personal-cabinet/my-docs', - accessLevel: 'personal', - itemCount: 25, - children: [] - }, - { - id: 'drawer-3-2', - name: 'Private Files', - type: 'drawer', - path: '/personal-cabinet/private', - accessLevel: 'private', - itemCount: 20, - children: [] - } - ] - } + // { + // id: 'cabinet-1', + // name: 'JKR Document Cabinet', + // type: 'cabinet', + // path: '/jkr-cabinet', + // accessLevel: 'department', + // itemCount: 234, + // children: [ + // { + // id: 'drawer-1-1', + // name: 'Administrative Drawer', + // type: 'drawer', + // path: '/jkr-cabinet/administrative', + // accessLevel: 'department', + // itemCount: 89, + // children: [ + // { + // id: 'folder-1-1-1', + // name: 'Personnel Files', + // type: 'folder', + // path: '/jkr-cabinet/administrative/personnel', + // accessLevel: 'private', + // itemCount: 45, + // children: [] + // }, + // { + // id: 'folder-1-1-2', + // name: 'Budget Reports', + // type: 'folder', + // path: '/jkr-cabinet/administrative/budget', + // accessLevel: 'department', + // itemCount: 23, + // children: [] + // } + // ] + // }, + // { + // id: 'drawer-1-2', + // name: 'Project Drawer', + // type: 'drawer', + // path: '/jkr-cabinet/projects', + // accessLevel: 'department', + // itemCount: 145, + // children: [ + // { + // id: 'folder-1-2-1', + // name: 'Highway Projects', + // type: 'folder', + // path: '/jkr-cabinet/projects/highway', + // accessLevel: 'department', + // itemCount: 67, + // children: [] + // }, + // { + // id: 'folder-1-2-2', + // name: 'Building Projects', + // type: 'folder', + // path: '/jkr-cabinet/projects/building', + // accessLevel: 'department', + // itemCount: 78, + // children: [] + // } + // ] + // } + // ] + // }, + // { + // id: 'cabinet-2', + // name: 'Public Documents Cabinet', + // type: 'cabinet', + // path: '/public-cabinet', + // accessLevel: 'public', + // itemCount: 156, + // children: [ + // { + // id: 'drawer-2-1', + // name: 'Forms & Applications', + // type: 'drawer', + // path: '/public-cabinet/forms', + // accessLevel: 'public', + // itemCount: 89, + // children: [] + // }, + // { + // id: 'drawer-2-2', + // name: 'Public Announcements', + // type: 'drawer', + // path: '/public-cabinet/announcements', + // accessLevel: 'public', + // itemCount: 67, + // children: [] + // } + // ] + // }, + // { + // id: 'cabinet-3', + // name: 'Personal Documents', + // type: 'cabinet', + // path: '/personal-cabinet', + // accessLevel: 'personal', + // itemCount: 45, + // children: [ + // { + // id: 'drawer-3-1', + // name: 'My Documents', + // type: 'drawer', + // path: '/personal-cabinet/my-docs', + // accessLevel: 'personal', + // itemCount: 25, + // children: [] + // }, + // { + // id: 'drawer-3-2', + // name: 'Private Files', + // type: 'drawer', + // path: '/personal-cabinet/private', + // accessLevel: 'private', + // itemCount: 20, + // children: [] + // } + // ] + // } ]); // Current folder contents (what's displayed in the main view) @@ -322,25 +322,85 @@ const getFileTypeColor = (fileName) => { return colorMap[extension] || colorMap.default; }; +// Function to build path from parent_id chain +const buildPathFromParentId = (item, allItems) => { + const pathSegments = []; + let currentItem = item; + + // Traverse up the parent chain until we reach a root item (null parent_id) + while (currentItem) { + // Add the current item's name to the start of the path + pathSegments.unshift(currentItem.name.toLowerCase().replace(/\s+/g, '-')); + + // If we've reached a root item (null parent_id), break the loop + if (!currentItem.parent_id) break; + + // Find the parent item + currentItem = allItems.find(i => i.id === currentItem.parent_id); + + // Break if we can't find the parent (shouldn't happen in a valid tree) + if (!currentItem) break; + } + + // Construct the final path with leading slash + return '/' + pathSegments.join('/'); +}; + +// Function to convert database records to frontend structure +const convertDbToFrontendStructure = (dbRecords) => { + // First pass: Create a map of all items with their paths + const itemsWithPaths = dbRecords.folders.map(record => ({ + id: record.cb_id, + name: record.cb_name, + type: record.cb_type || 'folder', + parent_id: record.cb_parent_id, + accessLevel: record.cb_access_level || 'public', + itemCount: record.item_count || 0, + children: [], + path: '' // Will be populated in the next step + })); + + // Second pass: Build paths for all items + itemsWithPaths.forEach(item => { + item.path = buildPathFromParentId(item, itemsWithPaths); + }); + + // Third pass: Build the tree structure + const rootItems = itemsWithPaths.filter(item => !item.parent_id); + + // Recursive function to build the tree + const buildTree = (items) => { + return items.map(item => { + const children = itemsWithPaths.filter(child => child.parent_id === item.id); + return { + ...item, + children: children.length > 0 ? buildTree(children) : [] + }; + }); + }; + + return buildTree(rootItems); +}; + const mapAPIToDocumentStructure = async () => { - const response = await fetch('/api/dms/folder'); - const APIData = await response.json(); + try { + const response = await fetch('/api/dms/folder'); + const dbRecords = await response.json(); - // const documentStructure.value = APIData.map(obj => { - // id: obj.cb_id, - // name: obj.cb_name, - // type: obj.cb_type || "folder", - // parent_id: obj.cb_parent_id, - // access_level: obj.cb_access_level || "public", - // itemCount: null, - // children: obj.children_count || null - // }) - - return documentStructure.value; -} + console.log(dbRecords); + + // Convert database records to frontend structure + documentStructure.value = convertDbToFrontendStructure(dbRecords); + + return documentStructure.value; + } catch (error) { + console.error('Error mapping API data:', error); + return []; + } +}; // Navigation functions -const buildBreadcrumbs = (path) => { +const buildBreadcrumbs = async (path) => { if (path === '/') { return [{ name: 'Root', path: '/', type: 'root' }]; } @@ -348,8 +408,10 @@ const buildBreadcrumbs = (path) => { const crumbs = [{ name: 'Root', path: '/', type: 'root' }]; const segments = path.split('/').filter(Boolean); - // const documentStructure.value = await mapAPIToDocumentStructure(); + documentStructure.value = await mapAPIToDocumentStructure(); + console.log(documentStructure.value); let itemsToSearch = documentStructure.value; + console.log(itemsToSearch); let currentPath = ''; for (const segment of segments) { @@ -456,7 +518,7 @@ const loadFolderContents = async (path) => { try { await new Promise(resolve => setTimeout(resolve, 200)); - const item = findItemByPath(path); + const item = findItemByPath(path, documentStructure.value); if (item && item.children) { currentFolderContents.value = item.children.map(child => ({ ...child, @@ -1031,7 +1093,7 @@ const getTabIconClasses = (tab) => { }; // Lifecycle hooks -onMounted(() => { +onMounted(async () => { checkMobileView(); // Add event listeners @@ -1039,6 +1101,9 @@ onMounted(() => { window.addEventListener('keydown', handleKeyboardShortcuts); document.addEventListener('click', handleGlobalClick); document.addEventListener('contextmenu', handleGlobalContextMenu); + + // Populate document structure + await mapAPIToDocumentStructure(); // Initialize navigation navigateToPath('/', false); diff --git a/server/api/dms/folder.get.js b/server/api/dms/folder.get.js index 3cbdabd..b6888db 100644 --- a/server/api/dms/folder.get.js +++ b/server/api/dms/folder.get.js @@ -3,9 +3,9 @@ import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); export default defineEventHandler( async (event) => { - console.log("This is a test for a GET request to the backend"); + console.log("GET request sent received from backend."); - const successMsg = "Hello from the backend"; + const successMsg = "Backend data sent successfully"; const folders = await prisma.cabinets.findMany(); diff --git a/server/api/dms/folder.post.js b/server/api/dms/folder.post.js index efa3082..a8f85ed 100644 --- a/server/api/dms/folder.post.js +++ b/server/api/dms/folder.post.js @@ -3,7 +3,7 @@ import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); export default defineEventHandler( async (event) => { - console.log("This is a test for a POST request to the backend"); + console.log("POST request received by backend."); // const successMsg = "Hello from the backend"; @@ -39,6 +39,19 @@ export default defineEventHandler( async (event) => { }; }; + const parentExists = await prisma.cabinets.findUnique({ + where: { + cb_id: body.cabinet_parent_id + } + }); + + if (!parentExists) { + return { + status: 400, + message: "Parent folder does not exist" + } + } + // Checked body data. const folderData = { cb_name: body.cabinet_name,