import { createClientFromRequest } from 'npm:@base44/sdk@0.8.31'; Deno.serve(async (req) => { try { const base44 = createClientFromRequest(req); const user = await base44.auth.me(); if (!user) return Response.json({ error: 'Unauthorized' }, { status: 401 }); const body = await req.json(); const { query_type, entity_id, entity_type, depth = 2 } = body; const entities = base44.asServiceRole.entities; const graph = { nodes: [], edges: [] }; const visited = new Set(); async function addNode(type, id, label, data = {}) { const key = `${type}:${id}`; if (visited.has(key)) return; visited.add(key); graph.nodes.push({ type, id, label, data }); } function edge(from, to, label) { graph.edges.push({ from, to, label }); } // ─── CONTRACT ─────────────────────────────────────────────── // Contract → Vendor, Client, Sector, RateCard async function traverseContract(contractId) { const contract = await entities.Contract.get(contractId); if (!contract) return; await addNode('Contract', contract.id, `Contract: ${contract.contract_name || contract.contract_number || ''}`, contract); if (contract.vendor_id) { await addNode('Vendor', contract.vendor_id, `Vendor: ${contract.vendor_name || ''}`); edge(`Contract:${contract.id}`, `Vendor:${contract.vendor_id}`, 'vendor_party'); } if (contract.client_id) { await addNode('Client', contract.client_id, `Client: ${contract.client_name || ''}`); edge(`Contract:${contract.id}`, `Client:${contract.client_id}`, 'client_party'); } if (contract.sector_id) { await addNode('Sector', contract.sector_id, `Sector: ${contract.sector_name || ''}`); edge(`Contract:${contract.id}`, `Sector:${contract.sector_id}`, 'governs_sector'); } if (contract.rate_card_id) { await addNode('RateCard', contract.rate_card_id, `RateCard`); edge(`Contract:${contract.id}`, `RateCard:${contract.rate_card_id}`, 'defines_rates'); } } // ─── TRANSPORTATION ──────────────────────────────────────── // Transportation → Shift, Worker, Vendor, Site, Client async function traverseTransportation(transportId) { const transport = await entities.Transportation.get(transportId); if (!transport) return; await addNode('Transportation', transport.id, `Transport: ${transport.transport_type} → ${transport.dropoff_location || ''}`, transport); if (transport.shift_id) { await addNode('Shift', transport.shift_id, `Shift`); edge(`Transportation:${transport.id}`, `Shift:${transport.shift_id}`, 'serves_shift'); } if (transport.workforce_id) { await addNode('Worker', transport.workforce_id, `Worker: ${transport.workforce_name || ''}`); edge(`Transportation:${transport.id}`, `Worker:${transport.workforce_id}`, 'transports_worker'); } if (transport.vendor_id) { await addNode('Vendor', transport.vendor_id, `Vendor: ${transport.vendor_name || ''}`); edge(`Transportation:${transport.id}`, `Vendor:${transport.vendor_id}`, 'arranged_by'); } if (transport.client_id) { await addNode('Client', transport.client_id, `Client: ${transport.client_name || ''}`); edge(`Transportation:${transport.id}`, `Client:${transport.client_id}`, 'destination_client'); } } // ─── EQUIPMENT ───────────────────────────────────────────── // Equipment → Vendor, Client, Site, Shift, Worker async function traverseEquipment(equipmentId) { const equipment = await entities.Equipment.get(equipmentId); if (!equipment) return; await addNode('Equipment', equipment.id, `Equipment: ${equipment.equipment_name || ''} (${equipment.category})`, equipment); if (equipment.vendor_id) { await addNode('Vendor', equipment.vendor_id, `Vendor: ${equipment.vendor_name || ''}`); edge(`Equipment:${equipment.id}`, `Vendor:${equipment.vendor_id}`, 'owned_by'); } if (equipment.client_id) { await addNode('Client', equipment.client_id, `Client: ${equipment.client_name || ''}`); edge(`Equipment:${equipment.id}`, `Client:${equipment.client_id}`, 'assigned_to_client'); } if (equipment.shift_id) { await addNode('Shift', equipment.shift_id, `Shift`); edge(`Equipment:${equipment.id}`, `Shift:${equipment.shift_id}`, 'used_in_shift'); } if (equipment.assigned_to_worker_id) { await addNode('Worker', equipment.assigned_to_worker_id, `Worker: ${equipment.assigned_to_worker_name || ''}`); edge(`Equipment:${equipment.id}`, `Worker:${equipment.assigned_to_worker_id}`, 'assigned_to_worker'); } } // ─── TRAINING ────────────────────────────────────────────── // Training → Worker, Vendor, Client, Certification async function traverseTraining(trainingId) { const training = await entities.Training.get(trainingId); if (!training) return; await addNode('Training', training.id, `Training: ${training.training_name || ''}`, training); if (training.workforce_id) { await addNode('Worker', training.workforce_id, `Worker: ${training.workforce_name || ''}`); edge(`Training:${training.id}`, `Worker:${training.workforce_id}`, 'enrolled_worker'); } if (training.vendor_id) { await addNode('Vendor', training.vendor_id, `Vendor: ${training.vendor_name || ''}`); edge(`Training:${training.id}`, `Vendor:${training.vendor_id}`, 'sponsored_by'); } if (training.client_id) { await addNode('Client', training.client_id, `Client: ${training.client_name || ''}`); edge(`Training:${training.id}`, `Client:${training.client_id}`, 'client_specific'); } if (training.certification_id) { await addNode('Certification', training.certification_id, `Cert: ${training.certification_earned || ''}`); edge(`Training:${training.id}`, `Certification:${training.certification_id}`, 'issues_certification'); } } // ─── FOOD SERVICE OPERATION ──────────────────────────────── // FoodServiceOperation → Client, Site, Sector, Vendor, Manager, Shift, Event, Contract, Invoice, Equipment async function traverseFoodServiceOperation(opId) { const op = await entities.FoodServiceOperation.get(opId); if (!op) return; await addNode('FoodServiceOperation', op.id, `FSO: ${op.operation_name || ''}`, op); if (op.client_id) { await addNode('Client', op.client_id, `Client: ${op.client_name || ''}`); edge(`FoodServiceOperation:${op.id}`, `Client:${op.client_id}`, 'serves_client'); } if (op.vendor_id) { await addNode('Vendor', op.vendor_id, `Vendor: ${op.vendor_name || ''}`); edge(`FoodServiceOperation:${op.id}`, `Vendor:${op.vendor_id}`, 'operated_by'); } if (op.manager_id) { await addNode('Manager', op.manager_id, `Manager: ${op.manager_name || ''}`); edge(`FoodServiceOperation:${op.id}`, `Manager:${op.manager_id}`, 'managed_by'); } if (op.shift_id) { await addNode('Shift', op.shift_id, `Shift`); edge(`FoodServiceOperation:${op.id}`, `Shift:${op.shift_id}`, 'runs_during_shift'); } if (op.contract_id) { await addNode('Contract', op.contract_id, `Contract`); edge(`FoodServiceOperation:${op.id}`, `Contract:${op.contract_id}`, 'governed_by_contract'); } if (op.invoice_id) { await addNode('Invoice', op.invoice_id, `Invoice`); edge(`FoodServiceOperation:${op.id}`, `Invoice:${op.invoice_id}`, 'billed_via'); } if (op.equipment_assigned) { for (const eq of op.equipment_assigned) { if (eq.equipment_id) { await addNode('Equipment', eq.equipment_id, `Equipment: ${eq.equipment_name || ''}`); edge(`FoodServiceOperation:${op.id}`, `Equipment:${eq.equipment_id}`, 'uses_equipment'); } } } } // ─── SHIFT (extended) ────────────────────────────────────── // Shift → Manager, Location, Event, Transportation, Equipment, FoodServiceOperation async function traverseShift(shiftId) { const shift = await entities.Shift.get(shiftId); if (!shift) return; await addNode('Shift', shift.id, `Shift: ${shift.shift_name || ''}`, shift); if (shift.manager_id) { await addNode('Manager', shift.manager_id, `Manager: ${shift.manager_name || ''}`); edge(`Shift:${shift.id}`, `Manager:${shift.manager_id}`, 'managed_by'); } if (shift.event_id) { await addNode('Event', shift.event_id, `Event`); edge(`Shift:${shift.id}`, `Event:${shift.event_id}`, 'part_of_event'); } // Transportation serving this shift const transports = await entities.Transportation.filter({ shift_id: shiftId }, '-created_date', 10); for (const t of transports) { await addNode('Transportation', t.id, `Transport: ${t.transport_type}`, t); edge(`Shift:${shiftId}`, `Transportation:${t.id}`, 'worker_transport'); } // Equipment assigned to this shift const equipment = await entities.Equipment.filter({ shift_id: shiftId }, '-created_date', 10); for (const e of equipment) { await addNode('Equipment', e.id, `Equipment: ${e.equipment_name || ''}`, e); edge(`Shift:${shiftId}`, `Equipment:${e.id}`, 'requires_equipment'); } // Food service operations running during this shift const fsos = await entities.FoodServiceOperation.filter({ shift_id: shiftId }, '-created_date', 10); for (const fso of fsos) { await addNode('FoodServiceOperation', fso.id, `FSO: ${fso.operation_name || ''}`, fso); edge(`Shift:${shiftId}`, `FoodServiceOperation:${fso.id}`, 'runs_operation'); } } // ─── ASSIGNMENT (existing, extended) ─────────────────────── async function traverseAssignment(assignmentId) { const assignment = await entities.Assignment.get(assignmentId); if (!assignment) return; await addNode('Assignment', assignment.id, `Assignment: ${assignment.workforce_name || 'Unknown'}`, assignment); if (assignment.order_id) { await addNode('Order', assignment.order_id, `Order: ${assignment.order_number || ''}`); edge(`Assignment:${assignment.id}`, `Order:${assignment.order_id}`, 'belongs_to'); await traverseOrder(assignment.order_id, 1); } if (assignment.workforce_id) { await addNode('Worker', assignment.workforce_id, `Worker: ${assignment.workforce_name || ''}`); edge(`Assignment:${assignment.id}`, `Worker:${assignment.workforce_id}`, 'assigned_to'); } if (assignment.vendor_id) { await addNode('Vendor', assignment.vendor_id, `Vendor: ${assignment.vendor_name || ''}`); edge(`Assignment:${assignment.id}`, `Vendor:${assignment.vendor_id}`, 'provided_by'); } } // ─── ORDER (existing, extended) ──────────────────────────── async function traverseOrder(orderId, currentDepth = 0) { if (currentDepth >= depth) return; try { const order = await entities.Order.get(orderId); if (!order) return; await addNode('Order', order.id, `Order: ${order.order_number || order.event_name || ''}`, order); if (order.partner_id) { await addNode('Partner', order.partner_id, `Partner: ${order.partner_name || ''}`); edge(`Order:${order.id}`, `Partner:${order.partner_id}`, 'for_client'); } if (order.vendor_id) { await addNode('Vendor', order.vendor_id, `Vendor: ${order.vendor_name || ''}`); edge(`Order:${order.id}`, `Vendor:${order.vendor_id}`, 'assigned_to'); } // Contracts governing this client-vendor pair if (order.vendor_id && order.business_id) { const contracts = await entities.Contract.filter({ vendor_id: order.vendor_id, client_id: order.business_id, status: 'active' }, '-created_date', 5); for (const c of contracts) { await addNode('Contract', c.id, `Contract: ${c.contract_name || ''}`, c); edge(`Order:${order.id}`, `Contract:${c.id}`, 'governed_by'); } } // Food service operations for this event if (order.event_id) { const fsos = await entities.FoodServiceOperation.filter({ event_id: order.event_id }, '-created_date', 10); for (const fso of fsos) { await addNode('FoodServiceOperation', fso.id, `FSO: ${fso.operation_name || ''}`, fso); edge(`Order:${order.id}`, `FoodServiceOperation:${fso.id}`, 'fulfills_operation'); } } } catch (e) { /* order might not exist */ } } // ─── VENDOR (existing, extended) ─────────────────────────── async function traverseVendor(vendorId) { const vendor = await entities.Vendor.get(vendorId); if (!vendor) return; await addNode('Vendor', vendor.id, `Vendor: ${vendor.legal_name || vendor.doing_business_as || ''}`, vendor); // Rates const rates = await entities.VendorRate.filter({ vendor_id: vendorId }, '-created_date', 10); for (const rate of rates) { await addNode('VendorRate', rate.id, `Rate: ${rate.role_name || 'Unknown'} - $${rate.client_rate || 0}/hr`, rate); edge(`Vendor:${vendorId}`, `VendorRate:${rate.id}`, 'has_rate'); } // Workforce const workforce = await entities.Workforce.filter({ vendor_id: vendorId }, '-created_date', 10); for (const w of workforce) { await addNode('Worker', w.id, `Worker: ${w.full_name || w.name || ''}`, w); edge(`Vendor:${vendorId}`, `Worker:${w.id}`, 'employs'); } // Contracts const contracts = await entities.Contract.filter({ vendor_id: vendorId }, '-created_date', 10); for (const c of contracts) { await addNode('Contract', c.id, `Contract: ${c.contract_name || ''}`, c); edge(`Vendor:${vendorId}`, `Contract:${c.id}`, 'party_to'); if (c.client_id) { await addNode('Client', c.client_id, `Client: ${c.client_name || ''}`); edge(`Contract:${c.id}`, `Client:${c.client_id}`, 'client_party'); } } // Equipment owned const equipment = await entities.Equipment.filter({ vendor_id: vendorId }, '-created_date', 10); for (const e of equipment) { await addNode('Equipment', e.id, `Equipment: ${e.equipment_name || ''}`, e); edge(`Vendor:${vendorId}`, `Equipment:${e.id}`, 'owns'); } // Transportation arranged const transports = await entities.Transportation.filter({ vendor_id: vendorId }, '-created_date', 10); for (const t of transports) { await addNode('Transportation', t.id, `Transport: ${t.transport_type}`, t); edge(`Vendor:${vendorId}`, `Transportation:${t.id}`, 'arranges'); } // Training sponsored const trainings = await entities.Training.filter({ vendor_id: vendorId }, '-created_date', 10); for (const t of trainings) { await addNode('Training', t.id, `Training: ${t.training_name || ''}`, t); edge(`Vendor:${vendorId}`, `Training:${t.id}`, 'sponsors_training'); } } // ─── WORKER (existing, extended) ─────────────────────────── async function traverseWorker(workerId) { const worker = await entities.Workforce.get(workerId); if (!worker) return; await addNode('Worker', worker.id, `Worker: ${worker.full_name || worker.name || ''}`, worker); if (worker.vendor_id) { await addNode('Vendor', worker.vendor_id, `Vendor: ${worker.vendor_name || ''}`); edge(`Worker:${workerId}`, `Vendor:${worker.vendor_id}`, 'employed_by'); } // Assignments const assignments = await entities.Assignment.filter({ workforce_id: workerId }, '-created_date', 10); for (const a of assignments) { await addNode('Assignment', a.id, `Assignment: ${a.role || ''}`, a); edge(`Worker:${workerId}`, `Assignment:${a.id}`, 'assigned'); } // Availability const availability = await entities.WorkerAvailability.filter({ staff_id: workerId }, '-created_date', 1); if (availability.length > 0) { await addNode('Availability', availability[0].id, `Availability: ${availability[0].availability_status}`, availability[0]); edge(`Worker:${workerId}`, `Availability:${availability[0].id}`, 'availability'); } // Reputation const reputation = await entities.WorkerReputationProfile.filter({ worker_id: workerId }, '-created_date', 1); if (reputation.length > 0) { await addNode('Reputation', reputation[0].id, `Reputation: ${reputation[0].trust_tier} (${reputation[0].trust_score || 0})`, reputation[0]); edge(`Worker:${workerId}`, `Reputation:${reputation[0].id}`, 'reputation'); } // Training records const trainings = await entities.Training.filter({ workforce_id: workerId }, '-created_date', 10); for (const t of trainings) { await addNode('Training', t.id, `Training: ${t.training_name || ''}`, t); edge(`Worker:${workerId}`, `Training:${t.id}`, 'enrolled_in'); } // Equipment assigned const equipment = await entities.Equipment.filter({ assigned_to_worker_id: workerId }, '-created_date', 10); for (const e of equipment) { await addNode('Equipment', e.id, `Equipment: ${e.equipment_name || ''}`, e); edge(`Worker:${workerId}`, `Equipment:${e.id}`, 'issued_equipment'); } // Transportation const transports = await entities.Transportation.filter({ workforce_id: workerId }, '-created_date', 10); for (const t of transports) { await addNode('Transportation', t.id, `Transport: ${t.transport_type}`, t); edge(`Worker:${workerId}`, `Transportation:${t.id}`, 'transported_by'); } } // ─── FULL GRAPH (new) ────────────────────────────────────── // Walks the entire connected chain from any starting node, up to depth hops async function traverseFull(startType, startId, maxDepth = 3) { const queue = [{ type: startType, id: startId, depth: 0 }]; const seen = new Set(); while (queue.length > 0) { const { type, id, depth: curDepth } = queue.shift(); const key = `${type}:${id}`; if (seen.has(key) || curDepth > maxDepth) continue; seen.add(key); switch (type) { case 'Worker': { const w = await entities.Workforce.get(id).catch(() => null); if (!w) break; await addNode('Worker', w.id, `Worker: ${w.full_name || w.name || ''}`, w); if (w.vendor_id) queue.push({ type: 'Vendor', id: w.vendor_id, depth: curDepth + 1 }); const as = await entities.Assignment.filter({ workforce_id: id }, '-created_date', 5); for (const a of as) queue.push({ type: 'Assignment', id: a.id, depth: curDepth + 1 }); const ts = await entities.Training.filter({ workforce_id: id }, '-created_date', 5); for (const t of ts) queue.push({ type: 'Training', id: t.id, depth: curDepth + 1 }); const eq = await entities.Equipment.filter({ assigned_to_worker_id: id }, '-created_date', 5); for (const e of eq) queue.push({ type: 'Equipment', id: e.id, depth: curDepth + 1 }); const tr = await entities.Transportation.filter({ workforce_id: id }, '-created_date', 5); for (const t of tr) queue.push({ type: 'Transportation', id: t.id, depth: curDepth + 1 }); break; } case 'Vendor': { const v = await entities.Vendor.get(id).catch(() => null); if (!v) break; await addNode('Vendor', v.id, `Vendor: ${v.legal_name || v.doing_business_as || ''}`, v); const ws = await entities.Workforce.filter({ vendor_id: id }, '-created_date', 10); for (const w of ws) { await addNode('Worker', w.id, `Worker: ${w.full_name || ''}`, w); edge(`Vendor:${id}`, `Worker:${w.id}`, 'employs'); queue.push({ type: 'Worker', id: w.id, depth: curDepth + 1 }); } const cs = await entities.Contract.filter({ vendor_id: id }, '-created_date', 10); for (const c of cs) { await addNode('Contract', c.id, `Contract: ${c.contract_name || ''}`, c); edge(`Vendor:${id}`, `Contract:${c.id}`, 'party_to'); queue.push({ type: 'Contract', id: c.id, depth: curDepth + 1 }); } const eqs = await entities.Equipment.filter({ vendor_id: id }, '-created_date', 10); for (const e of eqs) { await addNode('Equipment', e.id, `Equipment: ${e.equipment_name || ''}`, e); edge(`Vendor:${id}`, `Equipment:${e.id}`, 'owns'); } const fsos = await entities.FoodServiceOperation.filter({ vendor_id: id }, '-created_date', 10); for (const f of fsos) { await addNode('FoodServiceOperation', f.id, `FSO: ${f.operation_name || ''}`, f); edge(`Vendor:${id}`, `FoodServiceOperation:${f.id}`, 'operates'); queue.push({ type: 'FoodServiceOperation', id: f.id, depth: curDepth + 1 }); } break; } case 'Contract': { const c = await entities.Contract.get(id).catch(() => null); if (!c) break; await addNode('Contract', c.id, `Contract: ${c.contract_name || ''}`, c); if (c.vendor_id) { await addNode('Vendor', c.vendor_id, `Vendor: ${c.vendor_name || ''}`); edge(`Contract:${id}`, `Vendor:${c.vendor_id}`, 'vendor_party'); } if (c.client_id) { await addNode('Client', c.client_id, `Client: ${c.client_name || ''}`); edge(`Contract:${id}`, `Client:${c.client_id}`, 'client_party'); } break; } case 'Shift': { await traverseShift(id); break; } case 'FoodServiceOperation': { await traverseFoodServiceOperation(id); break; } case 'Transportation': { await traverseTransportation(id); break; } case 'Equipment': { await traverseEquipment(id); break; } case 'Training': { await traverseTraining(id); break; } case 'Assignment': { await traverseAssignment(id); break; } case 'Order': { await traverseOrder(id, 0); break; } } } } // ─── EXECUTE ─────────────────────────────────────────────── if (query_type === 'subgraph' && entity_id) { switch (entity_type) { case 'Assignment': await traverseAssignment(entity_id); break; case 'Order': await traverseOrder(entity_id, 0); break; case 'Vendor': await traverseVendor(entity_id); break; case 'Worker': await traverseWorker(entity_id); break; case 'Shift': await traverseShift(entity_id); break; case 'Contract': await traverseContract(entity_id); break; case 'Transportation': await traverseTransportation(entity_id); break; case 'Equipment': await traverseEquipment(entity_id); break; case 'Training': await traverseTraining(entity_id); break; case 'FoodServiceOperation': await traverseFoodServiceOperation(entity_id); break; default: break; } } else if (query_type === 'full_graph' && entity_id && entity_type) { await traverseFull(entity_type, entity_id, depth); } else if (query_type === 'site_labor') { const assignments = await entities.Assignment.filter({}, '-created_date', 100); graph.nodes = assignments.map(a => ({ type: 'Assignment', id: a.id, label: `${a.workforce_name} - ${a.role}`, data: a })); } else if (query_type === 'shortage_prediction') { const orders = await entities.Order.filter({ status: 'Open' }, '-created_date', 50); for (const order of orders) { await addNode('Order', order.id, `Order: ${order.order_number || ''}`, order); const assignments = await entities.Assignment.filter({ order_id: order.id }, '-created_date', 50); const filled = assignments.filter(a => a.assignment_status === 'Confirmed' || a.assignment_status === 'Checked In').length; const needed = order.total_staff || 0; if (filled < needed) { edge(`Order:${order.id}`, `SHORTAGE:${order.id}`, `short_by_${needed - filled}`); graph.nodes.push({ type: 'Alert', id: `SHORTAGE:${order.id}`, label: `Shortage: ${needed - filled} unfilled positions`, data: { order_id: order.id, needed, filled, gap: needed - filled } }); } } } else if (query_type === 'entity_summary') { // Returns counts of every entity type in the graph — the "graph census" const counts = {}; const entityTypes = ['Workforce', 'Vendor', 'Business', 'Site', 'Shift', 'Event', 'Order', 'Assignment', 'Invoice', 'CompliancePackage', 'Scorecard', 'Contract', 'Transportation', 'Equipment', 'Training', 'FoodServiceOperation']; for (const et of entityTypes) { try { const list = await entities[et].list('-created_date', 1); counts[et] = list?.length || 0; } catch (e) { counts[et] = 0; } } return Response.json({ success: true, counts }); } return Response.json({ success: true, graph, node_count: graph.nodes.length, edge_count: graph.edges.length }); } catch (error) { return Response.json({ error: error.message }, { status: 500 }); } });