update frontend ui chages

This commit is contained in:
2026-08-17 23:45:32 +05:30
parent 9a7852c8a2
commit ff841f8b86
278 changed files with 14841 additions and 14597 deletions

View File

@@ -1,3 +1,10 @@
# Build output — the image builds its own inside the container
dist
dist-ssr
*.local
node_modules
# Logs # Logs
logs logs
*.log *.log
@@ -7,13 +14,6 @@ yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
node_modules
# dist
!sdk/dist
dist-ssr
*.local
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
@@ -25,11 +25,5 @@ dist-ssr
*.sln *.sln
*.sw? *.sw?
sdk/node_modules .git
# Explicitly include SDK dist .env
# !/sdk/dist/*
sdk/build
sdk/coverage
sdk/tests/.env
.env

1
.gitignore vendored
View File

@@ -28,4 +28,3 @@ dist-ssr
*.sw? *.sw?
.env .env
base44/.app.jsonc

View File

@@ -1,9 +1,54 @@
# Base44 Preview Template for MicroVM sandbox # KROW Workforce Control Tower
This template is used by the server to preview user-apps. A standalone frontend application. There is no backend, no external API and no
login: the app opens directly into the Control Tower UI, locally and when
deployed, and keeps working with no network at all.
## user files ```
server creates the user-app files in the __components__, __pages__ folders Frontend → local data / React state / localStorage → KROW UI
```
## server injected data ## Running it
server injects app related data to __app.config.js__, which is used by App.jsx to render the components in the files.
```bash
npm install
npm run dev # http://localhost:5174
npm run build # static bundle in dist/
npm run preview # serve the production build locally
```
Deployment is a static-file deploy: serve `dist/` with any web server, with a
SPA fallback so deep links (`/Events`, `/Schedule`, …) resolve to `index.html`.
The included `Dockerfile` + `nginx.conf` do exactly that.
## Where the data lives
Everything the UI reads and writes goes through one object — `krow`, exported
from [`src/api/client.js`](src/api/client.js):
| Area | Local implementation |
| --- | --- |
| `krow.entities.*` | [`src/api/local/store.js`](src/api/local/store.js) — collections held in memory and mirrored into `localStorage` |
| Starting data | [`src/api/local/seedData.js`](src/api/local/seedData.js) — the bundled demo dataset, copied in on first read |
| Data model | [`src/api/local/schemas.json`](src/api/local/schemas.json) — entity schemas, loaded on demand for `.schema()` |
| `krow.auth` | [`src/api/local/auth.js`](src/api/local/auth.js) — a single local operator profile; no sign-in |
| `krow.integrations.Core` | [`src/api/local/integrations.js`](src/api/local/integrations.js) — uploads become local data/object URLs; email/SMS are recorded locally |
| `krow.agents` | [`src/api/local/agents.js`](src/api/local/agents.js) — assistant threads stored locally, answered by [`src/lib/owliver/owliverEngine.js`](src/lib/owliver/owliverEngine.js) |
Edits persist per browser. `krow.resetLocalData()` (from the console, or any
code) clears local data and restores the bundled demo dataset.
Collections with no seed data start empty, which is the empty state the pages
already render — so adding an entity needs no wiring, only a key in
`seedData.js` if it should start with rows.
## Layout
```
src/
api/ the local client (client.js) and its implementation (local/)
pages/ one file per route, auto-registered via pages.config.js
components/ feature components and the ui/ primitives
lib/ shared helpers, the Owliver engine and page registry
Layout.jsx the Control Tower shell: header, sidebar, Owliver
```

View File

@@ -1,89 +0,0 @@
{
"description": "Compliance Agent \u2014 monitors certifications, detects compliance risks, auto-flags expiring documents, manages audit trails. Acts as a digital compliance officer.",
"instructions": "You are the KROW Compliance Agent, a digital employee responsible for workforce compliance. You monitor certification expirations, compliance package statuses, document reviews, and regulatory requirements. You proactively flag risks before they become violations. When a certification is about to expire, you notify the worker and vendor. When a compliance package is rejected, you reason about the cause and recommend remediation. Be thorough and risk-averse \u2014 compliance failures are costly.",
"tool_configs": [
{
"entity_name": "WorkforceEvent",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "OperationalMemory",
"allowed_operations": [
"read"
]
},
{
"entity_name": "CompliancePackage",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "Certification",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "EmployeeDocument",
"allowed_operations": [
"read"
]
},
{
"entity_name": "VendorDocumentReview",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Workforce",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "DecisionQueue",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "AgentActivity",
"allowed_operations": [
"create",
"read"
]
},
{
"function_name": "reasonAboutEvent",
"description": "Trigger reasoning engine for compliance events"
}
],
"context_files": [],
"app_user_connector_configs": [],
"selected_skill_names": [],
"selected_workspace_skill_ids": [],
"model": "automatic",
"whatsapp_greeting": null,
"telegram_greeting": null,
"line_greeting": null,
"memory_config": {
"enabled": true,
"scope": "both",
"include_other_conversation_context": false,
"instructions": null
},
"voice_config": {
"voice_id": null
},
"allow_anonymous_access": false,
"name": "compliance_agent"
}

View File

@@ -1,81 +0,0 @@
{
"description": "Executive Agent \u2014 provides strategic cross-department insights, forecasts trends, identifies systemic patterns. Acts as a digital chief of staff for workforce strategy.",
"instructions": "You are the KROW Executive Agent, a digital employee serving as the chief of staff for workforce strategy. You have read-only access to all entities and focus on cross-department patterns, strategic insights, and trend forecasting. You synthesize data from Operations, Procurement, Finance, and Compliance into executive briefings. You identify systemic issues (e.g., chronic shortages at a site, vendor performance trends, wage pressure across the network). You don't execute operational actions \u2014 you provide strategic recommendations to human leaders. Be concise, strategic, and focused on what matters most.",
"tool_configs": [
{
"entity_name": "WorkforceEvent",
"allowed_operations": [
"read"
]
},
{
"entity_name": "OperationalMemory",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Order",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Assignment",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Vendor",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Invoice",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Budget",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Workforce",
"allowed_operations": [
"read"
]
},
{
"entity_name": "WorkerReputationProfile",
"allowed_operations": [
"read"
]
},
{
"entity_name": "AgentActivity",
"allowed_operations": [
"read"
]
},
{
"entity_name": "DecisionQueue",
"allowed_operations": [
"read"
]
},
{
"function_name": "queryWorkforceGraph",
"description": "Query the workforce graph for strategic analysis"
},
{
"function_name": "reasonAboutEvent",
"description": "Trigger the reasoning engine for strategic analysis"
}
],
"name": "executive_agent"
}

View File

@@ -1,75 +0,0 @@
{
"description": "Finance Agent \u2014 tracks spend, forecasts budgets, detects invoice anomalies, monitors cost centers. Acts as a digital finance manager.",
"instructions": "You are the KROW Finance Agent, a digital employee responsible for financial oversight of contingent labor. You monitor invoices, payroll, budgets, and spend patterns. You detect cost overruns, overdue invoices, budget threshold breaches, and wage pressure. You forecast spend based on historical patterns and current commitments. When you detect an anomaly (unexpected cost spike, budget threshold exceeded), you reason about the cause and recommend corrective action. Be precise with numbers and always quantify your recommendations.",
"tool_configs": [
{
"entity_name": "WorkforceEvent",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "OperationalMemory",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Invoice",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "PayrollRecord",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "Budget",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "SaleRecord",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Assignment",
"allowed_operations": [
"read"
]
},
{
"entity_name": "DecisionQueue",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "AgentActivity",
"allowed_operations": [
"create",
"read"
]
},
{
"function_name": "reasonAboutEvent",
"description": "Trigger the reasoning engine to analyze a financial event"
},
{
"function_name": "queryWorkforceGraph",
"description": "Query the workforce graph for cost and assignment data"
}
],
"name": "finance_agent"
}

File diff suppressed because one or more lines are too long

View File

@@ -1,96 +0,0 @@
{
"description": "Operations Agent \u2014 monitors shifts, predicts labor shortages, rebalances staffing automatically. Acts as a digital operations manager.",
"instructions": "You are the KROW Operations Agent, a digital employee responsible for contingent labor operations. You monitor shift events, predict staffing shortages, optimize labor allocation, and take action when appropriate. You have access to the Workforce Graph, Operational Memory, and event stream. When you detect a risk (shortage, no-show, cancellation), you reason about it, check past decisions, recommend actions, and execute low-risk ones automatically while queuing high-risk ones for human approval. Always explain your reasoning. Be proactive, not reactive.",
"tool_configs": [
{
"entity_name": "WorkforceEvent",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "OperationalMemory",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Order",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "Assignment",
"allowed_operations": [
"create",
"read",
"update"
]
},
{
"entity_name": "WorkerAvailability",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "Workforce",
"allowed_operations": [
"read"
]
},
{
"entity_name": "ShiftProposal",
"allowed_operations": [
"create",
"read",
"update"
]
},
{
"entity_name": "DecisionQueue",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "AgentActivity",
"allowed_operations": [
"create",
"read"
]
},
{
"function_name": "reasonAboutEvent",
"description": "Trigger the reasoning engine to analyze an event"
},
{
"function_name": "queryWorkforceGraph",
"description": "Query the workforce graph for relationship data"
}
],
"context_files": [],
"app_user_connector_configs": [],
"selected_skill_names": [],
"selected_workspace_skill_ids": [],
"model": "automatic",
"whatsapp_greeting": null,
"telegram_greeting": null,
"line_greeting": null,
"memory_config": {
"enabled": true,
"scope": "both",
"include_other_conversation_context": false,
"instructions": null
},
"voice_config": {
"voice_id": null
},
"allow_anonymous_access": false,
"name": "operations_agent"
}

View File

@@ -1,76 +0,0 @@
{
"description": "Procurement Agent \u2014 benchmarks vendor performance, optimizes rates, manages vendor approvals, detects pricing anomalies. Acts as a digital procurement manager.",
"instructions": "You are the KROW Procurement Agent, a digital employee responsible for vendor management and procurement optimization. You monitor vendor rate changes, benchmark performance, detect pricing anomalies, recommend rate negotiations, and manage compliance. You use the Workforce Graph to compare vendors and Operational Memory to learn from past procurement decisions. Be analytical and data-driven. When you detect a vendor offering rates above market, flag it. When you see performance declining, recommend action.",
"tool_configs": [
{
"entity_name": "WorkforceEvent",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "OperationalMemory",
"allowed_operations": [
"read"
]
},
{
"entity_name": "Vendor",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "VendorRate",
"allowed_operations": [
"read",
"update"
]
},
{
"entity_name": "VendorNetworkApproval",
"allowed_operations": [
"create",
"read",
"update"
]
},
{
"entity_name": "VendorRateBook",
"allowed_operations": [
"read"
]
},
{
"entity_name": "CompliancePackage",
"allowed_operations": [
"read"
]
},
{
"entity_name": "DecisionQueue",
"allowed_operations": [
"create",
"read"
]
},
{
"entity_name": "AgentActivity",
"allowed_operations": [
"create",
"read"
]
},
{
"function_name": "reasonAboutEvent",
"description": "Trigger the reasoning engine to analyze a procurement event"
},
{
"function_name": "queryWorkforceGraph",
"description": "Query the workforce graph for vendor and rate data"
}
],
"name": "procurement_agent"
}

View File

@@ -1,9 +0,0 @@
{
"name": "KROW Workforce Control Tower",
"site": {
"installCommand": "npm install",
"buildCommand": "npm run build",
"serveCommand": "npm run dev",
"outputDirectory": "./dist"
}
}

View File

@@ -1,127 +0,0 @@
{
"name": "AIInterview",
"type": "object",
"properties": {
"applicant_id": {
"type": "string"
},
"job_posting_id": {
"type": "string"
},
"applicant_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"scheduled",
"in_progress",
"completed",
"cancelled"
],
"default": "scheduled"
},
"messages": {
"type": "array",
"description": "Full conversation transcript",
"items": {
"type": "object",
"properties": {
"role": {
"type": "string",
"enum": [
"ai",
"candidate"
]
},
"content": {
"type": "string"
},
"timestamp": {
"type": "string"
},
"response_time_seconds": {
"type": "number"
}
}
}
},
"questions_asked": {
"type": "number",
"default": 0
},
"overall_interview_score": {
"type": "number"
},
"verdict": {
"type": "string"
},
"hire_recommendation": {
"type": "string"
},
"ai_flags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Flags like AI-generated responses, suspiciously fast answers"
},
"integrity_score": {
"type": "number",
"description": "0-100, higher = more likely authentic human responses"
},
"category_scores": {
"type": "object",
"properties": {
"communication": {
"type": "number"
},
"technical_fit": {
"type": "number"
},
"reliability": {
"type": "number"
},
"culture_fit": {
"type": "number"
},
"problem_solving": {
"type": "number"
}
}
},
"strengths": {
"type": "array",
"items": {
"type": "string"
}
},
"concerns": {
"type": "array",
"items": {
"type": "string"
}
},
"best_fit_roles": {
"type": "array",
"items": {
"type": "string"
}
},
"summary": {
"type": "string"
},
"completed_at": {
"type": "string"
}
},
"required": [
"applicant_id",
"job_posting_id",
"applicant_name",
"job_title"
]
}

View File

@@ -1,94 +0,0 @@
{
"name": "ActivityLog",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Notification title (e.g., 'Event Rescheduled', 'Invoice Paid')"
},
"description": {
"type": "string",
"description": "Detailed description of the activity"
},
"activity_type": {
"type": "string",
"enum": [
"event_created",
"event_updated",
"event_rescheduled",
"event_canceled",
"staff_assigned",
"staff_removed",
"invoice_paid",
"invoice_created",
"message_received",
"order_created",
"order_updated"
],
"description": "Type of activity"
},
"related_entity_type": {
"type": "string",
"enum": [
"event",
"staff",
"invoice",
"message",
"order",
"user"
],
"description": "Type of related entity"
},
"related_entity_id": {
"type": "string",
"description": "ID of the related entity"
},
"action_link": {
"type": "string",
"description": "Link to view the related item (e.g., 'EventDetail?id=123')"
},
"action_label": {
"type": "string",
"description": "Label for the action button (e.g., 'View Event', 'View Invoice')"
},
"user_id": {
"type": "string",
"description": "ID of the user this notification is for"
},
"is_read": {
"type": "boolean",
"default": false,
"description": "Whether the notification has been read"
},
"icon_type": {
"type": "string",
"enum": [
"calendar",
"user",
"invoice",
"message",
"alert",
"check"
],
"description": "Icon to display for this notification"
},
"icon_color": {
"type": "string",
"enum": [
"blue",
"red",
"green",
"yellow",
"purple"
],
"default": "blue",
"description": "Color theme for the icon"
}
},
"required": [
"title",
"description",
"activity_type",
"user_id"
]
}

View File

@@ -1,79 +0,0 @@
{
"name": "AgentActivity",
"type": "object",
"properties": {
"agent_name": {
"type": "string",
"description": "Name of the AI agent that performed this activity"
},
"department": {
"type": "string",
"enum": [
"operations",
"procurement",
"finance",
"vendor",
"compliance",
"executive"
]
},
"activity_type": {
"type": "string",
"enum": [
"event_processed",
"prediction_made",
"action_executed",
"decision_queued",
"anomaly_detected",
"recommendation_sent",
"learning_completed",
"graph_queried"
]
},
"trigger_event_id": {
"type": "string"
},
"memory_id": {
"type": "string"
},
"summary": {
"type": "string",
"description": "Human-readable description of what the agent did"
},
"details": {
"type": "object",
"additionalProperties": true
},
"impact_level": {
"type": "string",
"enum": [
"info",
"low",
"medium",
"high",
"critical"
],
"default": "info"
},
"entities_affected": {
"type": "array",
"items": {
"type": "object",
"properties": {
"entity_type": {
"type": "string"
},
"entity_id": {
"type": "string"
}
}
}
}
},
"required": [
"agent_name",
"department",
"activity_type",
"summary"
]
}

View File

@@ -1,138 +0,0 @@
{
"name": "Announcement",
"type": "object",
"properties": {
"team_id": {
"type": "string",
"description": "ID of the team that created this announcement"
},
"vendor_id": {
"type": "string",
"description": "Vendor/owner ID"
},
"title": {
"type": "string",
"description": "Announcement headline"
},
"body": {
"type": "string",
"description": "Full announcement content/message"
},
"flyer_url": {
"type": "string",
"description": "URL to uploaded flyer image"
},
"audience": {
"type": "array",
"description": "Who can see this announcement",
"items": {
"type": "string",
"enum": [
"all",
"vendor",
"client",
"workforce",
"procurement",
"operator",
"sector",
"admin"
]
}
},
"specific_recipients": {
"type": "array",
"description": "Specific individual staff members to send this announcement to",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"position": {
"type": "string"
}
}
}
},
"status": {
"type": "string",
"enum": [
"draft",
"scheduled",
"active",
"paused",
"expired"
],
"default": "draft",
"description": "Current status of the announcement"
},
"priority": {
"type": "string",
"enum": [
"low",
"normal",
"high",
"urgent"
],
"default": "normal",
"description": "Announcement priority/urgency level"
},
"publish_date": {
"type": "string",
"format": "date-time",
"description": "Scheduled publish date and time"
},
"expire_date": {
"type": "string",
"format": "date-time",
"description": "When the announcement stops being visible"
},
"send_email": {
"type": "boolean",
"default": false,
"description": "Whether to also send this as an email to audience"
},
"send_push": {
"type": "boolean",
"default": false,
"description": "Whether to send push notification"
},
"pin_to_top": {
"type": "boolean",
"default": false,
"description": "Pin this announcement to top of feed"
},
"view_count": {
"type": "number",
"default": 0,
"description": "Number of views"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tags for categorization"
},
"created_by_name": {
"type": "string",
"description": "Name of user who created the announcement"
}
},
"required": [
"team_id",
"title",
"body",
"audience",
"status"
]
}

View File

@@ -1,174 +0,0 @@
{
"name": "Assignment",
"type": "object",
"properties": {
"assignment_number": {
"type": "string",
"description": "Assignment Number format ASN-#### (auto-generated)",
"pattern": "^ASN-[0-9]{4,6}$"
},
"order_id": {
"type": "string",
"description": "Associated order ID"
},
"order_number": {
"type": "string",
"description": "Associated order number"
},
"workforce_id": {
"type": "string",
"description": "Assigned worker ID"
},
"workforce_name": {
"type": "string",
"description": "Assigned worker name"
},
"vendor_id": {
"type": "string",
"description": "Vendor providing the worker"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"partner_id": {
"type": "string",
"description": "Client/partner ID"
},
"role": {
"type": "string",
"description": "Role assigned"
},
"department": {
"type": "string",
"description": "Department"
},
"assignment_status": {
"type": "string",
"enum": [
"Pending",
"Confirmed",
"Checked In",
"In Progress",
"Completed",
"Cancelled",
"No Show"
],
"default": "Pending",
"description": "Assignment status"
},
"scheduled_start": {
"type": "string",
"format": "date-time",
"description": "Scheduled start time"
},
"scheduled_end": {
"type": "string",
"format": "date-time",
"description": "Scheduled end time"
},
"actual_start": {
"type": "string",
"format": "date-time",
"description": "Actual start time"
},
"actual_end": {
"type": "string",
"format": "date-time",
"description": "Actual end time"
},
"timesheet": {
"type": "object",
"description": "Timesheet data",
"properties": {
"clock_in": {
"type": "string",
"format": "date-time"
},
"clock_out": {
"type": "string",
"format": "date-time"
},
"break_minutes": {
"type": "number"
},
"total_hours": {
"type": "number"
},
"regular_hours": {
"type": "number"
},
"overtime_hours": {
"type": "number"
},
"approved_by": {
"type": "string"
},
"approved_date": {
"type": "string",
"format": "date-time"
}
}
},
"ratings": {
"type": "object",
"description": "Performance ratings",
"properties": {
"punctuality": {
"type": "number",
"minimum": 1,
"maximum": 5
},
"quality_of_work": {
"type": "number",
"minimum": 1,
"maximum": 5
},
"professionalism": {
"type": "number",
"minimum": 1,
"maximum": 5
},
"would_hire_again": {
"type": "boolean"
},
"comments": {
"type": "string"
},
"rated_by": {
"type": "string"
},
"rated_date": {
"type": "string",
"format": "date-time"
}
}
},
"pay_rate": {
"type": "number",
"description": "Worker pay rate"
},
"bill_rate": {
"type": "number",
"description": "Client bill rate"
},
"total_pay": {
"type": "number",
"description": "Total worker pay"
},
"total_bill": {
"type": "number",
"description": "Total client bill"
},
"notes": {
"type": "string",
"description": "Assignment notes"
}
},
"required": [
"order_id",
"workforce_id",
"vendor_id",
"role"
]
}

View File

@@ -1,55 +0,0 @@
{
"name": "Budget",
"type": "object",
"properties": {
"hub_name": {
"type": "string",
"description": "Name of the budget hub"
},
"cost_center": {
"type": "string",
"description": "Cost center code this budget maps to"
},
"amount": {
"type": "number",
"minimum": 0,
"description": "Budget amount for the period"
},
"frequency": {
"type": "string",
"enum": [
"Daily",
"Weekly",
"Monthly"
],
"default": "Monthly",
"description": "Budget periodicity"
},
"alert_enabled": {
"type": "boolean",
"default": true,
"description": "Whether usage alerts are on"
},
"alert_threshold": {
"type": "number",
"minimum": 1,
"maximum": 100,
"default": 80,
"description": "Utilization percentage that triggers an alert"
},
"business_id": {
"type": "string",
"description": "Client business ID this budget belongs to"
},
"business_name": {
"type": "string",
"description": "Client business name"
}
},
"required": [
"hub_name",
"cost_center",
"amount",
"frequency"
]
}

View File

@@ -1,306 +0,0 @@
{
"name": "Business",
"type": "object",
"properties": {
"business_name": {
"type": "string",
"description": "Name of the business/client company"
},
"company_logo": {
"type": "string",
"description": "URL to company logo image"
},
"contact_name": {
"type": "string",
"description": "Primary contact person"
},
"email": {
"type": "string",
"format": "email",
"description": "Business email"
},
"phone": {
"type": "string",
"description": "Business phone number"
},
"hub_building": {
"type": "string",
"description": "Hub or building name/location"
},
"address": {
"type": "string",
"description": "Street address"
},
"city": {
"type": "string",
"description": "City"
},
"area": {
"type": "string",
"enum": [
"Bay Area",
"Southern California",
"Northern California",
"Central Valley",
"Other"
],
"description": "Geographic area"
},
"sector": {
"type": "string",
"enum": [
"Bon App\u00e9tit",
"Eurest",
"Aramark",
"Epicurean Group",
"Chartwells",
"Other"
],
"description": "Sector/industry"
},
"rate_group": {
"type": "string",
"enum": [
"Standard",
"Premium",
"Enterprise",
"Custom"
],
"description": "Pricing tier/rate group"
},
"rate_card": {
"type": "string",
"description": "Assigned rate card name (e.g., Google, Bay Area Compass, Aramark)"
},
"erp_system": {
"type": "string",
"enum": [
"None",
"SAP Ariba",
"Fieldglass",
"CrunchTime",
"Coupa",
"Oracle NetSuite",
"Workday",
"Other"
],
"default": "None",
"description": "Client's ERP/procurement system"
},
"erp_vendor_id": {
"type": "string",
"description": "Vendor ID in client's ERP system"
},
"erp_cost_center": {
"type": "string",
"description": "Default cost center code for invoicing"
},
"edi_enabled": {
"type": "boolean",
"default": false,
"description": "Whether EDI integration is enabled"
},
"edi_format": {
"type": "string",
"enum": [
"EDI 810",
"cXML",
"CSV",
"JSON",
"Custom"
],
"description": "Preferred EDI/export format"
},
"invoice_email": {
"type": "string",
"format": "email",
"description": "Email address for invoice delivery"
},
"po_required": {
"type": "boolean",
"default": false,
"description": "Whether PO number is required for invoicing"
},
"status": {
"type": "string",
"enum": [
"Active",
"Inactive",
"Pending"
],
"default": "Active",
"description": "Business status"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Whether the client is currently active (vendor can toggle)"
},
"last_order_date": {
"type": "string",
"format": "date",
"description": "Date of the last order placed by this client"
},
"notes": {
"type": "string",
"description": "Additional notes"
},
"vendor_id": {
"type": "string",
"description": "Vendor managing this business"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"hubs": {
"type": "array",
"description": "List of hubs/locations for this business",
"items": {
"type": "object",
"properties": {
"hub_name": {
"type": "string",
"description": "Name of the hub/location"
},
"address": {
"type": "string",
"description": "Hub address"
},
"city": {
"type": "string",
"description": "City"
},
"state": {
"type": "string",
"description": "State"
},
"zip_code": {
"type": "string",
"description": "ZIP code"
},
"manager_name": {
"type": "string",
"description": "Hub manager name"
},
"manager_email": {
"type": "string",
"format": "email",
"description": "Hub manager email"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Whether the hub is active"
}
}
}
},
"departments": {
"type": "array",
"description": "List of departments within this business",
"items": {
"type": "string"
}
},
"team_members": {
"type": "array",
"description": "Team members managing this business",
"items": {
"type": "object",
"properties": {
"member_id": {
"type": "string"
},
"member_name": {
"type": "string"
},
"email": {
"type": "string"
},
"role": {
"type": "string"
},
"is_active": {
"type": "boolean",
"default": true
}
}
}
},
"favorite_staff": {
"type": "array",
"description": "Array of favorite staff IDs for this business",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"staff_name": {
"type": "string"
},
"position": {
"type": "string"
},
"added_date": {
"type": "string",
"format": "date-time"
}
}
}
},
"blocked_staff": {
"type": "array",
"description": "Array of blocked staff IDs for this business",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"staff_name": {
"type": "string"
},
"reason": {
"type": "string"
},
"blocked_date": {
"type": "string",
"format": "date-time"
}
}
}
},
"total_members": {
"type": "number",
"default": 0,
"description": "Total team members"
},
"active_members": {
"type": "number",
"default": 0,
"description": "Active team members"
},
"total_hubs": {
"type": "number",
"default": 0,
"description": "Total hubs"
},
"favorite_staff_count": {
"type": "number",
"default": 0,
"description": "Number of favorite staff"
},
"blocked_staff_count": {
"type": "number",
"default": 0,
"description": "Number of blocked staff"
}
},
"required": [
"business_name",
"contact_name",
"rate_group"
]
}

View File

@@ -1,88 +0,0 @@
{
"name": "Certification",
"type": "object",
"properties": {
"employee_id": {
"type": "string",
"description": "Staff member ID"
},
"employee_name": {
"type": "string",
"description": "Staff member name"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID who owns this employee"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"certification_type": {
"type": "string",
"enum": [
"Background Check",
"RBS",
"Food Handler"
],
"description": "Type of certification - all 3 are required for every employee"
},
"status": {
"type": "string",
"enum": [
"current",
"expiring_soon",
"expired",
"pending"
],
"default": "pending",
"description": "Current status of certification"
},
"issue_date": {
"type": "string",
"format": "date",
"description": "Date certification was issued"
},
"expiry_date": {
"type": "string",
"format": "date",
"description": "Date certification expires"
},
"issuer": {
"type": "string",
"description": "Name of issuing authority"
},
"certificate_number": {
"type": "string",
"description": "Certificate number or ID"
},
"document_url": {
"type": "string",
"description": "URL to uploaded certificate document"
},
"days_until_expiry": {
"type": "number",
"description": "Auto-calculated days until expiry"
},
"alert_sent_30_days": {
"type": "boolean",
"default": false,
"description": "Has 30-day expiry alert been sent"
},
"last_alert_date": {
"type": "string",
"format": "date-time",
"description": "When last alert was sent"
},
"notes": {
"type": "string",
"description": "Additional notes about this certification"
}
},
"required": [
"employee_id",
"employee_name",
"certification_type",
"expiry_date"
]
}

View File

@@ -1,158 +0,0 @@
{
"name": "CompliancePackage",
"type": "object",
"properties": {
"compliance_number": {
"type": "string",
"description": "Compliance Package Number format CP-#### (auto-generated)",
"pattern": "^CP-[0-9]{4}$"
},
"entity_type": {
"type": "string",
"enum": [
"Vendor",
"Workforce",
"Partner"
],
"description": "Type of entity this compliance package belongs to"
},
"entity_id": {
"type": "string",
"description": "ID of the vendor/worker/partner"
},
"entity_name": {
"type": "string",
"description": "Name of entity"
},
"package_type": {
"type": "string",
"enum": [
"Vendor Onboarding",
"Worker Onboarding",
"Annual Review",
"Audit"
],
"description": "Type of compliance package"
},
"required_documents": {
"type": "array",
"description": "Required document checklist",
"items": {
"type": "object",
"properties": {
"document_name": {
"type": "string"
},
"document_type": {
"type": "string",
"enum": [
"W9",
"COI",
"W2",
"I9",
"Background Check",
"Drug Test",
"Certification",
"License",
"Other"
]
},
"is_required": {
"type": "boolean"
},
"status": {
"type": "string",
"enum": [
"Not Started",
"Pending",
"Submitted",
"Approved",
"Rejected",
"Expired"
]
},
"document_url": {
"type": "string"
},
"submitted_date": {
"type": "string",
"format": "date"
},
"approved_date": {
"type": "string",
"format": "date"
},
"expiry_date": {
"type": "string",
"format": "date"
},
"reviewer": {
"type": "string"
},
"rejection_reason": {
"type": "string"
}
}
}
},
"overall_status": {
"type": "string",
"enum": [
"Not Started",
"In Progress",
"Pending Review",
"Approved",
"Rejected",
"Expired"
],
"default": "Not Started",
"description": "Overall package status"
},
"completion_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Percentage of required documents completed"
},
"audit_trail": {
"type": "array",
"description": "Audit trail of changes",
"items": {
"type": "object",
"properties": {
"timestamp": {
"type": "string",
"format": "date-time"
},
"action": {
"type": "string"
},
"performed_by": {
"type": "string"
},
"details": {
"type": "string"
}
}
}
},
"next_review_date": {
"type": "string",
"format": "date",
"description": "Next scheduled review date"
},
"compliance_officer": {
"type": "string",
"description": "Assigned compliance officer"
},
"notes": {
"type": "string",
"description": "Compliance notes"
}
},
"required": [
"entity_type",
"entity_id",
"package_type"
]
}

View File

@@ -1,168 +0,0 @@
{
"name": "Contract",
"type": "object",
"properties": {
"contract_number": {
"type": "string",
"description": "Contract Number format CT-#### (auto-generated)",
"pattern": "^CT-[0-9]{4}$"
},
"contract_name": {
"type": "string",
"description": "Descriptive contract name"
},
"contract_type": {
"type": "string",
"enum": [
"MSA",
"SOW",
"Rate Agreement",
"Service Agreement",
"NDA",
"Amendment"
],
"description": "Type of contract"
},
"vendor_id": {
"type": "string",
"description": "Vendor party to this contract"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"client_id": {
"type": "string",
"description": "Client/Business ID party to this contract"
},
"client_name": {
"type": "string",
"description": "Client/Business name"
},
"sector_id": {
"type": "string",
"description": "Sector this contract falls under (if applicable)"
},
"sector_name": {
"type": "string"
},
"partner_id": {
"type": "string",
"description": "Partner ID (if partner-specific)"
},
"partner_name": {
"type": "string"
},
"procurement_manager": {
"type": "string",
"description": "Procurement manager who owns this contract"
},
"rate_card_id": {
"type": "string",
"description": "Reference to RateCard entity if rates are defined there"
},
"effective_start_date": {
"type": "string",
"format": "date",
"description": "Contract effective start date"
},
"effective_end_date": {
"type": "string",
"format": "date",
"description": "Contract end date"
},
"status": {
"type": "string",
"enum": [
"draft",
"pending_review",
"active",
"expiring",
"expired",
"terminated",
"amended"
],
"default": "draft",
"description": "Contract lifecycle status"
},
"billing_terms": {
"type": "string",
"description": "Net 15, Net 30, Net 45, Net 60, etc."
},
"payment_terms": {
"type": "string",
"description": "Payment terms and conditions"
},
"auto_renew": {
"type": "boolean",
"default": false,
"description": "Whether contract auto-renews"
},
"renewal_notice_days": {
"type": "number",
"description": "Days notice required before renewal/termination"
},
"document_url": {
"type": "string",
"description": "URL to the signed contract document"
},
"total_contract_value": {
"type": "number",
"minimum": 0,
"description": "Total estimated contract value"
},
"signed_by_vendor": {
"type": "string",
"description": "Name of vendor signer"
},
"signed_by_client": {
"type": "string",
"description": "Name of client signer"
},
"signed_date": {
"type": "string",
"format": "date",
"description": "Date contract was fully executed"
},
"amendments": {
"type": "array",
"description": "List of contract amendments",
"items": {
"type": "object",
"properties": {
"amendment_number": {
"type": "number"
},
"description": {
"type": "string"
},
"effective_date": {
"type": "string",
"format": "date"
},
"document_url": {
"type": "string"
}
}
}
},
"compliance_requirements": {
"type": "array",
"description": "Compliance requirements tied to this contract",
"items": {
"type": "string"
}
},
"notes": {
"type": "string",
"description": "Additional notes"
}
},
"required": [
"contract_name",
"contract_type",
"vendor_id",
"client_id",
"status"
]
}

View File

@@ -1,97 +0,0 @@
{
"name": "Conversation",
"type": "object",
"properties": {
"participants": {
"type": "array",
"description": "Array of participant IDs/emails",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"role": {
"type": "string",
"enum": [
"client",
"vendor",
"staff",
"admin"
]
}
}
}
},
"conversation_type": {
"type": "string",
"enum": [
"client-vendor",
"staff-client",
"staff-admin",
"vendor-admin",
"client-admin",
"group-staff",
"group-event-staff"
],
"description": "Type of conversation"
},
"is_group": {
"type": "boolean",
"default": false,
"description": "Whether this is a group conversation"
},
"group_name": {
"type": "string",
"description": "Name of the group (e.g., 'All Active Staff', 'Event #123 Team')"
},
"related_to": {
"type": "string",
"description": "ID of related entity (event_id, order_id, etc.)"
},
"related_type": {
"type": "string",
"enum": [
"event",
"staff",
"business",
"general"
],
"description": "Type of related entity"
},
"subject": {
"type": "string",
"description": "Conversation subject/title"
},
"last_message": {
"type": "string",
"description": "Preview of last message"
},
"last_message_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp of last message"
},
"unread_count": {
"type": "number",
"default": 0,
"description": "Number of unread messages"
},
"status": {
"type": "string",
"enum": [
"active",
"archived",
"closed"
],
"default": "active"
}
},
"required": [
"participants",
"conversation_type"
]
}

View File

@@ -1,107 +0,0 @@
{
"name": "DecisionQueue",
"type": "object",
"properties": {
"memory_id": {
"type": "string",
"description": "Reference to the OperationalMemory this decision is based on"
},
"trigger_event_id": {
"type": "string",
"description": "Event that triggered this decision"
},
"title": {
"type": "string",
"description": "Short title for the decision"
},
"summary": {
"type": "string",
"description": "Human-readable summary of what the AI recommends and why"
},
"department": {
"type": "string",
"enum": [
"operations",
"procurement",
"finance",
"vendor",
"compliance",
"executive"
]
},
"agent_name": {
"type": "string"
},
"proposed_actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"action_type": {
"type": "string"
},
"description": {
"type": "string"
},
"auto_execute": {
"type": "boolean"
}
}
}
},
"confidence_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"risk_level": {
"type": "string",
"enum": [
"low",
"medium",
"high"
],
"default": "medium"
},
"status": {
"type": "string",
"enum": [
"pending_approval",
"approved",
"rejected",
"executed",
"expired"
],
"default": "pending_approval"
},
"reviewer_id": {
"type": "string",
"description": "User who reviewed the decision"
},
"reviewer_name": {
"type": "string"
},
"review_notes": {
"type": "string"
},
"reviewed_at": {
"type": "string",
"format": "date-time"
},
"expires_at": {
"type": "string",
"format": "date-time",
"description": "When this decision becomes stale"
},
"context_snapshot": {
"type": "object",
"additionalProperties": true,
"description": "Graph context for the reviewer to understand the decision"
}
},
"required": [
"title",
"department",
"status"
]
}

View File

@@ -1,84 +0,0 @@
{
"name": "EmployeeDocument",
"type": "object",
"properties": {
"employee_id": {
"type": "string",
"description": "Staff member ID"
},
"employee_name": {
"type": "string",
"description": "Staff member name"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"document_type": {
"type": "string",
"enum": [
"W-4 Form",
"I-9 Form",
"State Tax Form",
"Direct Deposit",
"Background Check",
"Food Handler",
"RBS License",
"ID Copy",
"SSN Card",
"Work Permit"
],
"description": "Type of document"
},
"status": {
"type": "string",
"enum": [
"uploaded",
"pending",
"expiring",
"expired",
"rejected",
"missing",
"approved"
],
"default": "missing",
"description": "Document status"
},
"document_url": {
"type": "string",
"description": "URL to uploaded document"
},
"upload_date": {
"type": "string",
"format": "date",
"description": "When document was uploaded"
},
"expiry_date": {
"type": "string",
"format": "date",
"description": "Document expiration date"
},
"approved_by": {
"type": "string",
"description": "Name of user who approved this document"
},
"approved_date": {
"type": "string",
"format": "date",
"description": "When the document was approved"
},
"notes": {
"type": "string",
"description": "Additional notes"
}
},
"required": [
"employee_id",
"employee_name",
"document_type"
]
}

View File

@@ -1,89 +0,0 @@
{
"name": "Enterprise",
"type": "object",
"properties": {
"enterprise_number": {
"type": "string",
"description": "Enterprise Number format EN-#### (auto-generated)",
"pattern": "^EN-[0-9]{4}$"
},
"enterprise_name": {
"type": "string",
"description": "Enterprise name (e.g., Compass)"
},
"enterprise_code": {
"type": "string",
"description": "Short code identifier"
},
"brand_family": {
"type": "array",
"description": "List of brands under this enterprise",
"items": {
"type": "string"
}
},
"headquarters_address": {
"type": "string",
"description": "HQ address"
},
"global_policies": {
"type": "object",
"description": "Global enterprise policies",
"properties": {
"minimum_wage": {
"type": "number"
},
"overtime_rules": {
"type": "string"
},
"uniform_standards": {
"type": "string"
},
"background_check_required": {
"type": "boolean"
}
}
},
"sector_registry": {
"type": "array",
"description": "List of sector IDs under this enterprise",
"items": {
"type": "string"
}
},
"rate_guardrails": {
"type": "object",
"description": "Rate limits and guardrails",
"properties": {
"minimum_markup_percentage": {
"type": "number"
},
"maximum_markup_percentage": {
"type": "number"
},
"minimum_bill_rate": {
"type": "number"
}
}
},
"primary_contact_name": {
"type": "string",
"description": "Enterprise contact"
},
"primary_contact_email": {
"type": "string",
"format": "email",
"description": "Enterprise contact email"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is enterprise active"
}
},
"required": [
"enterprise_name",
"enterprise_code",
"enterprise_number"
]
}

View File

@@ -1,138 +0,0 @@
{
"name": "Equipment",
"type": "object",
"properties": {
"equipment_id": {
"type": "string",
"description": "Equipment item ID"
},
"equipment_name": {
"type": "string",
"description": "Name of the equipment item"
},
"category": {
"type": "string",
"enum": [
"uniform",
"kitchen",
"service",
"cleaning",
"tech",
"safety",
"furniture",
"bar",
"other"
],
"description": "Equipment category"
},
"subcategory": {
"type": "string",
"description": "More specific type (e.g., chef coat, serving tray, POS tablet)"
},
"description": {
"type": "string"
},
"vendor_id": {
"type": "string",
"description": "Vendor that owns or provides this equipment"
},
"vendor_name": {
"type": "string"
},
"client_id": {
"type": "string",
"description": "Client/Business where equipment is assigned (if applicable)"
},
"client_name": {
"type": "string"
},
"site_id": {
"type": "string",
"description": "Site where equipment is located"
},
"shift_id": {
"type": "string",
"description": "Shift this equipment is assigned to (if temporary)"
},
"event_id": {
"type": "string",
"description": "Event this equipment is for"
},
"assigned_to_worker_id": {
"type": "string",
"description": "Worker currently assigned this equipment (for uniforms, tablets, etc.)"
},
"assigned_to_worker_name": {
"type": "string"
},
"quantity": {
"type": "number",
"default": 1,
"description": "Quantity of this item"
},
"unit_cost": {
"type": "number",
"minimum": 0,
"description": "Cost per unit"
},
"replacement_value": {
"type": "number",
"minimum": 0,
"description": "Replacement value per unit"
},
"serial_number": {
"type": "string",
"description": "Serial or asset tag number"
},
"condition": {
"type": "string",
"enum": [
"new",
"good",
"fair",
"poor",
"damaged",
"lost"
],
"default": "good"
},
"status": {
"type": "string",
"enum": [
"available",
"assigned",
"in_use",
"returned",
"in_repair",
"retired"
],
"default": "available",
"description": "Current status of the equipment"
},
"purchase_date": {
"type": "string",
"format": "date"
},
"last_inspection_date": {
"type": "string",
"format": "date"
},
"next_inspection_date": {
"type": "string",
"format": "date"
},
"requires_cleaning": {
"type": "boolean",
"default": false,
"description": "Whether item needs cleaning before next use"
},
"notes": {
"type": "string"
}
},
"required": [
"equipment_name",
"category",
"status"
]
}

View File

@@ -1,322 +0,0 @@
{
"name": "Event",
"type": "object",
"properties": {
"event_name": {
"type": "string",
"description": "Name of the event"
},
"is_rapid": {
"type": "boolean",
"default": false,
"description": "Is this a RAPID/urgent order"
},
"is_recurring": {
"type": "boolean",
"default": false,
"description": "Is this a recurring event"
},
"is_multi_day": {
"type": "boolean",
"default": false,
"description": "Is this a multi-day event"
},
"recurrence_type": {
"type": "string",
"enum": [
"single",
"date_range",
"scatter"
],
"description": "Type of recurrence: single, date_range, or scatter"
},
"recurrence_start_date": {
"type": "string",
"format": "date",
"description": "Start date for recurring events"
},
"recurrence_end_date": {
"type": "string",
"format": "date",
"description": "End date for recurring events"
},
"scatter_dates": {
"type": "array",
"description": "Array of specific dates for scatter recurring",
"items": {
"type": "string",
"format": "date"
}
},
"multi_day_start_date": {
"type": "string",
"format": "date",
"description": "Start date for multi-day events"
},
"multi_day_end_date": {
"type": "string",
"format": "date",
"description": "End date for multi-day events"
},
"buffer_time_before": {
"type": "number",
"default": 0,
"description": "Buffer time in minutes before event"
},
"buffer_time_after": {
"type": "number",
"default": 0,
"description": "Buffer time in minutes after event"
},
"conflict_detection_enabled": {
"type": "boolean",
"default": true,
"description": "Whether to check for conflicts"
},
"detected_conflicts": {
"type": "array",
"description": "Array of detected conflicts",
"items": {
"type": "object",
"properties": {
"conflict_type": {
"type": "string",
"enum": [
"staff_overlap",
"venue_overlap",
"time_buffer"
]
},
"severity": {
"type": "string",
"enum": [
"low",
"medium",
"high",
"critical"
]
},
"description": {
"type": "string"
},
"conflicting_event_id": {
"type": "string"
},
"staff_id": {
"type": "string"
},
"detected_at": {
"type": "string",
"format": "date-time"
}
}
}
},
"business_id": {
"type": "string",
"description": "Associated business ID"
},
"business_name": {
"type": "string",
"description": "Business name"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID if created by vendor"
},
"vendor_name": {
"type": "string",
"description": "Vendor name if created by vendor"
},
"hub": {
"type": "string",
"description": "Hub location for the event"
},
"event_location": {
"type": "string",
"description": "Event location address"
},
"contract_type": {
"type": "string",
"enum": [
"W2",
"1099",
"Temp",
"Contract"
],
"description": "Type of contract"
},
"po_reference": {
"type": "string",
"description": "Purchase order reference number"
},
"status": {
"type": "string",
"enum": [
"Draft",
"Active",
"Pending",
"Assigned",
"Confirmed",
"Completed",
"Canceled"
],
"default": "Draft"
},
"date": {
"type": "string",
"format": "date",
"description": "Event date (for single events)"
},
"requested": {
"type": "number",
"description": "Total number of staff requested for this event",
"default": 0
},
"assigned_staff": {
"type": "array",
"description": "Array of assigned staff members",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"staff_name": {
"type": "string"
},
"email": {
"type": "string"
},
"role": {
"type": "string"
}
}
}
},
"shifts": {
"type": "array",
"description": "Array of shifts for this event",
"items": {
"type": "object",
"properties": {
"shift_name": {
"type": "string"
},
"shift_contact": {
"type": "string"
},
"location": {
"type": "string"
},
"location_address": {
"type": "string"
},
"roles": {
"type": "array",
"items": {
"type": "object",
"properties": {
"role": {
"type": "string"
},
"department": {
"type": "string"
},
"count": {
"type": "number"
},
"start_time": {
"type": "string"
},
"end_time": {
"type": "string"
},
"hours": {
"type": "number"
},
"uniform": {
"type": "string"
},
"break_minutes": {
"type": "number"
},
"cost_per_hour": {
"type": "number"
},
"total_value": {
"type": "number"
},
"vendor_name": {
"type": "string"
},
"vendor_id": {
"type": "string"
}
}
}
}
}
}
},
"addons": {
"type": "object",
"properties": {
"goal": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"text": {
"type": "string"
}
}
},
"portal_access": {
"type": "boolean"
},
"meal_provided": {
"type": "boolean"
},
"travel_time": {
"type": "boolean"
},
"tips": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"amount": {
"type": "string"
}
}
}
}
},
"total": {
"type": "number",
"description": "Total cost"
},
"client_name": {
"type": "string"
},
"client_email": {
"type": "string"
},
"client_phone": {
"type": "string"
},
"invoice_id": {
"type": "string",
"description": "Associated invoice ID"
},
"notes": {
"type": "string"
}
},
"required": [
"event_name"
]
}

View File

@@ -1,200 +0,0 @@
{
"name": "FoodServiceOperation",
"type": "object",
"properties": {
"operation_id": {
"type": "string",
"description": "Operation record ID"
},
"operation_name": {
"type": "string",
"description": "Name of the food service operation"
},
"service_type": {
"type": "string",
"enum": [
"breakfast",
"lunch",
"dinner",
"catering",
"concession",
"cafe",
"buffet",
"banquet",
"retail",
"vending",
"other"
],
"description": "Type of food service"
},
"client_id": {
"type": "string",
"description": "Client/Business ID this operation serves"
},
"client_name": {
"type": "string"
},
"site_id": {
"type": "string",
"description": "Site where operation takes place"
},
"location_name": {
"type": "string",
"description": "Location/dining area name"
},
"sector_id": {
"type": "string",
"description": "Sector this operation belongs to"
},
"sector_name": {
"type": "string"
},
"vendor_id": {
"type": "string",
"description": "Vendor operating this service"
},
"vendor_name": {
"type": "string"
},
"manager_id": {
"type": "string",
"description": "Manager Staff ID running this operation"
},
"manager_name": {
"type": "string"
},
"shift_id": {
"type": "string",
"description": "Shift this operation is tied to"
},
"event_id": {
"type": "string",
"description": "Event this operation is part of"
},
"operation_date": {
"type": "string",
"format": "date",
"description": "Date of operation"
},
"service_start_time": {
"type": "string",
"description": "Service start time"
},
"service_end_time": {
"type": "string",
"description": "Service end time"
},
"meal_period": {
"type": "string",
"enum": [
"breakfast",
"am_break",
"lunch",
"pm_break",
"dinner",
"late_night",
"all_day"
],
"description": "Meal period"
},
"menu_type": {
"type": "string",
"description": "Menu style (e.g., American, Mediterranean, Asian, Custom)"
},
"covers_expected": {
"type": "number",
"default": 0,
"description": "Expected number of covers/guests"
},
"covers_actual": {
"type": "number",
"default": 0,
"description": "Actual covers served"
},
"staffing_level": {
"type": "number",
"default": 0,
"description": "Number of staff assigned to this operation"
},
"kitchen_status": {
"type": "string",
"enum": [
"closed",
"prep",
"service",
"post_service",
"cleanup",
"deep_clean"
],
"default": "closed",
"description": "Current kitchen/operation phase"
},
"equipment_assigned": {
"type": "array",
"description": "Equipment IDs assigned to this operation",
"items": {
"type": "object",
"properties": {
"equipment_id": {
"type": "string"
},
"equipment_name": {
"type": "string"
},
"quantity": {
"type": "number"
}
}
}
},
"contract_id": {
"type": "string",
"description": "Contract governing this operation"
},
"invoice_id": {
"type": "string",
"description": "Invoice generated for this operation"
},
"compliance_status": {
"type": "string",
"enum": [
"compliant",
"pending_review",
"non_compliant",
"not_applicable"
],
"default": "compliant"
},
"quality_score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Quality/QA score for this operation"
},
"client_feedback": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"scheduled",
"prep",
"in_service",
"completed",
"cancelled",
"no_show"
],
"default": "scheduled",
"description": "Operation status"
},
"notes": {
"type": "string"
}
},
"required": [
"operation_name",
"service_type",
"operation_date",
"status"
]
}

View File

@@ -1,309 +0,0 @@
{
"name": "Invoice",
"type": "object",
"properties": {
"invoice_number": {
"type": "string",
"description": "Unique invoice number (e.g., INV-619)"
},
"event_name": {
"type": "string",
"description": "Name of the event"
},
"event_id": {
"type": "string",
"description": "Related event ID"
},
"po_reference": {
"type": "string",
"description": "Purchase order reference"
},
"event_date": {
"type": "string",
"format": "date",
"description": "Event date"
},
"from_company": {
"type": "object",
"description": "Vendor company information",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
}
},
"to_company": {
"type": "object",
"description": "Client company information",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "string"
},
"email": {
"type": "string"
},
"manager": {
"type": "string"
},
"phone": {
"type": "string"
},
"vendor_id": {
"type": "string"
}
}
},
"business_name": {
"type": "string",
"description": "Client/Business name"
},
"manager_name": {
"type": "string",
"description": "Manager name"
},
"vendor_name": {
"type": "string",
"description": "Vendor providing services"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"hub": {
"type": "string",
"description": "Hub location"
},
"cost_center": {
"type": "string",
"description": "Cost center or department"
},
"roles": {
"type": "array",
"description": "Grouped by role with staff details",
"items": {
"type": "object",
"properties": {
"role_name": {
"type": "string"
},
"staff_entries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"staff_name": {
"type": "string"
},
"staff_id": {
"type": "string"
},
"date": {
"type": "string",
"format": "date"
},
"position": {
"type": "string"
},
"check_in": {
"type": "string"
},
"check_out": {
"type": "string"
},
"worked_hours": {
"type": "number"
},
"regular_hours": {
"type": "number"
},
"ot_hours": {
"type": "number"
},
"dt_hours": {
"type": "number"
},
"regular_rate": {
"type": "number"
},
"ot_rate": {
"type": "number"
},
"dt_rate": {
"type": "number"
},
"regular_value": {
"type": "number"
},
"ot_value": {
"type": "number"
},
"dt_value": {
"type": "number"
},
"rate": {
"type": "number"
},
"total": {
"type": "number"
}
}
}
},
"role_subtotal": {
"type": "number"
}
}
}
},
"subtotal": {
"type": "number",
"description": "Subtotal amount"
},
"other_charges": {
"type": "number",
"description": "Additional charges"
},
"amount": {
"type": "number",
"minimum": 0,
"description": "Grand total invoice amount"
},
"tax_amount": {
"type": "number",
"description": "Tax amount"
},
"tax_rate": {
"type": "number",
"description": "Tax rate percentage"
},
"status": {
"type": "string",
"enum": [
"Draft",
"Pending Review",
"Approved",
"Disputed",
"Under Review",
"Resolved",
"Overdue",
"Paid",
"Reconciled",
"Cancelled"
],
"default": "Draft",
"description": "Current invoice status"
},
"issue_date": {
"type": "string",
"format": "date",
"description": "Invoice issue date"
},
"due_date": {
"type": "string",
"format": "date",
"description": "Payment due date"
},
"paid_date": {
"type": "string",
"format": "date",
"description": "Date payment was received"
},
"payment_method": {
"type": "string",
"enum": [
"Credit Card",
"ACH",
"Wire Transfer",
"Check",
"Cash"
],
"description": "Payment method used"
},
"payment_reference": {
"type": "string",
"description": "Payment reference/transaction ID"
},
"dispute_reason": {
"type": "string",
"description": "Reason for dispute"
},
"dispute_details": {
"type": "string",
"description": "Detailed dispute information"
},
"disputed_items": {
"type": "array",
"description": "List of disputed staff entry indices",
"items": {
"type": "object",
"properties": {
"role_index": {
"type": "number"
},
"staff_index": {
"type": "number"
}
}
}
},
"disputed_by": {
"type": "string",
"description": "User who disputed the invoice"
},
"disputed_date": {
"type": "string",
"format": "date-time",
"description": "When dispute was raised"
},
"resolution_notes": {
"type": "string",
"description": "Notes about dispute resolution"
},
"approved_by": {
"type": "string",
"description": "User who approved the invoice"
},
"approved_date": {
"type": "string",
"format": "date-time",
"description": "When invoice was approved"
},
"is_auto_generated": {
"type": "boolean",
"default": false,
"description": "Whether invoice was auto-generated from event"
},
"pdf_url": {
"type": "string",
"description": "URL to PDF version of invoice"
},
"notes": {
"type": "string",
"description": "Additional notes about the invoice"
},
"internal_notes": {
"type": "string",
"description": "Internal notes not visible to client"
}
},
"required": [
"invoice_number",
"business_name",
"amount",
"status",
"issue_date",
"due_date"
]
}

View File

@@ -1,132 +0,0 @@
{
"name": "InvoiceTemplate",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Template name"
},
"client_name": {
"type": "string",
"description": "Client/Business name"
},
"from_company": {
"type": "object",
"description": "Vendor company info",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "string"
},
"phone": {
"type": "string"
},
"email": {
"type": "string"
}
}
},
"to_company": {
"type": "object",
"description": "Client company info",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "string"
},
"phone": {
"type": "string"
},
"email": {
"type": "string"
},
"hub_name": {
"type": "string"
},
"manager_name": {
"type": "string"
},
"vendor_id": {
"type": "string"
}
}
},
"hub": {
"type": "string",
"description": "Hub location"
},
"department": {
"type": "string",
"description": "Department"
},
"po_reference": {
"type": "string",
"description": "PO reference"
},
"payment_terms": {
"type": "string",
"description": "Payment terms"
},
"staff_entries": {
"type": "array",
"description": "Template staff entries (positions and rates)",
"items": {
"type": "object",
"properties": {
"position": {
"type": "string"
},
"rate": {
"type": "number"
},
"check_in": {
"type": "string"
},
"check_out": {
"type": "string"
},
"lunch": {
"type": "number"
}
}
}
},
"charges": {
"type": "array",
"description": "Template charges",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"qty": {
"type": "number"
},
"rate": {
"type": "number"
}
}
}
},
"staff_count": {
"type": "number",
"description": "Number of staff entries"
},
"charges_count": {
"type": "number",
"description": "Number of charges"
},
"notes": {
"type": "string",
"description": "Template notes"
}
},
"required": [
"name"
]
}

View File

@@ -1,147 +0,0 @@
{
"name": "JobApplication",
"type": "object",
"properties": {
"job_posting_id": {
"type": "string"
},
"job_title": {
"type": "string"
},
"applicant_name": {
"type": "string"
},
"applicant_email": {
"type": "string",
"format": "email"
},
"applicant_phone": {
"type": "string"
},
"years_experience": {
"type": "number",
"default": 0
},
"english_level": {
"type": "string",
"enum": [
"None",
"Basic",
"Intermediate",
"Fluent"
],
"default": "Basic"
},
"certifications": {
"type": "array",
"items": {
"type": "string"
}
},
"availability": {
"type": "array",
"items": {
"type": "string"
}
},
"employment_type_preference": {
"type": "string"
},
"resume_url": {
"type": "string"
},
"cover_letter": {
"type": "string"
},
"linkedin_url": {
"type": "string"
},
"references": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"applied",
"ai_screened",
"shortlisted",
"interview",
"hired",
"rejected"
],
"default": "applied"
},
"ai_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"ai_match_label": {
"type": "string",
"enum": [
"Excellent Match",
"Strong Match",
"Good Match",
"Weak Match",
"Not a Match"
]
},
"ai_summary": {
"type": "string",
"description": "AI-generated vetting summary"
},
"ai_strengths": {
"type": "array",
"items": {
"type": "string"
}
},
"ai_gaps": {
"type": "array",
"items": {
"type": "string"
}
},
"ai_recommendation": {
"type": "string"
},
"score_breakdown": {
"type": "object",
"properties": {
"experience_score": {
"type": "number"
},
"english_score": {
"type": "number"
},
"reliability_score": {
"type": "number"
},
"certifications_score": {
"type": "number"
},
"availability_score": {
"type": "number"
}
}
},
"assessment": {
"type": "object",
"description": "Full AI-generated structured assessment"
},
"rank": {
"type": "number"
},
"vendor_id": {
"type": "string"
},
"notes": {
"type": "string"
}
},
"required": [
"job_posting_id",
"applicant_name",
"applicant_email"
]
}

View File

@@ -1,144 +0,0 @@
{
"name": "JobPosting",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Job title"
},
"role_category": {
"type": "string",
"enum": [
"Server",
"Bartender",
"Chef",
"Line Cook",
"Event Manager",
"Security",
"Busser",
"Host",
"Barback",
"Runner",
"Setup Crew",
"Other"
],
"description": "Role category"
},
"description": {
"type": "string",
"description": "AI-generated or custom job description"
},
"responsibilities": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of responsibilities"
},
"qualifications": {
"type": "array",
"items": {
"type": "string"
},
"description": "Required qualifications"
},
"nice_to_have": {
"type": "array",
"items": {
"type": "string"
},
"description": "Nice to have qualifications"
},
"custom_requirements": {
"type": "string",
"description": "Custom requirements added by admin/vendor"
},
"vetting_criteria": {
"type": "object",
"description": "Scoring weights for vetting",
"properties": {
"experience_weight": {
"type": "number"
},
"english_weight": {
"type": "number"
},
"reliability_weight": {
"type": "number"
},
"certifications_weight": {
"type": "number"
},
"availability_weight": {
"type": "number"
}
}
},
"min_experience_years": {
"type": "number",
"default": 0
},
"english_required": {
"type": "string",
"enum": [
"None",
"Basic",
"Intermediate",
"Fluent"
],
"default": "Basic"
},
"certifications_required": {
"type": "array",
"items": {
"type": "string"
}
},
"employment_types": {
"type": "array",
"items": {
"type": "string"
}
},
"pay_range_min": {
"type": "number"
},
"pay_range_max": {
"type": "number"
},
"location": {
"type": "string"
},
"hub": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"draft",
"active",
"closed",
"paused"
],
"default": "draft"
},
"vendor_id": {
"type": "string"
},
"vendor_name": {
"type": "string"
},
"total_applicants": {
"type": "number",
"default": 0
},
"ai_generated": {
"type": "boolean",
"default": false
}
},
"required": [
"title",
"role_category"
]
}

View File

@@ -1,80 +0,0 @@
{
"name": "KrowEdgeAward",
"type": "object",
"properties": {
"event_id": {
"type": "string"
},
"event_name": {
"type": "string"
},
"event_date": {
"type": "string",
"format": "date"
},
"award_mode": {
"type": "string",
"enum": [
"standard",
"competitive",
"krow_smart"
]
},
"awarded_vendor_id": {
"type": "string"
},
"awarded_vendor_name": {
"type": "string"
},
"awarded_bid_id": {
"type": "string"
},
"final_score": {
"type": "number"
},
"evaluation_method": {
"type": "string"
},
"total_bids": {
"type": "number"
},
"market_avg_price": {
"type": "number"
},
"awarded_price": {
"type": "number"
},
"savings_vs_market": {
"type": "number"
},
"client_priority": {
"type": "string"
},
"risk_tolerance": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"pending_review",
"awarded",
"disputed",
"cancelled"
],
"default": "pending_review"
},
"awarded_by": {
"type": "string"
},
"award_rationale": {
"type": "string"
},
"procurement_notes": {
"type": "string"
}
},
"required": [
"event_id",
"award_mode"
]
}

View File

@@ -1,141 +0,0 @@
{
"name": "KrowEdgeBid",
"type": "object",
"properties": {
"event_id": {
"type": "string",
"description": "Associated event ID"
},
"event_name": {
"type": "string",
"description": "Event name for reference"
},
"event_date": {
"type": "string",
"format": "date"
},
"award_mode": {
"type": "string",
"enum": [
"standard",
"competitive",
"krow_smart"
],
"default": "krow_smart",
"description": "Award strategy selected by client"
},
"client_priority": {
"type": "string",
"enum": [
"cost_optimization",
"balanced",
"execution_certainty"
],
"default": "balanced"
},
"client_risk_tolerance": {
"type": "string",
"enum": [
"low",
"medium",
"aggressive"
],
"default": "low"
},
"transparency_mode": {
"type": "string",
"enum": [
"blind",
"guided"
],
"default": "blind"
},
"vendor_id": {
"type": "string"
},
"vendor_name": {
"type": "string"
},
"price_per_hour": {
"type": "number",
"minimum": 0
},
"total_cost": {
"type": "number",
"minimum": 0
},
"execution_plan_score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Self-declared execution plan quality"
},
"confidence_level": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Vendor self-declared confidence"
},
"notes": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"submitted",
"under_review",
"scored",
"awarded",
"rejected"
],
"default": "submitted"
},
"technical_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"financial_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"risk_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"final_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"rank": {
"type": "number"
},
"evaluation_method": {
"type": "string",
"enum": [
"arithmetic_mean",
"geometric_mean",
"lowest_price",
"krow_optimal"
],
"description": "Randomly selected evaluation method used"
},
"ai_insight": {
"type": "string",
"description": "AI-generated insight about this bid"
},
"market_deviation_pct": {
"type": "number",
"description": "% above or below market average"
}
},
"required": [
"event_id",
"vendor_id",
"price_per_hour",
"total_cost"
]
}

View File

@@ -1,62 +0,0 @@
{
"name": "Message",
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "ID of the conversation"
},
"sender_id": {
"type": "string",
"description": "ID of the sender"
},
"sender_name": {
"type": "string",
"description": "Name of the sender"
},
"sender_role": {
"type": "string",
"enum": [
"client",
"vendor",
"staff",
"admin"
],
"description": "Role of the sender"
},
"content": {
"type": "string",
"description": "Message content"
},
"read_by": {
"type": "array",
"description": "Array of user IDs who have read the message",
"items": {
"type": "string"
}
},
"attachments": {
"type": "array",
"description": "Attached files",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
}
},
"required": [
"conversation_id",
"sender_name",
"content"
]
}

View File

@@ -1,184 +0,0 @@
{
"name": "OperationalMemory",
"type": "object",
"properties": {
"memory_id": {
"type": "string",
"description": "Unique memory identifier (MEM-timestamp-random)"
},
"trigger_event_id": {
"type": "string",
"description": "ID of the WorkforceEvent that triggered this memory"
},
"trigger_event_type": {
"type": "string",
"description": "Type of the triggering event"
},
"context_snapshot": {
"type": "object",
"description": "Graph state at decision time \u2014 entities, relationships, metrics",
"additionalProperties": true
},
"decision": {
"type": "object",
"description": "The AI's decision/recommendation",
"properties": {
"prediction": {
"type": "string"
},
"recommendation": {
"type": "string"
},
"confidence_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"proposed_actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"action_type": {
"type": "string"
},
"description": {
"type": "string"
},
"target_entity_type": {
"type": "string"
},
"target_entity_id": {
"type": "string"
},
"auto_execute": {
"type": "boolean",
"default": false
},
"executed": {
"type": "boolean",
"default": false
}
}
}
}
}
},
"reasoning": {
"type": "string",
"description": "Full reasoning chain \u2014 why the AI made this decision"
},
"causal_chain": {
"type": "array",
"description": "Structured causal chain tracing WHY the outcome occurred \u2014 each factor connected to the next, ending at root cause(s)",
"items": {
"type": "object",
"properties": {
"step": {
"type": "number",
"description": "Order in the causal chain (1 = first contributing factor, last = root cause)"
},
"factor": {
"type": "string",
"description": "The contributing factor (e.g., 'Vendor A supplied fewer experienced cooks')"
},
"evidence": {
"type": "string",
"description": "Data-driven evidence supporting this factor"
},
"category": {
"type": "string",
"enum": [
"vendor",
"operations",
"workforce",
"training",
"demand",
"external",
"compliance",
"finance",
"equipment",
"transportation",
"system"
],
"description": "What area of the operation this factor belongs to"
},
"leads_to": {
"type": "string",
"description": "What this factor caused (the next link in the chain)"
}
}
}
},
"root_causes": {
"type": "array",
"description": "The root cause(s) identified at the end of the causal chain",
"items": {
"type": "string"
}
},
"similar_memories_consulted": {
"type": "array",
"description": "IDs of past memories used as reference",
"items": {
"type": "string"
}
},
"outcome": {
"type": "string",
"description": "What actually happened (filled in during outcome review)"
},
"outcome_score": {
"type": "number",
"description": "Did it work? (-100 = made things worse, 0 = no effect, 100 = helped significantly)"
},
"outcome_metrics": {
"type": "object",
"description": "Quantified outcome data (e.g., overtime_reduction_pct, cost_savings)",
"additionalProperties": true
},
"status": {
"type": "string",
"enum": [
"pending",
"executed",
"outcome_pending",
"learned",
"rejected"
],
"default": "pending"
},
"agent_name": {
"type": "string",
"description": "Which agent made this decision"
},
"department": {
"type": "string",
"enum": [
"operations",
"procurement",
"finance",
"vendor",
"compliance",
"executive"
],
"description": "Department this memory belongs to"
},
"learned_at": {
"type": "string",
"format": "date-time",
"description": "When the outcome was recorded and learning completed"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"memory_id",
"trigger_event_id",
"department"
]
}

View File

@@ -1,169 +0,0 @@
{
"name": "Order",
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "Order Number format ORD-#### (auto-generated)",
"pattern": "^ORD-[0-9]{4,6}$"
},
"partner_id": {
"type": "string",
"description": "Partner/Client ID"
},
"partner_name": {
"type": "string",
"description": "Partner/Client name"
},
"site_id": {
"type": "string",
"description": "Site/location ID"
},
"site_name": {
"type": "string",
"description": "Site/location name"
},
"site_address": {
"type": "string",
"description": "Event/site address"
},
"sector_id": {
"type": "string",
"description": "Sector ID"
},
"enterprise_id": {
"type": "string",
"description": "Enterprise ID"
},
"order_type": {
"type": "string",
"enum": [
"Standard",
"Last Minute",
"Emergency",
"Recurring"
],
"default": "Standard",
"description": "Type of order"
},
"cost_center": {
"type": "string",
"description": "Cost center"
},
"po_number": {
"type": "string",
"description": "Purchase order number"
},
"roles_requested": {
"type": "array",
"description": "Roles and headcount requested",
"items": {
"type": "object",
"properties": {
"role": {
"type": "string"
},
"department": {
"type": "string"
},
"headcount": {
"type": "number"
},
"start_date_time": {
"type": "string",
"format": "date-time"
},
"end_date_time": {
"type": "string",
"format": "date-time"
},
"pay_rate": {
"type": "number"
},
"bill_rate": {
"type": "number"
},
"uniform_required": {
"type": "string"
},
"special_requirements": {
"type": "string"
}
}
}
},
"tags": {
"type": "array",
"description": "Order tags/labels",
"items": {
"type": "string"
}
},
"point_of_contact": {
"type": "object",
"description": "On-site contact",
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
}
},
"sla_targets": {
"type": "object",
"description": "Service Level Agreement targets",
"properties": {
"response_time_hours": {
"type": "number"
},
"fill_rate_percentage": {
"type": "number"
},
"on_time_arrival": {
"type": "boolean"
}
}
},
"order_status": {
"type": "string",
"enum": [
"Draft",
"Submitted",
"Confirmed",
"In Progress",
"Completed",
"Cancelled"
],
"default": "Draft",
"description": "Order status"
},
"submitted_date": {
"type": "string",
"format": "date-time",
"description": "When order was submitted"
},
"confirmed_date": {
"type": "string",
"format": "date-time",
"description": "When order was confirmed"
},
"special_instructions": {
"type": "string",
"description": "Special instructions"
},
"total_estimated_cost": {
"type": "number",
"description": "Estimated total cost"
}
},
"required": [
"order_number",
"partner_id",
"roles_requested"
]
}

View File

@@ -1,60 +0,0 @@
{
"name": "OrderVendorInvite",
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "Order ID"
},
"order_number": {
"type": "string",
"description": "Order number"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"status": {
"type": "string",
"enum": [
"pending",
"accepted",
"declined",
"expired"
],
"default": "pending",
"description": "Invite status"
},
"invited_at": {
"type": "string",
"format": "date-time",
"description": "When vendor was invited"
},
"invited_by": {
"type": "string",
"description": "Who sent the invite"
},
"responded_at": {
"type": "string",
"format": "date-time",
"description": "When vendor responded"
},
"response_note": {
"type": "string",
"description": "Vendor's response note"
},
"expires_at": {
"type": "string",
"format": "date-time",
"description": "When invite expires"
}
},
"required": [
"order_id",
"vendor_id"
]
}

View File

@@ -1,116 +0,0 @@
{
"name": "Partner",
"type": "object",
"properties": {
"partner_name": {
"type": "string",
"description": "Partner/client name (e.g., Google, Nvidia)"
},
"partner_number": {
"type": "string",
"description": "Partner Number format PN-#### (auto-generated)",
"pattern": "^PN-[0-9]{4}$"
},
"partner_type": {
"type": "string",
"enum": [
"Corporate",
"Education",
"Healthcare",
"Sports & Entertainment",
"Government"
],
"description": "Partner type"
},
"sector_id": {
"type": "string",
"description": "Sector ID this partner belongs to"
},
"sector_name": {
"type": "string",
"description": "Sector name"
},
"primary_contact_name": {
"type": "string",
"description": "Primary contact person"
},
"primary_contact_email": {
"type": "string",
"format": "email",
"description": "Primary contact email"
},
"primary_contact_phone": {
"type": "string",
"description": "Primary contact phone"
},
"billing_address": {
"type": "string",
"description": "Billing address"
},
"sites": {
"type": "array",
"description": "List of partner sites/locations",
"items": {
"type": "object",
"properties": {
"site_name": {
"type": "string"
},
"address": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"zip_code": {
"type": "string"
},
"site_manager": {
"type": "string"
},
"site_manager_email": {
"type": "string"
}
}
}
},
"allowed_vendors": {
"type": "array",
"description": "List of vendor IDs allowed for this partner",
"items": {
"type": "string"
}
},
"rate_exceptions": {
"type": "array",
"description": "Partner-specific rate exceptions",
"items": {
"type": "object"
}
},
"cost_centers": {
"type": "array",
"description": "Cost centers/PO numbers",
"items": {
"type": "string"
}
},
"payment_terms": {
"type": "string",
"description": "Payment terms (e.g., Net 30)",
"default": "Net 30"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is partner active"
}
},
"required": [
"partner_name",
"partner_number"
]
}

View File

@@ -1,162 +0,0 @@
{
"name": "PayrollRecord",
"type": "object",
"properties": {
"pay_period_start": {
"type": "string",
"format": "date",
"description": "Start of pay period"
},
"pay_period_end": {
"type": "string",
"format": "date",
"description": "End of pay period"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"payroll_status": {
"type": "string",
"enum": [
"Draft",
"Processing",
"Approved",
"Paid"
],
"default": "Draft",
"description": "Overall payroll batch status"
},
"entries": {
"type": "array",
"description": "Individual employee payroll entries",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"staff_name": {
"type": "string"
},
"email": {
"type": "string"
},
"position": {
"type": "string"
},
"regular_hours": {
"type": "number"
},
"overtime_hours": {
"type": "number"
},
"double_time_hours": {
"type": "number"
},
"regular_rate": {
"type": "number"
},
"overtime_rate": {
"type": "number"
},
"double_time_rate": {
"type": "number"
},
"regular_pay": {
"type": "number"
},
"overtime_pay": {
"type": "number"
},
"double_time_pay": {
"type": "number"
},
"gross_pay": {
"type": "number"
},
"timecard_status": {
"type": "string",
"enum": [
"employee_pending",
"employee_confirmed",
"employee_edited",
"approved",
"paid"
],
"default": "employee_pending"
},
"timecard_sent_at": {
"type": "string",
"format": "date-time"
},
"employee_response_at": {
"type": "string",
"format": "date-time"
},
"employee_edit_notes": {
"type": "string"
},
"approved_at": {
"type": "string",
"format": "date-time"
},
"approved_by": {
"type": "string"
},
"paid_at": {
"type": "string",
"format": "date-time"
},
"shifts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"event_name": {
"type": "string"
},
"date": {
"type": "string",
"format": "date"
},
"clock_in": {
"type": "string"
},
"clock_out": {
"type": "string"
},
"hours": {
"type": "number"
},
"role": {
"type": "string"
}
}
}
}
}
}
},
"total_regular_pay": {
"type": "number"
},
"total_overtime_pay": {
"type": "number"
},
"total_gross_pay": {
"type": "number"
},
"notes": {
"type": "string"
}
},
"required": [
"pay_period_start",
"pay_period_end"
]
}

View File

@@ -1,169 +0,0 @@
{
"name": "RateCard",
"type": "object",
"properties": {
"rate_card_number": {
"type": "string",
"description": "Rate Card Number format RC-#### (auto-generated)",
"pattern": "^RC-[0-9]{4}$"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"partner_id": {
"type": "string",
"description": "Partner ID (if partner-specific)"
},
"partner_name": {
"type": "string",
"description": "Partner name (if partner-specific)"
},
"sector_id": {
"type": "string",
"description": "Sector ID (if sector-specific)"
},
"effective_start_date": {
"type": "string",
"format": "date",
"description": "Rate card effective start date"
},
"effective_end_date": {
"type": "string",
"format": "date",
"description": "Rate card effective end date"
},
"region": {
"type": "string",
"description": "Geographic region (e.g., 'San Francisco Bay Area')"
},
"state": {
"type": "string",
"description": "State"
},
"role": {
"type": "string",
"description": "Role/position"
},
"category": {
"type": "string",
"enum": [
"Kitchen and Culinary",
"Concessions",
"Facilities",
"Bartending",
"Security",
"Event Staff",
"Management",
"Technical",
"Other"
],
"description": "Role category"
},
"pay_rate": {
"type": "number",
"minimum": 0,
"description": "Worker pay rate per hour"
},
"bill_rate": {
"type": "number",
"minimum": 0,
"description": "Client bill rate per hour"
},
"markup_amount": {
"type": "number",
"description": "Markup amount in dollars"
},
"markup_percentage": {
"type": "number",
"description": "Markup as percentage"
},
"overtime_pay_rate": {
"type": "number",
"description": "Overtime pay rate (typically 1.5x)"
},
"overtime_bill_rate": {
"type": "number",
"description": "Overtime bill rate"
},
"overtime_rules": {
"type": "object",
"description": "Overtime rules",
"properties": {
"daily_threshold_hours": {
"type": "number"
},
"weekly_threshold_hours": {
"type": "number"
},
"overtime_multiplier": {
"type": "number",
"default": 1.5
}
}
},
"exceptions": {
"type": "array",
"description": "Rate exceptions",
"items": {
"type": "object",
"properties": {
"condition": {
"type": "string"
},
"exception_rate": {
"type": "number"
},
"notes": {
"type": "string"
}
}
}
},
"minimum_hours": {
"type": "number",
"description": "Minimum billable hours"
},
"travel_rate": {
"type": "number",
"description": "Travel reimbursement rate"
},
"meal_allowance": {
"type": "number",
"description": "Meal allowance if applicable"
},
"uniform_charge": {
"type": "number",
"description": "Uniform charge if applicable"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is rate card currently active"
},
"approved_by": {
"type": "string",
"description": "Who approved this rate card"
},
"approved_date": {
"type": "string",
"format": "date",
"description": "Approval date"
},
"notes": {
"type": "string",
"description": "Rate card notes"
}
},
"required": [
"vendor_id",
"role",
"pay_rate",
"bill_rate",
"effective_start_date"
]
}

View File

@@ -1,68 +0,0 @@
{
"name": "ReputationEvent",
"type": "object",
"properties": {
"worker_id": {
"type": "string",
"description": "ID of the workforce member"
},
"worker_name": {
"type": "string",
"description": "Name of the workforce member"
},
"event_type": {
"type": "string",
"enum": [
"shift_completed",
"no_show",
"late_arrival",
"early_departure",
"client_rated",
"supervisor_rated",
"certification_verified",
"last_minute_cancel",
"commendation"
],
"description": "Type of reputation event"
},
"event_date": {
"type": "string",
"format": "date",
"description": "Date the event occurred"
},
"agency_id": {
"type": "string",
"description": "Agency that submitted this event"
},
"agency_name": {
"type": "string",
"description": "Name of submitting agency"
},
"rating": {
"type": "number",
"minimum": 1,
"maximum": 5,
"description": "Star rating (for client_rated / supervisor_rated events)"
},
"role": {
"type": "string",
"description": "Role worked during this event"
},
"verified": {
"type": "boolean",
"default": true,
"description": "Whether KROW has verified this event"
},
"notes": {
"type": "string",
"description": "Optional non-sensitive note"
}
},
"required": [
"worker_id",
"worker_name",
"event_type",
"event_date",
"agency_name"
]
}

View File

@@ -1,90 +0,0 @@
{
"name": "SaleRecord",
"type": "object",
"properties": {
"invoice_id": {
"type": "string",
"description": "Linked invoice ID"
},
"invoice_number": {
"type": "string",
"description": "Invoice number"
},
"vendor_id": {
"type": "string"
},
"vendor_name": {
"type": "string"
},
"client_name": {
"type": "string"
},
"event_name": {
"type": "string"
},
"event_date": {
"type": "string",
"format": "date"
},
"gross_amount": {
"type": "number",
"description": "Total invoice amount (revenue)"
},
"vendor_fee_percentage": {
"type": "number",
"description": "Platform/network fee %"
},
"vendor_fee_amount": {
"type": "number",
"description": "Calculated fee amount"
},
"net_amount": {
"type": "number",
"description": "Net after vendor fee"
},
"payment_status": {
"type": "string",
"enum": [
"Unpaid",
"Partial",
"Paid"
],
"default": "Unpaid"
},
"amount_paid": {
"type": "number",
"default": 0
},
"balance_due": {
"type": "number"
},
"payment_history": {
"type": "array",
"items": {
"type": "object",
"properties": {
"payment_date": {
"type": "string",
"format": "date"
},
"amount": {
"type": "number"
},
"method": {
"type": "string"
},
"reference": {
"type": "string"
},
"recorded_by": {
"type": "string"
}
}
}
}
},
"required": [
"invoice_id",
"gross_amount"
]
}

View File

@@ -1,285 +0,0 @@
{
"name": "Scorecard",
"type": "object",
"properties": {
"scorecard_number": {
"type": "string",
"description": "Scorecard Number format SC-#### (auto-generated)",
"pattern": "^SC-[0-9]{4}$"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"period_type": {
"type": "string",
"enum": [
"Monthly",
"Quarterly",
"Annual",
"All Time"
],
"description": "Reporting period"
},
"period_start": {
"type": "string",
"format": "date",
"description": "Period start date"
},
"period_end": {
"type": "string",
"format": "date",
"description": "Period end date"
},
"kpis": {
"type": "object",
"description": "Key Performance Indicators",
"properties": {
"fill_rate": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"target": {
"type": "number"
},
"status": {
"type": "string",
"enum": [
"Exceeds",
"Meets",
"Below"
]
}
}
},
"on_time_arrival": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"target": {
"type": "number"
},
"status": {
"type": "string"
}
}
},
"cancellation_rate": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"target": {
"type": "number"
},
"status": {
"type": "string"
}
}
},
"no_show_rate": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"target": {
"type": "number"
},
"status": {
"type": "string"
}
}
},
"average_rating": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"target": {
"type": "number"
},
"status": {
"type": "string"
}
}
},
"response_time_hours": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"target": {
"type": "number"
},
"status": {
"type": "string"
}
}
},
"compliance_score": {
"type": "object",
"properties": {
"value": {
"type": "number"
},
"target": {
"type": "number"
},
"status": {
"type": "string"
}
}
}
}
},
"sla_results": {
"type": "object",
"description": "SLA Performance Results",
"properties": {
"orders_received": {
"type": "number"
},
"orders_filled": {
"type": "number"
},
"orders_partially_filled": {
"type": "number"
},
"orders_unfilled": {
"type": "number"
},
"total_shifts": {
"type": "number"
},
"shifts_completed": {
"type": "number"
},
"shifts_cancelled": {
"type": "number"
},
"avg_response_time": {
"type": "number"
}
}
},
"trends": {
"type": "array",
"description": "Performance trends over time",
"items": {
"type": "object",
"properties": {
"metric": {
"type": "string"
},
"current_value": {
"type": "number"
},
"previous_value": {
"type": "number"
},
"change_percentage": {
"type": "number"
},
"trend_direction": {
"type": "string",
"enum": [
"Up",
"Down",
"Stable"
]
}
}
}
},
"overall_score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Overall performance score (0-100)"
},
"grade": {
"type": "string",
"enum": [
"A+",
"A",
"B+",
"B",
"C+",
"C",
"D",
"F"
],
"description": "Performance grade"
},
"strengths": {
"type": "array",
"description": "Identified strengths",
"items": {
"type": "string"
}
},
"areas_for_improvement": {
"type": "array",
"description": "Areas needing improvement",
"items": {
"type": "string"
}
},
"action_items": {
"type": "array",
"description": "Recommended action items",
"items": {
"type": "object",
"properties": {
"item": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"High",
"Medium",
"Low"
]
},
"due_date": {
"type": "string",
"format": "date"
}
}
}
},
"reviewed_by": {
"type": "string",
"description": "Who reviewed this scorecard"
},
"reviewed_date": {
"type": "string",
"format": "date",
"description": "Review date"
},
"next_review_date": {
"type": "string",
"format": "date",
"description": "Next scheduled review"
}
},
"required": [
"vendor_id",
"period_type",
"period_start",
"period_end"
]
}

View File

@@ -1,171 +0,0 @@
{
"name": "Sector",
"type": "object",
"properties": {
"sector_number": {
"type": "string",
"description": "Sector Number format SN-#### (auto-generated)",
"pattern": "^SN-[0-9]{4}$"
},
"customer_group": {
"type": "string",
"description": "Customer group name"
},
"sector_code": {
"type": "string",
"description": "Short code identifier"
},
"sector_name": {
"type": "string",
"description": "Sector/brand name (e.g., Bon App\u00e9tit, Flik, Eurest)"
},
"division_code": {
"type": "string",
"description": "Division code"
},
"division": {
"type": "string",
"description": "Division name"
},
"region_code": {
"type": "string",
"description": "Region code"
},
"region": {
"type": "string",
"description": "Region name"
},
"district_code": {
"type": "string",
"description": "District code"
},
"district": {
"type": "string",
"description": "District name"
},
"customer_code": {
"type": "string",
"description": "Customer code"
},
"customer": {
"type": "string",
"description": "Customer name"
},
"main_contact_address1": {
"type": "string",
"description": "Main contact address line 1"
},
"main_contact_address2": {
"type": "string",
"description": "Main contact address line 2"
},
"main_contact_city": {
"type": "string",
"description": "Main contact city"
},
"main_contact_state": {
"type": "string",
"description": "Main contact state"
},
"main_contact_postal_code": {
"type": "string",
"description": "Main contact postal/ZIP code"
},
"financial_code": {
"type": "string",
"description": "Financial code"
},
"account_manager": {
"type": "string",
"description": "Account manager name"
},
"account_manager_department": {
"type": "string",
"description": "Account manager department"
},
"division_manager": {
"type": "string",
"description": "Division manager name"
},
"division_manager_department": {
"type": "string",
"description": "Division manager department"
},
"business_channels": {
"type": "array",
"description": "Business channels for this sector",
"items": {
"type": "string"
}
},
"new_addition": {
"type": "string",
"description": "New addition flag or info"
},
"parent_enterprise_id": {
"type": "string",
"description": "Parent enterprise ID"
},
"parent_enterprise_name": {
"type": "string",
"description": "Parent enterprise name"
},
"sector_type": {
"type": "string",
"enum": [
"Food Service",
"Facilities",
"Healthcare",
"Education",
"Corporate",
"Sports & Entertainment"
],
"description": "Sector business type"
},
"client_portfolio": {
"type": "array",
"description": "List of partner/client IDs in this sector",
"items": {
"type": "string"
}
},
"sector_policies": {
"type": "object",
"description": "Sector-specific policies",
"properties": {
"uniform_requirements": {
"type": "string"
},
"certification_requirements": {
"type": "array",
"items": {
"type": "string"
}
},
"special_training": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"approved_vendors": {
"type": "array",
"description": "List of approved vendor IDs for this sector",
"items": {
"type": "string"
}
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is sector active"
}
},
"required": [
"sector_name",
"sector_code",
"sector_number"
]
}

View File

@@ -1,93 +0,0 @@
{
"name": "Shift",
"type": "object",
"properties": {
"event_id": {
"type": "string",
"description": "Associated event ID"
},
"shift_name": {
"type": "string",
"description": "Name of the shift"
},
"manager_id": {
"type": "string",
"description": "Manager staff ID"
},
"manager_name": {
"type": "string",
"description": "Manager name"
},
"location": {
"type": "string",
"description": "Shift location"
},
"start_date": {
"type": "string",
"format": "date-time",
"description": "Shift start date and time"
},
"end_date": {
"type": "string",
"format": "date-time",
"description": "Shift end date and time"
},
"unpaid_break": {
"type": "number",
"description": "Unpaid break duration in minutes"
},
"count": {
"type": "number",
"description": "Number of staff needed"
},
"assigned": {
"type": "number",
"description": "Number of staff assigned"
},
"uniform_type": {
"type": "string",
"description": "Required uniform type"
},
"price": {
"type": "number",
"description": "Price per staff member"
},
"amount": {
"type": "number",
"description": "Total amount for shift"
},
"assigned_staff": {
"type": "array",
"description": "List of assigned staff",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"staff_name": {
"type": "string"
},
"role": {
"type": "string"
},
"department": {
"type": "string"
}
}
}
},
"role": {
"type": "string",
"description": "Role for this shift"
},
"department": {
"type": "string",
"description": "Department"
}
},
"required": [
"shift_name",
"start_date"
]
}

View File

@@ -1,148 +0,0 @@
{
"name": "ShiftOutcome",
"type": "object",
"properties": {
"event_id": {
"type": "string",
"description": "Associated event ID"
},
"event_name": {
"type": "string",
"description": "Event name for reference"
},
"event_date": {
"type": "string",
"format": "date",
"description": "Date of the event"
},
"business_name": {
"type": "string",
"description": "Client business name"
},
"hub_location": {
"type": "string",
"description": "Location where shift occurred"
},
"event_type": {
"type": "string",
"description": "Type of event (lunch, dinner, VIP, etc.)"
},
"expected_covers": {
"type": "number",
"description": "Expected covers/volume for this event"
},
"actual_covers": {
"type": "number",
"description": "Actual covers served"
},
"staff_requested": {
"type": "number",
"description": "Staff requested for the event"
},
"staff_actual": {
"type": "number",
"description": "Staff who actually showed up"
},
"no_show_count": {
"type": "number",
"default": 0,
"description": "Number of no-shows"
},
"late_arrival_count": {
"type": "number",
"default": 0,
"description": "Number of late arrivals"
},
"ot_hours_total": {
"type": "number",
"default": 0,
"description": "Total overtime hours across all staff"
},
"client_rating": {
"type": "number",
"minimum": 1,
"maximum": 5,
"description": "Client rating after shift (1-5 stars)"
},
"client_feedback": {
"type": "string",
"description": "Client written feedback"
},
"qa_score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Internal QA score (0-100)"
},
"issues_reported": {
"type": "array",
"description": "Issues reported during or after the shift",
"items": {
"type": "object",
"properties": {
"issue_type": {
"type": "string",
"enum": [
"late_food",
"understaffed",
"slow_service",
"no_show",
"quality",
"communication",
"other"
]
},
"description": {
"type": "string"
},
"severity": {
"type": "string",
"enum": [
"low",
"medium",
"high",
"critical"
]
}
}
}
},
"prediction_matched": {
"type": "boolean",
"description": "Did the actual demand match the prediction?"
},
"predicted_staff": {
"type": "number",
"description": "How many staff KROW predicted were needed"
},
"was_overstaffed": {
"type": "boolean",
"description": "Was the event overstaffed?"
},
"was_understaffed": {
"type": "boolean",
"description": "Was the event understaffed?"
},
"vendor_id": {
"type": "string",
"description": "Vendor who staffed this event"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"submitted_by": {
"type": "string",
"description": "Who submitted this outcome report"
},
"notes": {
"type": "string",
"description": "Internal notes about this outcome"
}
},
"required": [
"event_id",
"event_name",
"event_date"
]
}

View File

@@ -1,101 +0,0 @@
{
"name": "ShiftProposal",
"type": "object",
"properties": {
"event_id": {
"type": "string",
"description": "Event/shift being proposed"
},
"event_name": {
"type": "string",
"description": "Event name"
},
"staff_id": {
"type": "string",
"description": "Worker being proposed to"
},
"staff_name": {
"type": "string",
"description": "Worker name"
},
"vendor_id": {
"type": "string",
"description": "Vendor making the proposal"
},
"proposal_status": {
"type": "string",
"enum": [
"PENDING_WORKER_CONFIRMATION",
"ACCEPTED",
"DECLINED",
"EXPIRED",
"CANCELLED"
],
"default": "PENDING_WORKER_CONFIRMATION",
"description": "Status of the proposal"
},
"shift_date": {
"type": "string",
"format": "date",
"description": "Date of shift"
},
"start_time": {
"type": "string",
"description": "Shift start time"
},
"end_time": {
"type": "string",
"description": "Shift end time"
},
"location": {
"type": "string",
"description": "Shift location"
},
"role": {
"type": "string",
"description": "Role/position"
},
"pay_rate": {
"type": "number",
"description": "Pay rate for shift"
},
"total_pay": {
"type": "number",
"description": "Total estimated pay"
},
"response_deadline": {
"type": "string",
"format": "date-time",
"description": "Deadline for worker response"
},
"proposed_at": {
"type": "string",
"format": "date-time",
"description": "When proposal was sent"
},
"responded_at": {
"type": "string",
"format": "date-time",
"description": "When worker responded"
},
"decline_reason": {
"type": "string",
"description": "Reason for declining"
},
"was_marked_unavailable": {
"type": "boolean",
"default": false,
"description": "Was worker marked unavailable when proposed"
},
"notification_sent": {
"type": "boolean",
"default": false,
"description": "Whether notification was sent"
}
},
"required": [
"event_id",
"staff_id",
"shift_date"
]
}

View File

@@ -1,56 +0,0 @@
{
"name": "Site",
"type": "object",
"properties": {
"site_name": {
"type": "string",
"description": "Site name"
},
"partner_id": {
"type": "string",
"description": "Partner ID this site belongs to"
},
"partner_name": {
"type": "string",
"description": "Partner name"
},
"address": {
"type": "string",
"description": "Site address"
},
"city": {
"type": "string",
"description": "City"
},
"state": {
"type": "string",
"description": "State"
},
"zip_code": {
"type": "string",
"description": "ZIP code"
},
"site_manager": {
"type": "string",
"description": "Site manager name"
},
"site_manager_email": {
"type": "string",
"format": "email",
"description": "Site manager email"
},
"site_manager_phone": {
"type": "string",
"description": "Site manager phone"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is site active"
}
},
"required": [
"site_name",
"partner_id"
]
}

View File

@@ -1,259 +0,0 @@
{
"name": "Staff",
"type": "object",
"properties": {
"employee_name": {
"type": "string",
"description": "Full name of the employee"
},
"vendor_id": {
"type": "string",
"description": "ID of the vendor this staff belongs to"
},
"vendor_name": {
"type": "string",
"description": "Name of the vendor company"
},
"manager": {
"type": "string",
"description": "Manager's name"
},
"contact_number": {
"type": "string",
"description": "Primary contact number"
},
"phone": {
"type": "string",
"description": "Additional phone number"
},
"email": {
"type": "string",
"format": "email",
"description": "Employee email address"
},
"department": {
"type": "string",
"enum": [
"Operations",
"Sales",
"HR",
"Finance",
"IT",
"Marketing",
"Customer Service",
"Logistics"
],
"description": "Department"
},
"hub_location": {
"type": "string"
},
"event_location": {
"type": "string"
},
"track": {
"type": "string"
},
"address": {
"type": "string"
},
"city": {
"type": "string"
},
"position": {
"type": "string",
"description": "Primary job position/skill"
},
"position_2": {
"type": "string",
"description": "Secondary job position/skill"
},
"initial": {
"type": "string"
},
"profile_type": {
"type": "string",
"enum": [
"Skilled",
"Beginner",
"Cross-Trained"
]
},
"employment_type": {
"type": "string",
"enum": [
"Full Time",
"Part Time",
"On call",
"Weekends",
"Specific Days",
"Seasonal",
"Medical Leave"
]
},
"english": {
"type": "string",
"enum": [
"Fluent",
"Intermediate",
"Basic",
"None"
]
},
"english_required": {
"type": "boolean"
},
"check_in": {
"type": "string",
"format": "date"
},
"replaced_by": {
"type": "string"
},
"ro": {
"type": "string"
},
"mon": {
"type": "string"
},
"schedule_days": {
"type": "string"
},
"invoiced": {
"type": "boolean"
},
"action": {
"type": "string"
},
"notes": {
"type": "string"
},
"accounting_comments": {
"type": "string"
},
"pay_rate": {
"type": "number",
"minimum": 0,
"description": "Employee base pay rate per hour"
},
"pay_rate_overtime": {
"type": "number",
"minimum": 0,
"description": "Overtime pay rate per hour (1.5x)"
},
"pay_rate_double_time": {
"type": "number",
"minimum": 0,
"description": "Double-time pay rate per hour (2x)"
},
"pay_rate_type": {
"type": "string",
"enum": [
"Hourly",
"Salary",
"1099"
],
"default": "Hourly"
},
"pay_rate_effective_date": {
"type": "string",
"format": "date"
},
"position_pay_rates": {
"type": "array",
"description": "Per-position pay rates \u2014 different rates based on the role/position worked",
"items": {
"type": "object",
"properties": {
"position": {
"type": "string",
"description": "Position/role name"
},
"pay_rate": {
"type": "number",
"minimum": 0,
"description": "Base pay for this position"
},
"pay_rate_overtime": {
"type": "number",
"minimum": 0
},
"pay_rate_double_time": {
"type": "number",
"minimum": 0
},
"notes": {
"type": "string"
}
}
}
},
"rating": {
"type": "number",
"minimum": 0,
"maximum": 5
},
"shift_coverage_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"cancellation_count": {
"type": "number",
"minimum": 0
},
"no_show_count": {
"type": "number",
"minimum": 0
},
"total_shifts": {
"type": "number",
"minimum": 0
},
"reliability_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"background_check_status": {
"type": "string",
"enum": [
"pending",
"cleared",
"failed",
"expired",
"not_required"
],
"default": "pending"
},
"background_check_date": {
"type": "string",
"format": "date"
},
"certifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"issued_date": {
"type": "string",
"format": "date"
},
"expiry_date": {
"type": "string",
"format": "date"
},
"issuer": {
"type": "string"
}
}
}
}
},
"required": [
"employee_name"
]
}

View File

@@ -1,124 +0,0 @@
{
"name": "Task",
"type": "object",
"properties": {
"task_name": {
"type": "string",
"description": "Name of the task"
},
"description": {
"type": "string",
"description": "Task description"
},
"team_id": {
"type": "string",
"description": "ID of the team this task belongs to"
},
"status": {
"type": "string",
"enum": [
"pending",
"in_progress",
"on_hold",
"completed"
],
"default": "pending",
"description": "Current status of the task"
},
"priority": {
"type": "string",
"enum": [
"low",
"normal",
"high"
],
"default": "normal",
"description": "Task priority"
},
"due_date": {
"type": "string",
"format": "date",
"description": "Due date"
},
"progress": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 0,
"description": "Task completion percentage"
},
"assigned_members": {
"type": "array",
"description": "Team members assigned to this task",
"items": {
"type": "object",
"properties": {
"member_id": {
"type": "string"
},
"member_name": {
"type": "string"
},
"avatar_url": {
"type": "string"
}
}
}
},
"owner_id": {
"type": "string",
"description": "Task owner ID"
},
"owner_name": {
"type": "string",
"description": "Task owner name"
},
"files": {
"type": "array",
"description": "Attached files",
"items": {
"type": "object",
"properties": {
"file_name": {
"type": "string"
},
"file_url": {
"type": "string"
},
"file_size": {
"type": "number"
},
"uploaded_by": {
"type": "string"
},
"uploaded_at": {
"type": "string",
"format": "date-time"
}
}
}
},
"attachment_count": {
"type": "number",
"default": 0,
"description": "Number of attachments"
},
"comment_count": {
"type": "number",
"default": 0,
"description": "Number of comments"
},
"notes": {
"type": "string",
"description": "Additional notes"
},
"order_index": {
"type": "number",
"description": "Order within the column"
}
},
"required": [
"task_name",
"team_id"
]
}

View File

@@ -1,36 +0,0 @@
{
"name": "TaskComment",
"type": "object",
"properties": {
"task_id": {
"type": "string",
"description": "ID of the task this comment belongs to"
},
"author_id": {
"type": "string",
"description": "ID of the comment author"
},
"author_name": {
"type": "string",
"description": "Name of the comment author"
},
"author_avatar": {
"type": "string",
"description": "Avatar URL of the author"
},
"comment": {
"type": "string",
"description": "Comment text"
},
"is_system": {
"type": "boolean",
"default": false,
"description": "Is this a system message (e.g., status change)"
}
},
"required": [
"task_id",
"author_name",
"comment"
]
}

View File

@@ -1,146 +0,0 @@
{
"name": "Team",
"type": "object",
"properties": {
"team_name": {
"type": "string",
"description": "Name of the team"
},
"owner_id": {
"type": "string",
"description": "ID of the team owner (user)"
},
"owner_name": {
"type": "string",
"description": "Name of the team owner"
},
"owner_role": {
"type": "string",
"enum": [
"admin",
"procurement",
"operator",
"sector",
"client",
"vendor",
"workforce"
],
"description": "Role of the team owner"
},
"company_logo": {
"type": "string",
"description": "URL to company logo"
},
"full_name": {
"type": "string",
"description": "Full name of primary contact"
},
"email": {
"type": "string",
"format": "email",
"description": "Contact email"
},
"phone": {
"type": "string",
"description": "Contact phone"
},
"address": {
"type": "string",
"description": "Company address"
},
"city": {
"type": "string",
"description": "City"
},
"zip_code": {
"type": "string",
"description": "ZIP code"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID if applicable"
},
"departments": {
"type": "array",
"description": "List of departments available for this team",
"items": {
"type": "string"
}
},
"total_members": {
"type": "number",
"default": 0,
"description": "Total team members"
},
"active_members": {
"type": "number",
"default": 0,
"description": "Active team members"
},
"total_hubs": {
"type": "number",
"default": 0,
"description": "Total hubs"
},
"favorite_staff": {
"type": "array",
"description": "Array of favorite staff IDs",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"staff_name": {
"type": "string"
},
"position": {
"type": "string"
},
"added_date": {
"type": "string",
"format": "date-time"
}
}
}
},
"blocked_staff": {
"type": "array",
"description": "Array of blocked staff IDs",
"items": {
"type": "object",
"properties": {
"staff_id": {
"type": "string"
},
"staff_name": {
"type": "string"
},
"reason": {
"type": "string"
},
"blocked_date": {
"type": "string",
"format": "date-time"
}
}
}
},
"favorite_staff_count": {
"type": "number",
"default": 0,
"description": "Number of favorite staff"
},
"blocked_staff_count": {
"type": "number",
"default": 0,
"description": "Number of blocked staff"
}
},
"required": [
"team_name",
"owner_id",
"owner_name",
"owner_role"
]
}

View File

@@ -1,69 +0,0 @@
{
"name": "TeamHub",
"type": "object",
"properties": {
"team_id": {
"type": "string",
"description": "ID of the team"
},
"hub_name": {
"type": "string",
"description": "Name of the hub/location (e.g., BVG 300)"
},
"address": {
"type": "string",
"description": "Hub address (e.g., 300 Bayview Dr, Mountain View, CA 94043)"
},
"city": {
"type": "string",
"description": "City"
},
"state": {
"type": "string",
"description": "State"
},
"zip_code": {
"type": "string",
"description": "ZIP code"
},
"manager_name": {
"type": "string",
"description": "Hub manager name"
},
"manager_email": {
"type": "string",
"format": "email",
"description": "Hub manager email"
},
"departments": {
"type": "array",
"description": "Departments within this hub",
"items": {
"type": "object",
"properties": {
"department_name": {
"type": "string",
"description": "Department name (e.g., Catering FOH, Catering BOH)"
},
"cost_center": {
"type": "string",
"description": "Cost center code"
},
"manager_name": {
"type": "string",
"description": "Department manager"
}
}
}
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Whether the hub is active"
}
},
"required": [
"team_id",
"hub_name"
]
}

View File

@@ -1,66 +0,0 @@
{
"name": "TeamMember",
"type": "object",
"properties": {
"team_id": {
"type": "string",
"description": "ID of the team this member belongs to"
},
"member_name": {
"type": "string",
"description": "Name of the team member"
},
"email": {
"type": "string",
"format": "email",
"description": "Member email"
},
"phone": {
"type": "string",
"description": "Member phone"
},
"title": {
"type": "string",
"description": "Job title"
},
"role": {
"type": "string",
"enum": [
"admin",
"manager",
"member",
"viewer"
],
"description": "Role in the team"
},
"department": {
"type": "string",
"description": "Department"
},
"hub": {
"type": "string",
"description": "Assigned hub"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Whether the member is active"
},
"permissions": {
"type": "array",
"description": "Array of permissions",
"items": {
"type": "string"
}
},
"avatar_url": {
"type": "string",
"description": "URL to member avatar"
}
},
"required": [
"team_id",
"member_name",
"email"
]
}

View File

@@ -1,72 +0,0 @@
{
"name": "TeamMemberInvite",
"type": "object",
"properties": {
"team_id": {
"type": "string",
"description": "ID of the team"
},
"team_name": {
"type": "string",
"description": "Name of the team"
},
"invite_code": {
"type": "string",
"description": "Unique invite code"
},
"email": {
"type": "string",
"format": "email",
"description": "Email of invited person"
},
"full_name": {
"type": "string",
"description": "Name of invited person"
},
"role": {
"type": "string",
"enum": [
"admin",
"manager",
"member",
"viewer"
],
"description": "Proposed role"
},
"invited_by": {
"type": "string",
"description": "Who sent the invite"
},
"invite_status": {
"type": "string",
"enum": [
"pending",
"accepted",
"expired",
"cancelled"
],
"default": "pending",
"description": "Invitation status"
},
"invited_date": {
"type": "string",
"format": "date-time",
"description": "When invite was sent"
},
"accepted_date": {
"type": "string",
"format": "date-time",
"description": "When invite was accepted"
},
"expires_at": {
"type": "string",
"format": "date-time",
"description": "When invite expires"
}
},
"required": [
"team_id",
"email",
"invited_by"
]
}

View File

@@ -1,149 +0,0 @@
{
"name": "Training",
"type": "object",
"properties": {
"training_id": {
"type": "string",
"description": "Training record ID"
},
"training_name": {
"type": "string",
"description": "Name of the training program"
},
"training_type": {
"type": "string",
"enum": [
"food_safety",
"alcohol_service",
"customer_service",
"safety",
"onboarding",
"culinary_skills",
"equipment_operation",
"compliance",
"leadership",
"other"
],
"description": "Type of training"
},
"description": {
"type": "string"
},
"workforce_id": {
"type": "string",
"description": "Worker enrolled in this training"
},
"workforce_name": {
"type": "string"
},
"staff_id": {
"type": "string",
"description": "Staff record ID (if different from workforce)"
},
"vendor_id": {
"type": "string",
"description": "Vendor sponsoring this training"
},
"vendor_name": {
"type": "string"
},
"client_id": {
"type": "string",
"description": "Client-specific training (if applicable)"
},
"client_name": {
"type": "string"
},
"instructor_name": {
"type": "string",
"description": "Instructor or training provider"
},
"training_format": {
"type": "string",
"enum": [
"in_person",
"online",
"hybrid",
"on_the_job",
"video"
],
"default": "online"
},
"duration_hours": {
"type": "number",
"minimum": 0,
"description": "Total training duration in hours"
},
"enrollment_date": {
"type": "string",
"format": "date",
"description": "When worker was enrolled"
},
"start_date": {
"type": "string",
"format": "date",
"description": "Training start date"
},
"completion_date": {
"type": "string",
"format": "date",
"description": "Date training was completed"
},
"expiry_date": {
"type": "string",
"format": "date",
"description": "When this training expires and needs renewal"
},
"status": {
"type": "string",
"enum": [
"not_started",
"enrolled",
"in_progress",
"completed",
"failed",
"expired",
"cancelled"
],
"default": "not_started",
"description": "Training status"
},
"score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Score achieved (if graded)"
},
"passing_score": {
"type": "number",
"default": 70,
"description": "Minimum passing score"
},
"certification_earned": {
"type": "string",
"description": "Name of certification earned upon completion"
},
"certification_id": {
"type": "string",
"description": "Reference to Certification entity if one was issued"
},
"cost": {
"type": "number",
"minimum": 0,
"description": "Cost of this training"
},
"billable_to_client": {
"type": "boolean",
"default": false
},
"notes": {
"type": "string"
}
},
"required": [
"training_name",
"training_type",
"workforce_id",
"status"
]
}

View File

@@ -1,139 +0,0 @@
{
"name": "Transportation",
"type": "object",
"properties": {
"transport_id": {
"type": "string",
"description": "Transport record ID"
},
"transport_type": {
"type": "string",
"enum": [
"shuttle",
"van",
"carpool",
"bus",
"public_transit",
"rideshare",
"personal_vehicle",
"walking"
],
"description": "Mode of transportation"
},
"shift_id": {
"type": "string",
"description": "Shift this transport serves"
},
"event_id": {
"type": "string",
"description": "Event this transport serves"
},
"workforce_id": {
"type": "string",
"description": "Worker being transported (if individual)"
},
"workforce_name": {
"type": "string"
},
"vendor_id": {
"type": "string",
"description": "Vendor providing/arranging transport"
},
"vendor_name": {
"type": "string"
},
"pickup_location": {
"type": "string",
"description": "Pickup address or landmark"
},
"dropoff_location": {
"type": "string",
"description": "Dropoff location (typically the site/client location)"
},
"site_id": {
"type": "string",
"description": "Destination Site ID"
},
"client_id": {
"type": "string",
"description": "Client/Business ID at the destination"
},
"client_name": {
"type": "string"
},
"scheduled_pickup_time": {
"type": "string",
"format": "date-time",
"description": "Scheduled pickup time"
},
"scheduled_dropoff_time": {
"type": "string",
"format": "date-time",
"description": "Scheduled dropoff time"
},
"actual_pickup_time": {
"type": "string",
"format": "date-time"
},
"actual_dropoff_time": {
"type": "string",
"format": "date-time"
},
"driver_name": {
"type": "string",
"description": "Driver name (if applicable)"
},
"driver_phone": {
"type": "string"
},
"vehicle_identifier": {
"type": "string",
"description": "License plate or vehicle ID"
},
"capacity": {
"type": "number",
"description": "Number of workers this transport can carry"
},
"passenger_count": {
"type": "number",
"default": 0,
"description": "Actual number of workers transported"
},
"cost_per_trip": {
"type": "number",
"minimum": 0,
"description": "Cost per trip"
},
"billable_to_client": {
"type": "boolean",
"default": false,
"description": "Whether transport cost is billed to client"
},
"status": {
"type": "string",
"enum": [
"scheduled",
"in_progress",
"completed",
"cancelled",
"no_show",
"delayed"
],
"default": "scheduled",
"description": "Transport status"
},
"distance_miles": {
"type": "number",
"description": "Distance in miles"
},
"notes": {
"type": "string"
}
},
"required": [
"transport_type",
"pickup_location",
"dropoff_location",
"status"
]
}

View File

@@ -1,185 +0,0 @@
{
"name": "User",
"type": "object",
"properties": {
"full_name": {
"type": "string",
"description": "Full name of the user"
},
"user_role": {
"type": "string",
"enum": [
"admin",
"procurement",
"operator",
"sector",
"client",
"vendor",
"workforce"
],
"description": "User's role in the system"
},
"company_name": {
"type": "string",
"description": "Company or organization name"
},
"profile_picture": {
"type": "string",
"description": "URL to profile picture"
},
"phone": {
"type": "string",
"description": "Phone number"
},
"address": {
"type": "string",
"description": "Address"
},
"preferred_vendor_id": {
"type": "string",
"description": "ID of the client's preferred/default vendor"
},
"preferred_vendor_name": {
"type": "string",
"description": "Name of the client's preferred vendor"
},
"backup_vendor_ids": {
"type": "array",
"description": "List of backup vendor IDs",
"items": {
"type": "string"
}
},
"dashboard_layout": {
"type": "object",
"description": "User's customized dashboard layout preferences (legacy, kept for backward compatibility)",
"properties": {
"widgets": {
"type": "array",
"description": "Ordered list of visible widgets",
"items": {
"type": "string"
}
},
"hidden_widgets": {
"type": "array",
"description": "List of hidden widgets",
"items": {
"type": "string"
}
},
"layout_version": {
"type": "string",
"description": "Layout version for migration"
}
}
},
"dashboard_layout_client": {
"type": "object",
"description": "Client dashboard layout",
"properties": {
"widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"hidden_widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"layout_version": {
"type": "string"
}
}
},
"dashboard_layout_vendor": {
"type": "object",
"description": "Vendor dashboard layout",
"properties": {
"widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"hidden_widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"layout_version": {
"type": "string"
}
}
},
"dashboard_layout_operator": {
"type": "object",
"description": "Operator dashboard layout",
"properties": {
"widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"hidden_widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"layout_version": {
"type": "string"
}
}
},
"dashboard_layout_workforce": {
"type": "object",
"description": "Workforce dashboard layout",
"properties": {
"widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"hidden_widgets": {
"type": "array",
"items": {
"type": "string"
}
},
"layout_version": {
"type": "string"
}
}
},
"preferences": {
"type": "object",
"description": "User preferences and settings",
"properties": {
"theme": {
"type": "string",
"enum": [
"light",
"dark"
],
"default": "light"
},
"notifications_enabled": {
"type": "boolean",
"default": true
},
"email_notifications": {
"type": "boolean",
"default": true
}
}
}
},
"required": []
}

View File

@@ -1,217 +0,0 @@
{
"name": "Vendor",
"type": "object",
"properties": {
"vendor_number": {
"type": "string",
"description": "Vendor Number format VN-#### (auto-generated)",
"pattern": "^VN-[0-9]{4}$"
},
"legal_name": {
"type": "string",
"description": "Legal business name"
},
"doing_business_as": {
"type": "string",
"description": "DBA / Trade name"
},
"region": {
"type": "string",
"enum": [
"National",
"Bay Area",
"Southern California",
"Northern California",
"West",
"East",
"Midwest",
"South"
],
"description": "Geographic region vendor operates in"
},
"state": {
"type": "string",
"description": "Primary state (or 'Multiple' for multi-state)"
},
"city": {
"type": "string",
"description": "Primary city"
},
"service_specialty": {
"type": "string",
"description": "Service specialty (e.g., Event Staffing, Full Service Events, On-Demand Gig Platform)"
},
"workforce_count": {
"type": "number",
"description": "Total workforce/staff count available"
},
"platform_type": {
"type": "string",
"enum": [
"Full Platform",
"Building platform (KROW)",
"Partial Tech",
"Traditional"
],
"description": "Technology/platform integration level"
},
"tax_id": {
"type": "string",
"description": "Federal Tax ID / EIN"
},
"business_type": {
"type": "string",
"enum": [
"Corporation",
"LLC",
"Partnership",
"Sole Proprietorship"
],
"description": "Business entity type"
},
"primary_contact_name": {
"type": "string",
"description": "Primary contact person"
},
"primary_contact_email": {
"type": "string",
"format": "email",
"description": "Primary contact email"
},
"primary_contact_phone": {
"type": "string",
"description": "Primary contact phone"
},
"billing_address": {
"type": "string",
"description": "Billing address"
},
"service_address": {
"type": "string",
"description": "Service/office address"
},
"coverage_regions": {
"type": "array",
"description": "Geographic regions vendor can service",
"items": {
"type": "string"
}
},
"eligible_roles": {
"type": "array",
"description": "Roles/positions vendor can provide",
"items": {
"type": "string"
}
},
"insurance_certificate": {
"type": "string",
"description": "URL to insurance certificate"
},
"insurance_expiry": {
"type": "string",
"format": "date",
"description": "Insurance expiration date"
},
"w9_document": {
"type": "string",
"description": "URL to W9 form"
},
"coi_document": {
"type": "string",
"description": "URL to Certificate of Insurance"
},
"compliance_package_id": {
"type": "string",
"description": "Reference to compliance package"
},
"performance_scorecard_id": {
"type": "string",
"description": "Reference to performance scorecard"
},
"performance_requirements": {
"type": "array",
"description": "Customized KPIs and service standards the vendor must meet",
"items": {
"type": "object",
"properties": {
"metric": {
"type": "string"
},
"target": {
"type": "string"
},
"operator": {
"type": "string"
},
"unit": {
"type": "string"
},
"enabled": {
"type": "boolean",
"default": true
}
}
}
},
"compliance_requirements": {
"type": "array",
"description": "Customized compliance and attestation requirements the vendor must acknowledge",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean",
"default": true
}
}
}
},
"vendor_fee_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Vendor admin fee percentage set by procurement"
},
"approval_status": {
"type": "string",
"enum": [
"pending",
"approved",
"suspended",
"terminated"
],
"default": "pending",
"description": "Vendor approval status"
},
"approved_date": {
"type": "string",
"format": "date",
"description": "Date vendor was approved"
},
"approved_by": {
"type": "string",
"description": "User who approved vendor"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is vendor currently active"
},
"notes": {
"type": "string",
"description": "Internal notes about vendor"
}
},
"required": [
"legal_name",
"vendor_number",
"primary_contact_email"
]
}

View File

@@ -1,31 +0,0 @@
{
"name": "VendorDefaultSettings",
"type": "object",
"properties": {
"vendor_name": {
"type": "string",
"description": "Name of the vendor"
},
"default_markup_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Default markup percentage for this vendor"
},
"default_vendor_fee_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Default vendor fee percentage for this vendor"
},
"notes": {
"type": "string",
"description": "Additional notes about vendor defaults"
}
},
"required": [
"vendor_name",
"default_markup_percentage",
"default_vendor_fee_percentage"
]
}

View File

@@ -1,147 +0,0 @@
{
"name": "VendorDocumentReview",
"type": "object",
"properties": {
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"document_type": {
"type": "string",
"enum": [
"Contract",
"NDA",
"W9",
"COI",
"Insurance",
"Service Agreement",
"Other"
],
"description": "Type of document being reviewed"
},
"document_url": {
"type": "string",
"description": "URL to the document PDF"
},
"document_name": {
"type": "string",
"description": "Name of the document"
},
"review_notes": {
"type": "string",
"description": "Notes taken by vendor during review"
},
"annotations": {
"type": "array",
"description": "Document annotations (highlights, comments, drawings)",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"highlight",
"comment",
"question",
"emoji",
"signature"
]
},
"page_number": {
"type": "number"
},
"selected_text": {
"type": "string"
},
"comment": {
"type": "string"
},
"emoji": {
"type": "string"
},
"color": {
"type": "string"
},
"position": {
"type": "object",
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
}
}
},
"created_date": {
"type": "string",
"format": "date-time"
}
}
}
},
"questions": {
"type": "array",
"description": "Questions or concerns about the document",
"items": {
"type": "object",
"properties": {
"question": {
"type": "string"
},
"page_number": {
"type": "number"
},
"status": {
"type": "string",
"enum": [
"open",
"answered",
"resolved"
]
},
"answer": {
"type": "string"
}
}
}
},
"reviewed": {
"type": "boolean",
"default": false,
"description": "Has vendor reviewed this document"
},
"reviewed_date": {
"type": "string",
"format": "date-time",
"description": "When vendor marked as reviewed"
},
"acknowledged": {
"type": "boolean",
"default": false,
"description": "Has vendor acknowledged/accepted the document"
},
"acknowledged_date": {
"type": "string",
"format": "date-time",
"description": "When vendor acknowledged the document"
},
"time_spent_minutes": {
"type": "number",
"description": "Total time spent reviewing (in minutes)"
}
},
"required": [
"vendor_name",
"document_type",
"document_url",
"document_name"
]
}

View File

@@ -1,112 +0,0 @@
{
"name": "VendorInvite",
"type": "object",
"properties": {
"invite_code": {
"type": "string",
"description": "Unique invite code (e.g., INV-XXXX)"
},
"company_name": {
"type": "string",
"description": "Vendor company name"
},
"primary_contact_name": {
"type": "string",
"description": "Point of contact name"
},
"primary_contact_email": {
"type": "string",
"format": "email",
"description": "Point of contact email"
},
"vendor_admin_fee": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Vendor admin fee percentage set by procurement"
},
"performance_requirements": {
"type": "array",
"description": "Customized KPIs and service standards the vendor must meet",
"items": {
"type": "object",
"properties": {
"metric": {
"type": "string"
},
"target": {
"type": "string"
},
"operator": {
"type": "string"
},
"unit": {
"type": "string"
},
"enabled": {
"type": "boolean",
"default": true
}
}
}
},
"compliance_requirements": {
"type": "array",
"description": "Customized compliance and attestation requirements the vendor must acknowledge",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean",
"default": true
}
}
}
},
"invited_by": {
"type": "string",
"description": "Email of procurement admin who sent invite"
},
"invite_status": {
"type": "string",
"enum": [
"pending",
"accepted",
"completed",
"expired"
],
"default": "pending",
"description": "Status of the invite"
},
"invite_sent_date": {
"type": "string",
"format": "date-time",
"description": "When the invite was sent"
},
"invite_accepted_date": {
"type": "string",
"format": "date-time",
"description": "When vendor accepted/started onboarding"
},
"vendor_id": {
"type": "string",
"description": "Vendor ID once they complete onboarding"
},
"notes": {
"type": "string",
"description": "Internal notes about this invite"
}
},
"required": [
"company_name",
"primary_contact_email",
"vendor_admin_fee",
"invited_by"
]
}

View File

@@ -1,119 +0,0 @@
{
"name": "VendorNetworkApproval",
"type": "object",
"properties": {
"vendor_id": {
"type": "string",
"description": "ID of the approved vendor"
},
"vendor_name": {
"type": "string",
"description": "Name of the vendor"
},
"enterprise_network": {
"type": "string",
"enum": [
"Compass",
"Aramark",
"Sodexo",
"Foodbuy",
"Bon App\u00e9tit",
"Eurest"
],
"description": "Enterprise network vendor is approved for"
},
"approval_status": {
"type": "string",
"enum": [
"pending",
"approved",
"rejected",
"suspended"
],
"default": "pending",
"description": "Current approval status"
},
"approved_by": {
"type": "string",
"description": "Procurement manager who approved"
},
"approved_date": {
"type": "string",
"format": "date",
"description": "Date of approval"
},
"rate_book_id": {
"type": "string",
"description": "Associated rate book ID for this network"
},
"regions_approved": {
"type": "array",
"description": "Geographic regions vendor is approved to serve",
"items": {
"type": "string"
}
},
"categories_approved": {
"type": "array",
"description": "Service categories vendor is approved for",
"items": {
"type": "string"
}
},
"compliance_documents": {
"type": "array",
"description": "Required compliance documents",
"items": {
"type": "object",
"properties": {
"document_type": {
"type": "string"
},
"document_url": {
"type": "string"
},
"verified": {
"type": "boolean"
}
}
}
},
"restrictions": {
"type": "object",
"description": "Any restrictions on vendor's approved status",
"properties": {
"max_order_size": {
"type": "number"
},
"max_concurrent_events": {
"type": "number"
},
"excluded_clients": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"valid_from": {
"type": "string",
"format": "date",
"description": "Approval start date"
},
"valid_until": {
"type": "string",
"format": "date",
"description": "Approval expiration date"
},
"notes": {
"type": "string",
"description": "Additional notes about approval"
}
},
"required": [
"vendor_name",
"enterprise_network",
"approval_status"
]
}

View File

@@ -1,40 +0,0 @@
{
"name": "VendorPartnerLink",
"type": "object",
"properties": {
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"partner_id": {
"type": "string",
"description": "Partner ID"
},
"partner_name": {
"type": "string",
"description": "Partner name"
},
"assigned_by": {
"type": "string",
"description": "Who assigned this link"
},
"assigned_date": {
"type": "string",
"format": "date",
"description": "When the link was created"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is this link active"
}
},
"required": [
"vendor_id",
"partner_id"
]
}

View File

@@ -1,248 +0,0 @@
{
"name": "VendorRate",
"type": "object",
"properties": {
"vendor_id": {
"type": "string",
"description": "ID of the vendor this rate belongs to"
},
"vendor_name": {
"type": "string",
"description": "Name of the vendor"
},
"rate_book_name": {
"type": "string",
"description": "Name of the rate book (e.g., 'Default', 'Compass Approved', 'Google Caf\u00e9 7')"
},
"rate_book_type": {
"type": "string",
"enum": [
"default",
"enterprise_network",
"client_specific"
],
"description": "Type of rate book: default (vendor's own rates), enterprise_network (Compass/Aramark approved), or client_specific (negotiated)"
},
"enterprise_network": {
"type": "string",
"enum": [
"Compass",
"Aramark",
"Sodexo",
"Foodbuy",
"Bon App\u00e9tit",
"Eurest",
"None"
],
"description": "Enterprise network this rate book belongs to (if applicable)"
},
"is_approved_rate": {
"type": "boolean",
"default": false,
"description": "Whether this is an approved procurement rate (locked for vendors)"
},
"approved_by_procurement": {
"type": "string",
"description": "Name of procurement manager who approved these rates"
},
"approved_date": {
"type": "string",
"format": "date",
"description": "Date rates were approved"
},
"client_id": {
"type": "string",
"description": "Specific client ID if this is a client-specific rate book"
},
"client_name": {
"type": "string",
"description": "Specific client name if this is a client-specific rate book"
},
"category": {
"type": "string",
"enum": [
"Kitchen and Culinary",
"Concessions",
"Facilities",
"Bartending",
"Security",
"Event Staff",
"Management",
"Technical",
"Other"
],
"description": "Service category"
},
"role_name": {
"type": "string",
"description": "Name of the role/position"
},
"region": {
"type": "string",
"description": "Geographic region (e.g., 'Bay Area', 'LA', 'National')"
},
"employee_wage": {
"type": "number",
"minimum": 0,
"description": "Employee base wage per hour (must be >= minimum wage)"
},
"overtime_rate": {
"type": "number",
"minimum": 0,
"description": "Overtime rate (typically 1.5x base wage)"
},
"double_time_rate": {
"type": "number",
"minimum": 0,
"description": "Double-time rate (typically 2x base wage)"
},
"travel_rate": {
"type": "number",
"minimum": 0,
"description": "Travel time rate per hour (if applicable)"
},
"markup_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Markup percentage on employee wage"
},
"vendor_fee_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Vendor fee as percentage on (wage + markup)"
},
"client_rate": {
"type": "number",
"minimum": 0,
"description": "Final rate charged to client per hour"
},
"approved_cap_rate": {
"type": "number",
"description": "Maximum rate approved by enterprise network (vendors cannot exceed this)"
},
"discount_from_approved": {
"type": "number",
"description": "Dollar amount discount from approved cap rate (if vendor is offering lower rate)"
},
"discount_percentage": {
"type": "number",
"description": "Percentage discount from approved cap rate"
},
"is_discounted": {
"type": "boolean",
"default": false,
"description": "Whether vendor is offering a discount below approved rate"
},
"negotiation_allowed": {
"type": "boolean",
"default": true,
"description": "Whether clients can request rate negotiations"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Whether this rate is currently active"
},
"is_editable_by_vendor": {
"type": "boolean",
"default": true,
"description": "Whether vendor can edit this rate (false for locked procurement rates)"
},
"minimum_wage_compliance": {
"type": "boolean",
"description": "Whether this rate meets minimum wage requirements"
},
"market_comparison": {
"type": "object",
"description": "Market comparison data",
"properties": {
"market_average": {
"type": "number",
"description": "Market average for this role"
},
"market_position": {
"type": "string",
"enum": [
"below_market",
"at_market",
"above_market",
"significantly_above"
],
"description": "How this rate compares to market"
},
"competitiveness_score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Competitiveness score (0-100)"
},
"market_warning": {
"type": "string",
"description": "Warning if rate is significantly above or below market"
}
}
},
"compliance": {
"type": "object",
"description": "Compliance information",
"properties": {
"csta_compliant": {
"type": "boolean",
"default": false,
"description": "California Staffing Agency compliance"
},
"minimum_wage_verified": {
"type": "boolean",
"default": false,
"description": "Minimum wage verification status"
},
"compliance_verified_date": {
"type": "string",
"format": "date",
"description": "Date compliance was last verified"
}
}
},
"shift_type": {
"type": "string",
"enum": [
"regular",
"night",
"weekend",
"holiday",
"premium"
],
"default": "regular",
"description": "Type of shift this rate applies to"
},
"valid_from": {
"type": "string",
"format": "date",
"description": "Date this rate becomes valid"
},
"valid_until": {
"type": "string",
"format": "date",
"description": "Date this rate expires"
},
"notes": {
"type": "string",
"description": "Additional notes about this rate"
},
"internal_notes": {
"type": "string",
"description": "Internal vendor notes (not visible to clients)"
}
},
"required": [
"vendor_name",
"rate_book_name",
"rate_book_type",
"category",
"role_name",
"employee_wage",
"client_rate"
]
}

View File

@@ -1,113 +0,0 @@
{
"name": "VendorRateBook",
"type": "object",
"properties": {
"vendor_id": {
"type": "string",
"description": "ID of the vendor who owns this rate book"
},
"vendor_name": {
"type": "string",
"description": "Name of the vendor"
},
"rate_book_name": {
"type": "string",
"description": "Name of the rate book (e.g., 'Default Rates', 'Compass Network', 'Google Special')"
},
"rate_book_type": {
"type": "string",
"enum": [
"default",
"enterprise_network",
"client_specific",
"premium",
"seasonal"
],
"description": "Type of rate book"
},
"enterprise_network": {
"type": "string",
"enum": [
"Compass",
"Aramark",
"Sodexo",
"Foodbuy",
"Bon App\u00e9tit",
"Eurest",
"None"
],
"description": "Enterprise network if applicable"
},
"is_approved_by_procurement": {
"type": "boolean",
"default": false,
"description": "Whether this rate book is approved by procurement"
},
"approved_by": {
"type": "string",
"description": "Name of person who approved this rate book"
},
"approved_date": {
"type": "string",
"format": "date",
"description": "Date of approval"
},
"is_locked": {
"type": "boolean",
"default": false,
"description": "Whether rates in this book are locked (cannot be edited by vendor)"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Whether this rate book is currently active"
},
"client_assignments": {
"type": "array",
"description": "Clients assigned to use this rate book",
"items": {
"type": "object",
"properties": {
"client_id": {
"type": "string"
},
"client_name": {
"type": "string"
},
"assigned_date": {
"type": "string",
"format": "date-time"
}
}
}
},
"valid_from": {
"type": "string",
"format": "date",
"description": "Start date for rate book validity"
},
"valid_until": {
"type": "string",
"format": "date",
"description": "End date for rate book validity"
},
"description": {
"type": "string",
"description": "Description of this rate book"
},
"total_roles": {
"type": "number",
"default": 0,
"description": "Total number of roles in this rate book"
},
"notes": {
"type": "string",
"description": "Additional notes"
}
},
"required": [
"vendor_name",
"rate_book_name",
"rate_book_type"
]
}

View File

@@ -1,40 +0,0 @@
{
"name": "VendorSectorLink",
"type": "object",
"properties": {
"vendor_id": {
"type": "string",
"description": "Vendor ID"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"sector_id": {
"type": "string",
"description": "Sector ID"
},
"sector_name": {
"type": "string",
"description": "Sector name"
},
"assigned_by": {
"type": "string",
"description": "Who assigned this link"
},
"assigned_date": {
"type": "string",
"format": "date",
"description": "When the link was created"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is this link active"
}
},
"required": [
"vendor_id",
"sector_id"
]
}

View File

@@ -1,129 +0,0 @@
{
"name": "WorkerAvailability",
"type": "object",
"properties": {
"staff_id": {
"type": "string",
"description": "Reference to Staff entity"
},
"staff_name": {
"type": "string",
"description": "Staff member name"
},
"availability_status": {
"type": "string",
"enum": [
"CONFIRMED_AVAILABLE",
"UNKNOWN",
"BLOCKED"
],
"default": "UNKNOWN",
"description": "Current availability status"
},
"predicted_availability_score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 50,
"description": "ML/rules-based prediction of availability (0-100)"
},
"need_work_index": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 0,
"description": "How much worker needs hours (0-100)"
},
"scheduled_hours_this_period": {
"type": "number",
"default": 0,
"description": "Hours scheduled in current period"
},
"desired_hours_this_period": {
"type": "number",
"default": 40,
"description": "Hours worker wants in current period"
},
"last_shift_date": {
"type": "string",
"format": "date",
"description": "Date of last completed shift"
},
"acceptance_rate": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 0,
"description": "Historical acceptance rate percentage"
},
"utilization_percentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 0,
"description": "Scheduled hours / desired hours * 100"
},
"days_since_last_shift": {
"type": "number",
"default": 0,
"description": "Days since last shift"
},
"total_shifts_this_period": {
"type": "number",
"default": 0,
"description": "Number of shifts in current period"
},
"available_dates": {
"type": "array",
"description": "Dates worker is confirmed available",
"items": {
"type": "string",
"format": "date"
}
},
"blocked_dates": {
"type": "array",
"description": "Dates worker is blocked/unavailable",
"items": {
"type": "string",
"format": "date"
}
},
"preferences": {
"type": "object",
"description": "Worker preferences",
"properties": {
"preferred_days": {
"type": "array",
"items": {
"type": "string"
}
},
"preferred_shift_types": {
"type": "array",
"items": {
"type": "string",
"enum": [
"Morning",
"Day",
"Evening",
"Night",
"Overnight"
]
}
},
"max_commute_miles": {
"type": "number"
},
"wants_more_hours": {
"type": "boolean",
"default": false
}
}
}
},
"required": [
"staff_id",
"staff_name"
]
}

View File

@@ -1,99 +0,0 @@
{
"name": "WorkerReputationProfile",
"type": "object",
"properties": {
"worker_id": {
"type": "string",
"description": "Linked workforce member ID"
},
"worker_name": {
"type": "string",
"description": "Full name"
},
"total_shifts": {
"type": "number",
"default": 0,
"description": "Total verified completed shifts across all agencies"
},
"no_shows": {
"type": "number",
"default": 0
},
"late_arrivals": {
"type": "number",
"default": 0
},
"last_minute_cancels": {
"type": "number",
"default": 0
},
"attendance_rate": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Attendance percentage (0-100)"
},
"avg_client_rating": {
"type": "number",
"minimum": 0,
"maximum": 5
},
"avg_supervisor_rating": {
"type": "number",
"minimum": 0,
"maximum": 5
},
"verified_skills": {
"type": "array",
"items": {
"type": "string"
},
"description": "Verified skill/role tags"
},
"verified_certifications": {
"type": "array",
"items": {
"type": "string"
}
},
"contributing_agencies": {
"type": "number",
"default": 0,
"description": "Number of agencies that have contributed data"
},
"trust_score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Computed KROW trust score (0-100)"
},
"trust_tier": {
"type": "string",
"enum": [
"Bronze",
"Silver",
"Gold",
"Platinum"
],
"default": "Bronze"
},
"opted_in": {
"type": "boolean",
"default": true,
"description": "Worker has opted into the reputation network"
},
"profile_visibility": {
"type": "string",
"enum": [
"public",
"network_only",
"private"
],
"default": "network_only"
}
},
"required": [
"worker_id",
"worker_name"
]
}

View File

@@ -1,244 +0,0 @@
{
"name": "Workforce",
"type": "object",
"properties": {
"workforce_number": {
"type": "string",
"description": "Worker Number format WF-#### (auto-generated)",
"pattern": "^WF-[0-9]{4,6}$"
},
"vendor_id": {
"type": "string",
"description": "Vendor who manages this worker"
},
"vendor_name": {
"type": "string",
"description": "Vendor name"
},
"first_name": {
"type": "string",
"description": "Worker first name"
},
"last_name": {
"type": "string",
"description": "Worker last name"
},
"email": {
"type": "string",
"format": "email",
"description": "Worker email"
},
"phone": {
"type": "string",
"description": "Worker phone"
},
"address": {
"type": "string",
"description": "Worker address"
},
"city": {
"type": "string",
"description": "City"
},
"state": {
"type": "string",
"description": "State"
},
"zip_code": {
"type": "string",
"description": "ZIP code"
},
"date_of_birth": {
"type": "string",
"format": "date",
"description": "Date of birth"
},
"ssn_last_four": {
"type": "string",
"description": "Last 4 digits of SSN",
"pattern": "^[0-9]{4}$"
},
"employment_type": {
"type": "string",
"enum": [
"W2",
"1099",
"Temporary",
"Contract"
],
"description": "Employment classification"
},
"skills": {
"type": "array",
"description": "Worker skills",
"items": {
"type": "string"
}
},
"roles_qualified": {
"type": "array",
"description": "Roles worker is qualified for",
"items": {
"type": "string"
}
},
"certifications": {
"type": "array",
"description": "Certifications",
"items": {
"type": "object",
"properties": {
"cert_name": {
"type": "string"
},
"cert_number": {
"type": "string"
},
"issued_date": {
"type": "string",
"format": "date"
},
"expiry_date": {
"type": "string",
"format": "date"
},
"issuer": {
"type": "string"
},
"document_url": {
"type": "string"
}
}
}
},
"background_check_status": {
"type": "string",
"enum": [
"Pending",
"Cleared",
"Failed",
"Expired"
],
"description": "Background check status"
},
"background_check_date": {
"type": "string",
"format": "date",
"description": "Background check completion date"
},
"drug_test_status": {
"type": "string",
"enum": [
"Pending",
"Passed",
"Failed",
"Expired"
],
"description": "Drug test status"
},
"drug_test_date": {
"type": "string",
"format": "date",
"description": "Drug test date"
},
"reliability_index": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Reliability score (0-100)"
},
"total_assignments": {
"type": "number",
"default": 0,
"description": "Total assignments completed"
},
"cancelled_assignments": {
"type": "number",
"default": 0,
"description": "Cancelled assignments"
},
"no_shows": {
"type": "number",
"default": 0,
"description": "No-show count"
},
"average_rating": {
"type": "number",
"minimum": 0,
"maximum": 5,
"description": "Average performance rating"
},
"languages": {
"type": "array",
"description": "Languages spoken",
"items": {
"type": "string"
}
},
"availability": {
"type": "object",
"description": "Worker availability",
"properties": {
"monday": {
"type": "boolean"
},
"tuesday": {
"type": "boolean"
},
"wednesday": {
"type": "boolean"
},
"thursday": {
"type": "boolean"
},
"friday": {
"type": "boolean"
},
"saturday": {
"type": "boolean"
},
"sunday": {
"type": "boolean"
},
"shift_preferences": {
"type": "array",
"items": {
"type": "string",
"enum": [
"Morning",
"Day",
"Evening",
"Night",
"Overnight"
]
}
}
}
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Is worker active"
},
"hire_date": {
"type": "string",
"format": "date",
"description": "Date worker was hired"
},
"termination_date": {
"type": "string",
"format": "date",
"description": "Termination date if applicable"
},
"notes": {
"type": "string",
"description": "Internal notes"
}
},
"required": [
"vendor_id",
"first_name",
"last_name",
"workforce_number"
]
}

View File

@@ -1,134 +0,0 @@
{
"name": "WorkforceEvent",
"type": "object",
"properties": {
"event_id": {
"type": "string",
"description": "Unique event identifier (EVT-timestamp-random)"
},
"event_type": {
"type": "string",
"enum": [
"shift.completed",
"shift.created",
"shift.cancelled",
"shift.no_show",
"order.created",
"order.updated",
"order.cancelled",
"order.filled",
"assignment.created",
"assignment.confirmed",
"assignment.completed",
"assignment.cancelled",
"invoice.created",
"invoice.approved",
"invoice.paid",
"invoice.overdue",
"vendor.rate_changed",
"vendor.onboarded",
"vendor.compliance_updated",
"worker.hired",
"worker.availability_changed",
"worker.certification_expired",
"worker.clock_in",
"worker.clock_out",
"budget.threshold_exceeded",
"compliance.flagged",
"system.prediction",
"system.anomaly_detected"
],
"description": "Type of workforce event"
},
"severity": {
"type": "string",
"enum": [
"info",
"warning",
"critical"
],
"default": "info"
},
"actor_type": {
"type": "string",
"enum": [
"worker",
"vendor",
"manager",
"system",
"agent",
"client"
],
"description": "Who or what triggered the event"
},
"actor_id": {
"type": "string",
"description": "ID of the actor"
},
"actor_name": {
"type": "string",
"description": "Name of the actor"
},
"subject_entity_type": {
"type": "string",
"description": "Type of entity the event is about (Order, Assignment, Invoice, etc.)"
},
"subject_entity_id": {
"type": "string",
"description": "ID of the subject entity"
},
"subject_krow_id": {
"type": "string",
"description": "KROW universal ID of the subject"
},
"context_summary": {
"type": "string",
"description": "Human-readable summary of the event"
},
"related_entities": {
"type": "array",
"description": "Related entity references for graph traversal",
"items": {
"type": "object",
"properties": {
"entity_type": {
"type": "string"
},
"entity_id": {
"type": "string"
},
"relationship": {
"type": "string"
}
}
}
},
"metadata": {
"type": "object",
"description": "Event-specific payload data",
"additionalProperties": true
},
"reasoning_triggered": {
"type": "boolean",
"default": false,
"description": "Whether the reasoning engine has processed this event"
},
"memory_id": {
"type": "string",
"description": "ID of the OperationalMemory record created from reasoning about this event"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Searchable tags (e.g., 'overtime', 'shortage', 'compliance')"
}
},
"required": [
"event_id",
"event_type",
"subject_entity_type",
"subject_entity_id"
]
}

View File

@@ -1,125 +0,0 @@
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.23';
/**
* Fill Escalation Monitor
* Runs every 15 minutes. Detects events at risk of not being filled
* and creates escalation alerts in ActivityLog.
*
* Fill probability formula:
* score = (assigned / requested) * 60
* + timeBuffer * 20 (more time = more chance to fill)
* + dayOfWeek * 20 (based on historical fill rate for that day)
*
* If fillProbability < 60% → escalation alert
*/
Deno.serve(async (req) => {
const base44 = createClientFromRequest(req);
// Allow scheduled/service calls without user auth
const now = new Date();
const in24h = new Date(now.getTime() + 24 * 60 * 60 * 1000);
const in4h = new Date(now.getTime() + 4 * 60 * 60 * 1000);
// Fetch all upcoming active events
const allEvents = await base44.asServiceRole.entities.Event.list();
const upcoming = allEvents.filter(e => {
if (!e.date) return false;
const eventDate = new Date(e.date);
return eventDate > now && eventDate <= in24h &&
["Active", "Pending", "Confirmed", "Draft"].includes(e.status);
});
// Historical fill rate by day of week
const completed = allEvents.filter(e => e.status === "Completed" && e.date);
const dayFillRate = Array(7).fill(null).map((_, dow) => {
const dayEvents = completed.filter(e => new Date(e.date).getDay() === dow);
if (dayEvents.length === 0) return 0.75; // default 75%
const filled = dayEvents.filter(e => (e.assigned_staff?.length || 0) >= (e.requested || 1));
return filled.length / dayEvents.length;
});
const escalations = [];
const alerts = [];
for (const event of upcoming) {
const assigned = event.assigned_staff?.length || 0;
const requested = event.requested || 0;
if (requested === 0) continue;
const fillRatio = assigned / requested;
const eventDate = new Date(event.date);
const hoursUntil = (eventDate - now) / (1000 * 60 * 60);
const dow = eventDate.getDay();
// Time buffer score: 0 = within 2h (critical), 1 = 24h out (comfortable)
const timeBuffer = Math.min(hoursUntil / 24, 1);
// Fill probability 0-100
const fillProbability = Math.round(
fillRatio * 60 +
timeBuffer * 20 +
dayFillRate[dow] * 20
);
const isCritical = hoursUntil <= 4 && fillRatio < 1;
const isAtRisk = fillProbability < 60;
if (isCritical || isAtRisk) {
escalations.push({
event_id: event.id,
event_name: event.event_name,
hours_until: Math.round(hoursUntil),
fill_ratio: fillRatio,
fill_probability: fillProbability,
assigned,
requested,
is_critical: isCritical,
});
// Create ActivityLog alert (avoid duplicates — check if one exists in last 2h)
const existingAlerts = await base44.asServiceRole.entities.ActivityLog.filter({
related_entity_id: event.id,
activity_type: "event_updated",
});
const recentAlert = existingAlerts.find(a => {
const created = new Date(a.created_date);
return (now - created) < 2 * 60 * 60 * 1000; // within 2h
});
if (!recentAlert) {
const severity = isCritical ? "🚨 CRITICAL" : "⚠️ AT RISK";
const gapCount = requested - assigned;
const description = isCritical
? `${event.event_name} starts in ${Math.round(hoursUntil)}h — ${gapCount} position${gapCount > 1 ? "s" : ""} still open. Fill immediately.`
: `${event.event_name} has a ${fillProbability}% fill probability. ${gapCount} position${gapCount > 1 ? "s" : ""} unfilled with ${Math.round(hoursUntil)}h remaining.`;
alerts.push(
base44.asServiceRole.entities.ActivityLog.create({
title: `${severity}: ${event.event_name} needs staffing`,
description,
activity_type: "event_updated",
related_entity_type: "event",
related_entity_id: event.id,
action_link: `EventDetail?id=${event.id}`,
action_label: "Assign Staff Now",
user_id: event.created_by || "system",
is_read: false,
icon_type: "alert",
icon_color: isCritical ? "red" : "yellow",
})
);
}
}
}
await Promise.all(alerts);
return Response.json({
checked: upcoming.length,
escalations: escalations.length,
details: escalations,
timestamp: now.toISOString(),
});
});

View File

@@ -1,504 +0,0 @@
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 });
}
});

View File

@@ -1,235 +0,0 @@
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 {
event_id,
event_type,
subject_entity_type,
subject_entity_id,
context_summary,
metadata = {},
related_entities = []
} = body;
const entities = base44.asServiceRole.entities;
// 1. Fetch context from the Workforce Graph
let graphContext = {};
try {
const graphRes = await base44.asServiceRole.functions.invoke('queryWorkforceGraph', {
query_type: 'subgraph',
entity_type: subject_entity_type,
entity_id: subject_entity_id
});
graphContext = graphRes.data?.graph || {};
} catch (e) {
console.log('Graph query failed (non-blocking):', e.message);
}
// 2. Fetch similar past memories (Operational Memory)
const similarMemories = await entities.OperationalMemory.filter(
{ trigger_event_type: event_type },
'-created_date',
5
);
const memoryContext = similarMemories.map(m => ({
decision: m.decision?.recommendation || 'N/A',
outcome: m.outcome || 'pending',
outcome_score: m.outcome_score,
reasoning: m.reasoning?.substring(0, 200) || ''
}));
// 3. Determine department based on event type
const departmentMap = {
'shift.completed': 'operations',
'shift.cancelled': 'operations',
'shift.no_show': 'operations',
'order.created': 'operations',
'order.cancelled': 'operations',
'order.filled': 'operations',
'assignment.cancelled': 'operations',
'invoice.overdue': 'finance',
'vendor.rate_changed': 'procurement',
'worker.availability_changed': 'operations',
'worker.certification_expired': 'compliance',
'budget.threshold_exceeded': 'finance',
'compliance.flagged': 'compliance',
'system.anomaly_detected': 'executive'
};
const department = departmentMap[event_type] || 'operations';
// 4. Build the reasoning prompt with full context — CAUSAL CHAIN reasoning
const prompt = `You are the KROW Intelligence Engine — an AI workforce reasoning system for contingent labor management.
You do not answer. You REASON.
When an event occurs, you trace the CAUSAL CHAIN backwards — from the observed event through each contributing factor (connected by ↓) to the root cause(s). You do not give generic analysis. You follow the chain.
AN EVENT HAS OCCURRED:
Event Type: ${event_type}
Summary: ${context_summary || 'No summary provided'}
Subject: ${subject_entity_type} (${subject_entity_id})
CURRENT GRAPH STATE (connected entities, relationships, metrics):
${JSON.stringify(graphContext, null, 2).substring(0, 3000)}
SIMILAR PAST DECISIONS (Operational Memory — what happened last time):
${memoryContext.length > 0 ? JSON.stringify(memoryContext, null, 2) : 'No similar past decisions found.'}
EVENT METADATA:
${JSON.stringify(metadata, null, 2).substring(0, 1000)}
REASONING INSTRUCTIONS:
1. OBSERVATION: State what happened as a fact.
2. CAUSAL CHAIN: Trace WHY this event occurred. Walk backwards from the event through contributing factors. Each link in the chain is a factor that CAUSED the next. The chain flows:
Factor A → caused → Factor B → caused → Factor C → caused → [Root Cause]
Use the graph state, metadata, and past memories as evidence. Reference specific entities, workers, vendors, shifts, or metrics. Do not be generic.
Categories: vendor, operations, workforce, training, demand, external, compliance, finance, equipment, transportation, system.
3. ROOT CAUSE(S): The deepest factor(s) — the thing that, if fixed, prevents recurrence.
4. PREDICTION: What will happen next if no action is taken.
5. RECOMMENDATION: Target the root cause, not just the symptom.
Respond as JSON.`;
// 5. Call the LLM for reasoning
let aiDecision = {};
try {
const llmResponse = await base44.asServiceRole.integrations.Core.InvokeLLM({
prompt,
response_json_schema: {
type: 'object',
properties: {
observation: { type: 'string', description: 'What happened, stated as a fact' },
causal_chain: {
type: 'array',
description: 'Causal chain traced backwards from the event to root cause(s)',
items: {
type: 'object',
properties: {
step: { type: 'number' },
factor: { type: 'string' },
evidence: { type: 'string' },
category: { type: 'string', enum: ['vendor', 'operations', 'workforce', 'training', 'demand', 'external', 'compliance', 'finance', 'equipment', 'transportation', 'system'] },
leads_to: { type: 'string' }
}
}
},
root_causes: {
type: 'array',
items: { type: 'string' }
},
prediction: { type: 'string' },
recommendation: { type: 'string' },
confidence_score: { type: 'number' },
risk_level: { type: 'string' },
proposed_actions: {
type: 'array',
items: {
type: 'object',
properties: {
action_type: { type: 'string' },
description: { type: 'string' },
auto_execute: { type: 'boolean' }
}
}
},
reasoning: { type: 'string', description: 'Full reasoning chain in natural language' }
}
}
});
aiDecision = typeof llmResponse === 'string' ? JSON.parse(llmResponse) : llmResponse;
} catch (llmErr) {
console.log('LLM reasoning failed:', llmErr.message);
aiDecision = {
prediction: 'Unable to generate prediction',
recommendation: 'Manual review recommended',
confidence_score: 0,
risk_level: 'medium',
proposed_actions: [],
reasoning: 'LLM reasoning failed: ' + llmErr.message
};
}
// 6. Create Operational Memory — store the causal chain
const memory_id = `MEM-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
const memory = await entities.OperationalMemory.create({
memory_id,
trigger_event_id: event_id,
trigger_event_type: event_type,
context_snapshot: { graph: graphContext, metadata },
decision: aiDecision,
reasoning: aiDecision.reasoning || '',
causal_chain: aiDecision.causal_chain || [],
root_causes: aiDecision.root_causes || [],
similar_memories_consulted: similarMemories.map(m => m.memory_id),
status: 'pending',
agent_name: `${department}_agent`,
department,
tags: [event_type, department, aiDecision.risk_level || 'medium']
});
// 7. Mark event as reasoning-triggered
await entities.WorkforceEvent.update(event_id, {
reasoning_triggered: true,
memory_id: memory.id
});
// 8. Log agent activity
await entities.AgentActivity.create({
agent_name: `${department}_agent`,
department,
activity_type: 'event_processed',
trigger_event_id: event_id,
memory_id: memory.id,
summary: `Processed ${event_type}: ${aiDecision.recommendation || 'No recommendation'}`,
details: { confidence: aiDecision.confidence_score, risk: aiDecision.risk_level },
impact_level: aiDecision.risk_level === 'high' ? 'high' : (aiDecision.risk_level === 'medium' ? 'medium' : 'low'),
entities_affected: [{ entity_type: subject_entity_type, entity_id: subject_entity_id }]
});
// 9. If high risk, queue for human approval; if low risk + auto_execute, execute
const hasAutoActions = (aiDecision.proposed_actions || []).some(a => a.auto_execute);
if (aiDecision.risk_level === 'high' || (aiDecision.confidence_score || 0) < 60) {
await entities.DecisionQueue.create({
memory_id: memory.id,
trigger_event_id: event_id,
title: `${department} decision: ${event_type}`,
summary: aiDecision.recommendation || 'AI recommends manual review',
department,
agent_name: `${department}_agent`,
proposed_actions: aiDecision.proposed_actions || [],
confidence_score: aiDecision.confidence_score || 0,
risk_level: aiDecision.risk_level || 'medium',
status: 'pending_approval',
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
context_snapshot: { graph: graphContext, event_summary: context_summary }
});
}
return Response.json({
success: true,
memory_id: memory.id,
decision: aiDecision,
graph_nodes: graphContext.nodes?.length || 0,
similar_memories: similarMemories.length
});
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
});

View File

@@ -1,79 +0,0 @@
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 {
event_type,
severity = 'info',
actor_type = 'system',
actor_id,
actor_name,
subject_entity_type,
subject_entity_id,
subject_krow_id,
context_summary,
related_entities = [],
metadata = {},
tags = []
} = body;
if (!event_type || !subject_entity_type || !subject_entity_id) {
return Response.json({ error: 'event_type, subject_entity_type, and subject_entity_id are required' }, { status: 400 });
}
const event_id = `EVT-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
const event = await base44.asServiceRole.entities.WorkforceEvent.create({
event_id,
event_type,
severity,
actor_type,
actor_id: actor_id || user.id,
actor_name: actor_name || user.full_name || 'System',
subject_entity_type,
subject_entity_id,
subject_krow_id,
context_summary: context_summary || `${event_type} on ${subject_entity_type}`,
related_entities,
metadata,
reasoning_triggered: false,
tags
});
// Determine if this event should trigger reasoning
const reasoningTriggers = [
'shift.completed', 'shift.cancelled', 'shift.no_show',
'order.created', 'order.cancelled', 'order.filled',
'assignment.cancelled', 'invoice.overdue',
'vendor.rate_changed', 'worker.availability_changed',
'worker.certification_expired', 'budget.threshold_exceeded',
'compliance.flagged', 'system.anomaly_detected'
];
if (reasoningTriggers.includes(event_type)) {
try {
await base44.asServiceRole.functions.invoke('reasonAboutEvent', {
event_id: event.id,
event_type,
subject_entity_type,
subject_entity_id,
context_summary,
metadata,
related_entities
});
} catch (reasonErr) {
// Reasoning failure shouldn't fail the event recording
console.log('Reasoning engine error (non-blocking):', reasonErr.message);
}
}
return Response.json({ success: true, event_id: event.id, event });
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
});

View File

@@ -1,104 +0,0 @@
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 });
if (user.role !== 'admin') return Response.json({ error: 'Forbidden — admin only' }, { status: 403 });
const entities = base44.asServiceRole.entities;
// Fetch memories with pending outcomes (executed but not yet learned)
const pendingMemories = await entities.OperationalMemory.filter(
{ status: 'outcome_pending' },
'-created_date',
50
);
let learned = 0;
let stillPending = 0;
for (const memory of pendingMemories) {
try {
// Re-query the graph to see what happened since the decision was made
const graphRes = await base44.asServiceRole.functions.invoke('queryWorkforceGraph', {
query_type: 'subgraph',
entity_type: memory.context_snapshot?.metadata?.subject_entity_type || 'Order',
entity_id: memory.context_snapshot?.metadata?.subject_entity_id || memory.trigger_event_id
});
const currentGraph = graphRes.data?.graph || {};
// Ask the LLM to evaluate the outcome
const prompt = `You are evaluating the outcome of a past KROW AI decision.
ORIGINAL EVENT: ${memory.trigger_event_type}
ORIGINAL DECISION: ${memory.decision?.recommendation || 'N/A'}
ORIGINAL PREDICTION: ${memory.decision?.prediction || 'N/A'}
CURRENT STATE (after decision):
${JSON.stringify(currentGraph, null, 2).substring(0, 2000)}
ORIGINAL CONTEXT (at decision time):
${JSON.stringify(memory.context_snapshot?.graph || {}, null, 2).substring(0, 2000)}
Evaluate: Did the decision help? Did the prediction come true? What was the outcome?
Respond as JSON:
{
"outcome": "Description of what actually happened",
"outcome_score": -100 to 100 (negative = made things worse, 0 = no effect, positive = helped),
"outcome_metrics": {"key_metric": value}
}`;
const evalResponse = await base44.asServiceRole.integrations.Core.InvokeLLM({
prompt,
response_json_schema: {
type: 'object',
properties: {
outcome: { type: 'string' },
outcome_score: { type: 'number' },
outcome_metrics: { type: 'object', additionalProperties: true }
}
}
});
const evaluation = typeof evalResponse === 'string' ? JSON.parse(evalResponse) : evalResponse;
await entities.OperationalMemory.update(memory.id, {
outcome: evaluation.outcome,
outcome_score: evaluation.outcome_score,
outcome_metrics: evaluation.outcome_metrics,
status: 'learned',
learned_at: new Date().toISOString()
});
// Log learning activity
await entities.AgentActivity.create({
agent_name: memory.agent_name || 'system',
department: memory.department || 'operations',
activity_type: 'learning_completed',
memory_id: memory.id,
summary: `Learned from ${memory.trigger_event_type}: outcome score ${evaluation.outcome_score}`,
details: evaluation,
impact_level: evaluation.outcome_score > 50 ? 'high' : (evaluation.outcome_score > 0 ? 'medium' : 'low'),
entities_affected: []
});
learned++;
} catch (e) {
console.log(`Failed to evaluate memory ${memory.id}:`, e.message);
stillPending++;
}
}
return Response.json({
success: true,
reviewed: pendingMemories.length,
learned,
still_pending: stillPending
});
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
});

View File

@@ -1,19 +0,0 @@
#!/bin/bash
set -e
# Ensure the SDK is built before deployment
echo "Building SDK..."
cd sdk
npm install --legacy-peer-deps
npm run build
cd ..
# Commit and push changes
echo "Committing and pushing changes..."
git add .
git commit -m "deploy"
git push origin main
# Build and deploy the e2b template
echo "Building and deploying e2b template..."
cd e2b_template && e2b template build -c "npm run dev"

View File

@@ -2,9 +2,10 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="https://base44.com/logo_v2.svg" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Base44 APP</title> <meta name="description" content="KROW Workforce Control Tower" />
<title>KROW Workforce Control Tower</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

2723
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,16 @@
{ {
"name": "base44-app", "name": "krow-workforce-control-tower",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"lint": "eslint .", "preview": "vite preview",
"preview": "vite preview" "lint": "eslint ."
}, },
"dependencies": { "dependencies": {
"@base44/sdk": "^0.8.42", "@astryxdesign/core": "^0.4.3",
"@base44/vite-plugin": "^1.0.30",
"@hello-pangea/dnd": "^17.0.0", "@hello-pangea/dnd": "^17.0.0",
"@hookform/resolvers": "^4.1.2", "@hookform/resolvers": "^4.1.2",
"@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-accordion": "^1.2.3",
@@ -40,6 +39,7 @@
"@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-toggle": "^1.1.2",
"@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-tooltip": "^1.1.8",
"@stylexjs/stylex": "^0.19.0",
"@tanstack/react-query": "^5.84.1", "@tanstack/react-query": "^5.84.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@@ -52,9 +52,9 @@
"lucide-react": "^0.475.0", "lucide-react": "^0.475.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"next-themes": "^0.4.4", "next-themes": "^0.4.4",
"react": "^18.2.0", "react": "^19.2.8",
"react-day-picker": "^8.10.1", "react-day-picker": "^10.0.1",
"react-dom": "^18.2.0", "react-dom": "^19.2.8",
"react-hook-form": "^7.54.2", "react-hook-form": "^7.54.2",
"react-leaflet": "^4.2.1", "react-leaflet": "^4.2.1",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
@@ -72,7 +72,6 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.19.0", "@eslint/js": "^9.19.0",
"@flydotio/dockerfile": "^0.7.8",
"@types/node": "^22.13.5", "@types/node": "^22.13.5",
"@types/react": "^18.2.66", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",

Binary file not shown.

Binary file not shown.

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
public/favicon.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -2,14 +2,9 @@ import './App.css'
import { Toaster } from "@/components/ui/toaster" import { Toaster } from "@/components/ui/toaster"
import { QueryClientProvider } from '@tanstack/react-query' import { QueryClientProvider } from '@tanstack/react-query'
import { queryClientInstance } from '@/lib/query-client' import { queryClientInstance } from '@/lib/query-client'
import VisualEditAgent from '@/lib/VisualEditAgent'
import NavigationTracker from '@/lib/NavigationTracker'
import { pagesConfig } from './pages.config' import { pagesConfig } from './pages.config'
import { BrowserRouter as Router, Route, Routes, useLocation } from 'react-router-dom'; import { BrowserRouter as Router, Route, Routes, useLocation } from 'react-router-dom';
import { setupIframeMessaging } from './lib/iframe-messaging';
import PageNotFound from './lib/PageNotFound'; import PageNotFound from './lib/PageNotFound';
import { AuthProvider, useAuth } from '@/lib/AuthContext';
import UserNotRegisteredError from '@/components/UserNotRegisteredError';
import FeatureReport from './pages/FeatureReport'; import FeatureReport from './pages/FeatureReport';
import LandingPage from './pages/LandingPage'; import LandingPage from './pages/LandingPage';
import KrowX from './pages/KrowX'; import KrowX from './pages/KrowX';
@@ -42,38 +37,20 @@ const { Pages, Layout, mainPage } = pagesConfig;
const mainPageKey = mainPage ?? Object.keys(Pages)[0]; const mainPageKey = mainPage ?? Object.keys(Pages)[0];
const MainPage = mainPageKey ? Pages[mainPageKey] : <></>; const MainPage = mainPageKey ? Pages[mainPageKey] : <></>;
setupIframeMessaging();
const LayoutWrapper = ({ children, currentPageName }) => Layout ? const LayoutWrapper = ({ children, currentPageName }) => Layout ?
<Layout currentPageName={currentPageName}>{children}</Layout> <Layout currentPageName={currentPageName}>{children}</Layout>
: <>{children}</>; : <>{children}</>;
const AuthenticatedApp = () => { /**
const { isLoadingAuth, isLoadingPublicSettings, authError, isAuthenticated, navigateToLogin } = useAuth(); * The application routes.
*
* Nothing gates this render: the app is a standalone frontend with local data,
* so the first paint is always the KROW UI — no session check, no loading gate,
* no redirect.
*/
const AppRoutes = () => {
const location = useLocation(); const location = useLocation();
// Show loading spinner while checking app public settings or auth
if (isLoadingPublicSettings || isLoadingAuth) {
return (
<div className="fixed inset-0 flex items-center justify-center">
<div className="w-8 h-8 border-4 border-slate-200 border-t-slate-800 rounded-full animate-spin"></div>
</div>
);
}
// Handle authentication errors
if (authError) {
if (authError.type === 'user_not_registered') {
return <UserNotRegisteredError />;
} else if (authError.type === 'auth_required') {
// Redirect to login automatically
navigateToLogin();
return null;
}
}
// Render the main app
if (location.pathname === '/LandingPage') { if (location.pathname === '/LandingPage') {
return ( return (
<Routes> <Routes>
@@ -126,16 +103,12 @@ const AuthenticatedApp = () => {
function App() { function App() {
return ( return (
<AuthProvider> <QueryClientProvider client={queryClientInstance}>
<QueryClientProvider client={queryClientInstance}> <Router>
<Router> <AppRoutes />
<NavigationTracker /> </Router>
<AuthenticatedApp /> <Toaster />
</Router> </QueryClientProvider>
<Toaster />
<VisualEditAgent />
</QueryClientProvider>
</AuthProvider>
) )
} }

View File

@@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation, useNavigate } from "react-router-dom";
import { createPageUrl } from "@/utils"; import { createPageUrl } from "@/utils";
import { base44 } from "@/api/base44Client"; import { krow } from "@/api/client";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { import {
Users, LayoutDashboard, UserPlus, Calendar, Briefcase, FileText, Users, LayoutDashboard, UserPlus, Calendar, Briefcase, FileText,
@@ -469,17 +469,18 @@ function LayoutChrome({ children }) {
const { data: user } = useQuery({ const { data: user } = useQuery({
queryKey: ['current-user-layout'], queryKey: ['current-user-layout'],
queryFn: () => base44.auth.me(), queryFn: () => krow.auth.me(),
}); });
const sampleAvatar = "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&h=400&fit=crop"; // Local default: use user's profile picture if available (and not company logo), otherwise null to trigger AvatarFallback.
const userAvatar = user?.profile_picture || sampleAvatar; const rawAvatar = user?.profile_picture;
const userAvatar = (rawAvatar && !rawAvatar.includes('krow-logo')) ? rawAvatar : null;
const { data: unreadCount = 0 } = useQuery({ const { data: unreadCount = 0 } = useQuery({
queryKey: ['unread-notifications', user?.id], queryKey: ['unread-notifications', user?.id],
queryFn: async () => { queryFn: async () => {
if (!user?.id) return 0; if (!user?.id) return 0;
const notifications = await base44.entities.ActivityLog.filter({ const notifications = await krow.entities.ActivityLog.filter({
user_id: user?.id, user_id: user?.id,
is_read: false is_read: false
}); });
@@ -496,16 +497,28 @@ function LayoutChrome({ children }) {
const userName = user?.full_name || user?.email || "User"; const userName = user?.full_name || user?.email || "User";
const userInitial = userName.charAt(0).toUpperCase(); const userInitial = userName.charAt(0).toUpperCase();
const handleLogout = () => { // There is no session to end. This resets the local profile to its defaults
base44.auth.logout(); // and reloads into the dashboard.
const handleLogout = async () => {
await krow.auth.logout();
window.location.assign("/");
}; };
const handleRefresh = () => { const handleRefresh = () => {
window.location.reload(); window.location.reload();
}; };
// Route change starts at the top of the new page. The browser does this for
// free while the document scrolls; the page has its own scrollport now, so the
// shell resets it. Keyed on pathname only — changing a filter in the query
// string is not a new page and must not yank the user upward.
const mainRef = React.useRef(null);
React.useEffect(() => {
mainRef.current?.scrollTo({ top: 0, left: 0 });
}, [location.pathname]);
return ( return (
<div className="min-h-screen flex flex-col w-full bg-slate-50"> <div className="krow-shell flex flex-col w-full bg-slate-50">
<style>{` <style>{`
:root { :root {
--primary: 0 57 166; --primary: 0 57 166;
@@ -522,39 +535,38 @@ function LayoutChrome({ children }) {
.rdp * { border-color: transparent !important; } .rdp * { border-color: transparent !important; }
.rdp-day { font-size: 0.875rem !important; min-width: 36px !important; height: 36px !important; border-radius: 50% !important; transition: all 0.2s ease !important; font-weight: 500 !important; position: relative !important; } .rdp-day { font-size: 0.875rem !important; min-width: 36px !important; height: 36px !important; border-radius: 50% !important; transition: all 0.2s ease !important; font-weight: 500 !important; position: relative !important; }
.rdp-day button { width: 100% !important; height: 100% !important; border-radius: 50% !important; background-color: transparent !important; } .rdp-day_button { width: 100% !important; height: 100% !important; border-radius: 50% !important; background-color: transparent !important; }
.rdp-day_range_start, .rdp-day_range_start > button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; } .rdp-range_start, .rdp-range_start .rdp-day_button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; }
.rdp-day_range_end, .rdp-day_range_end > button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; } .rdp-range_end, .rdp-range_end .rdp-day_button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; }
.rdp-day_selected:not(.rdp-day_range_start):not(.rdp-day_range_end), .rdp-day_selected:not(.rdp-day_range_start):not(.rdp-day_range_end) > button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; } .rdp-selected:not(.rdp-range_start):not(.rdp-range_end), .rdp-selected:not(.rdp-range_start):not(.rdp-range_end) .rdp-day_button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; }
.rdp-day_selected, .rdp-day_selected > button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; } .rdp-selected, .rdp-selected .rdp-day_button { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; color: white !important; font-weight: 700 !important; border-radius: 50% !important; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3) !important; }
.rdp-day_range_middle, .rdp-day_range_middle > button { background-color: #dbeafe !important; background: #dbeafe !important; color: #2563eb !important; font-weight: 600 !important; border-radius: 0 !important; box-shadow: none !important; } .rdp-range_middle, .rdp-range_middle .rdp-day_button { background-color: #dbeafe !important; background: #dbeafe !important; color: #2563eb !important; font-weight: 600 !important; border-radius: 0 !important; box-shadow: none !important; }
.rdp-day_range_start.rdp-day_range_end, .rdp-day_range_start.rdp-day_range_end > button { border-radius: 50% !important; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; } .rdp-range_start.rdp-range_end, .rdp-range_start.rdp-range_end .rdp-day_button { border-radius: 50% !important; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%) !important; }
.rdp-day:hover:not(.rdp-day_selected):not(.rdp-day_disabled):not(.rdp-day_range_start):not(.rdp-day_range_end):not(.rdp-day_range_middle) > button { background-color: #eff6ff !important; background: #eff6ff !important; color: #2563eb !important; border-radius: 50% !important; } .rdp-day:hover:not(.rdp-selected):not(.rdp-disabled):not(.rdp-range_start):not(.rdp-range_end):not(.rdp-range_middle) .rdp-day_button { background-color: #eff6ff !important; background: #eff6ff !important; color: #2563eb !important; border-radius: 50% !important; }
.rdp-day_today:not(.rdp-day_selected):not(.rdp-day_range_start):not(.rdp-day_range_end)::after { content: '' !important; position: absolute !important; bottom: 4px !important; left: 50% !important; transform: translateX(-50%) !important; width: 4px !important; height: 4px !important; background-color: #ec4899 !important; border-radius: 50% !important; z-index: 10 !important; } .rdp-today:not(.rdp-selected):not(.rdp-range_start):not(.rdp-range_end)::after { content: '' !important; position: absolute !important; bottom: 4px !important; left: 50% !important; transform: translateX(-50%) !important; width: 4px !important; height: 4px !important; background-color: #ec4899 !important; border-radius: 50% !important; z-index: 10 !important; }
.rdp-day_today.rdp-day_selected, .rdp-day_today.rdp-day_range_start, .rdp-day_today.rdp-day_range_end { color: white !important; } .rdp-today.rdp-selected, .rdp-today.rdp-range_start, .rdp-today.rdp-range_end { color: white !important; }
.rdp-day_today.rdp-day_selected > button, .rdp-day_today.rdp-day_range_start > button, .rdp-day_today.rdp-day_range_end > button { color: white !important; } .rdp-today.rdp-selected .rdp-day_button, .rdp-today.rdp-range_start .rdp-day_button, .rdp-today.rdp-range_end .rdp-day_button { color: white !important; }
.rdp-day_outside, .rdp-day_outside > button { color: #cbd5e1 !important; opacity: 0.5 !important; } .rdp-outside, .rdp-outside .rdp-day_button { color: #cbd5e1 !important; opacity: 0.5 !important; }
.rdp-day_disabled, .rdp-day_disabled > button { opacity: 0.3 !important; cursor: not-allowed !important; } .rdp-disabled, .rdp-disabled .rdp-day_button { opacity: 0.3 !important; cursor: not-allowed !important; }
.rdp-day_selected, .rdp-day_range_start, .rdp-day_range_end, .rdp-day_range_middle { opacity: 1 !important; visibility: visible !important; z-index: 5 !important; } .rdp-selected, .rdp-range_start, .rdp-range_end, .rdp-range_middle { opacity: 1 !important; visibility: visible !important; z-index: 5 !important; }
.rdp-day.has-events:not(.rdp-day_selected):not(.rdp-day_range_start):not(.rdp-day_range_end)::before { content: '' !important; position: absolute !important; top: 4px !important; right: 4px !important; width: 4px !important; height: 4px !important; background-color: #2563eb !important; border-radius: 50% !important; } .rdp-day.has-events:not(.rdp-selected):not(.rdp-range_start):not(.rdp-range_end)::before { content: '' !important; position: absolute !important; top: 4px !important; right: 4px !important; width: 4px !important; height: 4px !important; background-color: #2563eb !important; border-radius: 50% !important; }
.rdp-day_selected.has-events::before, .rdp-day_range_start.has-events::before, .rdp-day_range_end.has-events::before { background-color: white !important; } .rdp-selected.has-events::before, .rdp-range_start.has-events::before, .rdp-range_end.has-events::before { background-color: white !important; }
.rdp-day_range_middle.has-events::before { background-color: #2563eb !important; } .rdp-range_middle.has-events::before { background-color: #2563eb !important; }
.rdp-head_cell { color: #64748b !important; font-weight: 600 !important; font-size: 0.75rem !important; text-transform: uppercase !important; padding: 8px 0 !important; } .rdp-weekday { color: #64748b !important; font-weight: 600 !important; font-size: 0.75rem !important; text-transform: uppercase !important; padding: 8px 0 !important; }
.rdp-caption_label { font-size: 1rem !important; font-weight: 700 !important; color: #0f172a !important; } .rdp-caption_label { font-size: 1rem !important; font-weight: 700 !important; color: #0f172a !important; }
.rdp-nav_button { width: 32px !important; height: 32px !important; border-radius: 6px !important; transition: all 0.2s ease !important; } .rdp-button_previous, .rdp-button_next { width: 32px !important; height: 32px !important; border-radius: 6px !important; transition: all 0.2s ease !important; }
.rdp-nav_button:hover { background-color: #eff6ff !important; color: #2563eb !important; } .rdp-button_previous:hover, .rdp-button_next:hover { background-color: #eff6ff !important; color: #2563eb !important; }
.rdp-months { gap: 2rem !important; } .rdp-months { gap: 2rem !important; }
.rdp-month { padding: 0.75rem !important; } .rdp-month { padding: 0.75rem !important; }
.rdp-table { border-spacing: 0 !important; margin-top: 1rem !important; } .rdp-month_grid { border-spacing: 0 !important; margin-top: 1rem !important; }
.rdp-cell { padding: 2px !important; } .rdp-day { padding: 2px !important; }
.rdp-day[style*="background"] { background: transparent !important; } .rdp-day[style*="background"] { background: transparent !important; }
`}</style> `}</style>
<header className="bg-white border-b border-slate-200 shadow-sm sticky top-0 z-30"> <header className="bg-white border-b border-slate-200 shadow-sm sticky top-0 z-30">
<div className="px-2 sm:px-4 md:px-6 py-3 flex items-center justify-between gap-2 md:gap-4"> <div className="px-2 sm:px-4 md:px-6 py-3 flex items-center justify-between gap-2 md:gap-4">
<div className="flex items-center gap-1 sm:gap-3 md:gap-4 flex-1 min-w-0"> {/* Left section: Mobile menu toggle + Logo/Brand */}
<div className="flex items-center gap-1 sm:gap-3 flex-1 basis-0 min-w-0">
<Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}> <Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>
<SheetTrigger asChild> <SheetTrigger asChild>
<Button variant="ghost" size="icon" className="lg:hidden hover:bg-slate-100"> <Button variant="ghost" size="icon" className="lg:hidden hover:bg-slate-100">
@@ -563,8 +575,9 @@ function LayoutChrome({ children }) {
</SheetTrigger> </SheetTrigger>
<SheetContent side="left" className="w-64 p-0 bg-white border-slate-200"> <SheetContent side="left" className="w-64 p-0 bg-white border-slate-200">
<div className="border-b border-slate-200 p-6"> <div className="border-b border-slate-200 p-6">
<Link to={getDashboardUrl(userRole)} className="flex items-center gap-3 mb-4" onClick={() => setMobileMenuOpen(false)}> <Link to={getDashboardUrl(userRole)} className="flex items-center gap-2.5 mb-4" onClick={() => setMobileMenuOpen(false)}>
<img src={krowLogo} alt="KROW" className="h-6 w-auto object-contain" /> <img src={krowLogo} alt="KROW" className="h-6 w-auto flex-shrink-0 object-contain" />
<span className="font-bold text-[#1C323E] text-base">KROW</span>
</Link> </Link>
<div className="flex items-center gap-3 bg-slate-50 p-3 rounded-lg"> <div className="flex items-center gap-3 bg-slate-50 p-3 rounded-lg">
<Avatar className="w-10 h-10"> <Avatar className="w-10 h-10">
@@ -588,7 +601,7 @@ function LayoutChrome({ children }) {
</SheetContent> </SheetContent>
</Sheet> </Sheet>
<Link to={getDashboardUrl(userRole)} className="flex items-center gap-2 hover:opacity-80 transition-opacity min-w-0"> <Link to={getDashboardUrl(userRole)} className="flex items-center gap-2.5 hover:opacity-85 transition-opacity min-w-0">
<img src={krowLogo} alt="KROW" className="h-6 sm:h-7 w-auto flex-shrink-0 object-contain" /> <img src={krowLogo} alt="KROW" className="h-6 sm:h-7 w-auto flex-shrink-0 object-contain" />
<div className="hidden sm:block min-w-0"> <div className="hidden sm:block min-w-0">
<h1 className="text-sm lg:text-base font-bold text-[#1C323E] truncate"> <h1 className="text-sm lg:text-base font-bold text-[#1C323E] truncate">
@@ -597,20 +610,18 @@ function LayoutChrome({ children }) {
</h1> </h1>
</div> </div>
</Link> </Link>
</div>
<div className="hidden md:flex flex-1 max-w-xl"> {/* Center section: Search bar centered */}
<div className="relative w-full"> <div className="hidden md:flex flex-1 max-w-xl mx-auto justify-center px-2">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-slate-400" /> <div className="relative w-full">
<input type="text" placeholder="Find employees, menu items, settings, and more..." className="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-[#0039A6] focus:border-transparent" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-slate-400" />
</div> <input type="text" placeholder="Find employees, menu items, settings, and more..." className="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-[#0039A6] focus:border-transparent bg-slate-50/50 hover:bg-white focus:bg-white transition-colors" />
</div> </div>
</div> </div>
<div className="flex items-center gap-0.5 sm:gap-1 md:gap-2 flex-shrink-0"> {/* Right section: Notifications + Options + User Avatar */}
{/* Below xl these collapse into the "More options" menu so the header <div className="flex items-center gap-0.5 sm:gap-1 md:gap-2 flex-1 basis-0 min-w-0 justify-end flex-shrink-0">
never overflows a narrow viewport. */}
<button onClick={() => setShowNotifications(true)} className="relative p-2 hover:bg-[#C8DBDC]/30 rounded-lg transition-colors" title="Notifications" aria-label={`Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ''}`}> <button onClick={() => setShowNotifications(true)} className="relative p-2 hover:bg-[#C8DBDC]/30 rounded-lg transition-colors" title="Notifications" aria-label={`Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ''}`}>
<Bell className="w-5 h-5 text-[#3C4F5C]" /> <Bell className="w-5 h-5 text-[#3C4F5C]" />
{unreadCount > 0 && ( {unreadCount > 0 && (
@@ -620,8 +631,6 @@ function LayoutChrome({ children }) {
)} )}
</button> </button>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="hover:bg-slate-100" title="More options" aria-label="More options"> <Button variant="ghost" size="icon" className="hover:bg-slate-100" title="More options" aria-label="More options">
@@ -655,9 +664,6 @@ function LayoutChrome({ children }) {
<Activity className="w-4 h-4 mr-2" />Activity Log <Activity className="w-4 h-4 mr-2" />Activity Log
</DropdownMenuItem> </DropdownMenuItem>
{/* Secondary categories lifted out of the sidebar. Already
de-duplicated against the entries above, so Settings and
Messages are not offered twice. */}
{secondaryNavItems.length > 0 && <DropdownMenuSeparator />} {secondaryNavItems.length > 0 && <DropdownMenuSeparator />}
{secondaryNavItems.map((item) => ( {secondaryNavItems.map((item) => (
<DropdownMenuItem <DropdownMenuItem
@@ -707,24 +713,46 @@ function LayoutChrome({ children }) {
</div> </div>
</header> </header>
<div className="flex flex-1 overflow-hidden min-w-0"> {/* The body row: exactly viewport-minus-header tall, and the only place
<aside className="hidden lg:flex lg:flex-col w-64 flex-shrink-0 bg-white border-r border-slate-200 shadow-sm overflow-y-auto"> scrolling happens. Its three tracks — rail, page, Owliver — each own
<div className="p-3"> their overflow, so none of them can scroll the others. */}
<div className="krow-shell-body flex flex-1 min-h-0 overflow-hidden min-w-0">
{/* The rail itself never moves: it is a track in a row that is exactly
viewport-minus-header tall. Only the navigation inside it scrolls, and
only when the nav is taller than that. */}
<aside className="hidden lg:flex lg:flex-col w-64 flex-shrink-0 min-h-0 bg-white border-r border-slate-200 shadow-sm">
<div className="flex-1 min-h-0 overflow-y-auto p-3">
<NavigationMenu location={location} userRole={userRole} closeSheet={() => {}} /> <NavigationMenu location={location} userRole={userRole} closeSheet={() => {}} />
</div> </div>
</aside> </aside>
{/* min-w-0 is required: without it a wide child (table, chart, scheduler) {/* The page's scrollport — the one place vertical scrolling happens.
stretches this flex item and forces horizontal scroll on the page. */} `min-w-0`/`min-h-0` are both required: without them a wide table or a
<main className="flex-1 min-w-0 overflow-auto pb-8"> tall page stretches this flex item, which is what used to push the
whole shell past the viewport and take the side tracks with it.
`relative` makes this the containing block for the page's absolutely
positioned descendants. Without it, the hidden <select>/<input> nodes
that form controls render for accessibility resolve against the
viewport instead, and a form field far down a long page sits at a
document coordinate below the fold — which quietly gives the DOCUMENT
a scroll range again, and with it the ability to drag the whole shell
(rail and Owliver included) upward. */}
<main ref={mainRef} className="relative flex-1 min-w-0 min-h-0 overflow-auto pb-8">
{children} {children}
</main> </main>
{/* From `md` up Owliver is a track in this row, to the right of the page,
mirroring the nav rail on the left — it reserves its own width, so it
never covers the page. Below `md` the same element is a fixed bottom
sheet and therefore out of flow, so the row gives it no width and
<main> spans the viewport. See .krow-owliver-panel in index.css. */}
<OwliverShell />
</div> </div>
<NotificationPanel isOpen={showNotifications} onClose={() => setShowNotifications(false)} /> <NotificationPanel isOpen={showNotifications} onClose={() => setShowNotifications(false)} />
<NotificationEngine /> <NotificationEngine />
<OwliverShell />
<Toaster /> <Toaster />
</div> </div>
); );

View File

@@ -1,13 +0,0 @@
import { createClient } from '@base44/sdk';
import { appParams } from '@/lib/app-params';
const { appId, serverUrl, token, functionsVersion } = appParams;
//Create a client with authentication required
export const base44 = createClient({
appId,
serverUrl,
token,
functionsVersion,
requiresAuth: false
});

46
src/api/client.js Normal file
View File

@@ -0,0 +1,46 @@
/**
* The application client — `krow`.
*
* This is the single object every page and component reads and writes through.
* It is entirely local: entity data lives in the browser (see local/store.js),
* the user is a local profile (local/auth.js), integrations are local
* implementations (local/integrations.js), and the assistant answers on-device
* (local/agents.js).
*
* There is no client initialisation, no app id, no token, no server URL, and no
* network request anywhere behind it — the app runs the same offline as online,
* locally and deployed.
*/
import { entities, resetLocalData, setCurrentUserResolver } from './local/store';
import { auth, getCurrentUser, resetLocalUser } from './local/auth';
import { integrations } from './local/integrations';
import { agents } from './local/agents';
// Records get stamped with the local user's email; wired here to keep the store
// free of any dependency on the auth module.
setCurrentUserResolver(getCurrentUser);
export const krow = {
entities,
auth,
integrations,
agents,
/** Invitations have nowhere to go without a backend; recorded locally. */
users: {
async inviteUser({ email, role, user_role } = {}) {
console.info('[local invite] recorded locally, no email sent:', { email, role, user_role });
return { status: 'recorded_locally', email };
},
},
/** Wipe local data and restore the bundled demo dataset. */
resetLocalData() {
resetLocalData();
// The cached profile pointed at a row that no longer exists.
resetLocalUser();
},
};
export default krow;

View File

@@ -1,9 +0,0 @@
import { base44 } from './base44Client';
export const Query = base44.entities.Query;
// auth sdk:
export const User = base44.auth;

View File

@@ -1,24 +0,0 @@
import { base44 } from './base44Client';
export const Core = base44.integrations.Core;
export const InvokeLLM = base44.integrations.Core.InvokeLLM;
export const SendEmail = base44.integrations.Core.SendEmail;
export const SendSMS = base44.integrations.Core.SendSMS;
export const UploadFile = base44.integrations.Core.UploadFile;
export const GenerateImage = base44.integrations.Core.GenerateImage;
export const ExtractDataFromUploadedFile = base44.integrations.Core.ExtractDataFromUploadedFile;

143
src/api/local/agents.js Normal file
View File

@@ -0,0 +1,143 @@
/**
* Local assistant conversations (Owliver / KROW AI).
*
* Threads live in the local store, so they persist across reloads like any other
* local data. Replies come from the on-device Owliver engine
* (src/lib/owliver/owliverEngine.js) — the same engine the Owliver panel uses —
* loaded on demand so it only ships to pages that actually open the assistant.
*
* Where the hosted version needed a websocket room and an HTTP poll to find out
* what the agent had said, `subscribeToConversation` is now a direct in-process
* subscription: the reply is written to the thread and every subscriber is
* handed it in the same tick.
*/
import { entities } from './store';
import { stripContext } from '@/lib/owliver/context';
const CONVERSATIONS = 'AgentConversation';
const newMessageId = () =>
`msg-${Date.now().toString(36)}-${Math.floor(Math.random() * 1e6).toString(36)}`;
const subscribers = new Map(); // conversationId -> Set<callback>
function notify(conversation) {
const subs = subscribers.get(conversation.id);
if (!subs?.size) return;
subs.forEach((cb) => {
try {
cb(conversation);
} catch (err) {
console.error('[local agents] subscriber threw:', err);
}
});
}
let enginePromise = null;
async function engine() {
if (!enginePromise) {
enginePromise = import('@/lib/owliver/owliverEngine').then((m) => m.getOwliverResponse);
}
return enginePromise;
}
export const agents = {
async createConversation({ agent_name, metadata } = {}) {
return entities[CONVERSATIONS].create({
agent_name: agent_name || 'krow_ai',
metadata: metadata || {},
name: metadata?.name || 'New Conversation',
messages: [],
});
},
async getConversation(conversationId) {
return entities[CONVERSATIONS].get(conversationId);
},
async listConversations({ agent_name } = {}) {
const query = agent_name ? { agent_name } : {};
return entities[CONVERSATIONS].filter(query, '-created_date');
},
/**
* Append the user's turn, then answer it locally. The answer is produced after
* this call resolves and delivered to subscribers, which keeps the ordering the
* chat pages expect: send returns, the "thinking" state shows, the reply lands.
*/
async addMessage(conversation, message) {
const id = conversation?.id || conversation;
const current = await entities[CONVERSATIONS].get(id);
const priorMessages = current.messages || [];
const userMessage = {
id: newMessageId(),
role: message?.role || 'user',
content: message?.content || '',
created_date: new Date().toISOString(),
};
const withUser = await entities[CONVERSATIONS].update(id, {
messages: [...priorMessages, userMessage],
// First user turn names the thread, matching what the sidebar shows.
name:
current.name && current.name !== 'New Conversation'
? current.name
: userMessage.content.slice(0, 40) || 'New Conversation',
});
notify(withUser);
if (userMessage.role !== 'user') return withUser;
// Answer out of band: the caller awaits delivery of its own message only.
(async () => {
try {
const respond = await engine();
const content = await respond({
// A thread here has no page attached, so the engine answers generally.
prompt: stripContext(userMessage.content) || userMessage.content,
pageContext: null,
conversation: priorMessages,
});
const latest = await entities[CONVERSATIONS].get(id);
const answered = await entities[CONVERSATIONS].update(id, {
messages: [
...(latest.messages || []),
{
id: newMessageId(),
role: 'assistant',
content,
created_date: new Date().toISOString(),
},
],
});
notify(answered);
} catch (err) {
console.error('[local agents] could not produce a reply:', err);
}
})();
return withUser;
},
/** Returns an unsubscribe function, like the call sites expect. */
subscribeToConversation(conversationId, callback) {
if (!conversationId || typeof callback !== 'function') return () => {};
if (!subscribers.has(conversationId)) subscribers.set(conversationId, new Set());
subscribers.get(conversationId).add(callback);
// Deliver the thread as it stands now, so a late subscriber never misses a
// reply that landed before it joined.
entities[CONVERSATIONS]
.get(conversationId)
.then((conversation) => callback(conversation))
.catch(() => {
/* a brand-new thread may not be written yet — the next notify covers it */
});
return () => subscribers.get(conversationId)?.delete(callback);
},
};
export default agents;

117
src/api/local/auth.js Normal file
View File

@@ -0,0 +1,117 @@
/**
* Local user — replaces the hosted login/session that used to gate the app.
*
* There is no sign-in step and no session to check: the app opens straight into
* the KROW UI as a single local operator. The user is a row in the local `User`
* collection, so profile edits (`updateMe`) and the User Management page write
* to the same place, and both survive a reload via localStorage.
*
* `logout()` stays because the menu item does — locally it resets the profile to
* its defaults and keeps the user in the app.
*/
import { entities } from './store';
import defaultAvatar from '@/assets/krow-logo.webp';
export const LOCAL_USER_ID = 'local-user';
/** The default operator profile: enough for every UI state that reads a user. */
export const DEFAULT_USER = {
id: LOCAL_USER_ID,
full_name: 'KROW Operator',
email: 'operator@krow.local',
// `role` is the platform-level flag the UI checks for admin-only affordances;
// `user_role` drives which navigation set the sidebar renders.
role: 'admin',
user_role: 'admin',
company_name: 'KROW',
phone: '(415) 555-0100',
profile_picture: null,
is_active: true,
};
/**
* Kept in sync with the User collection so anything reading the user
* synchronously (created_by stamping, for instance) sees current values.
*/
let currentUser = { ...DEFAULT_USER };
/**
* Memoised, not a boolean flag: several components ask for the user at once on
* first paint, and two concurrent hydrations would each miss the row and create
* a duplicate.
*/
let hydration = null;
function hydrate() {
if (!hydration) {
hydration = (async () => {
const rows = await entities.User.filter({ id: LOCAL_USER_ID });
let user = rows.length
? { ...DEFAULT_USER, ...rows[0] }
: await entities.User.create({ ...DEFAULT_USER });
if (user.profile_picture === defaultAvatar) {
user = await entities.User.update(LOCAL_USER_ID, { profile_picture: null });
}
currentUser = user;
return currentUser;
})();
}
return hydration;
}
export const getCurrentUser = () => currentUser;
/**
* Forget the cached profile — used after local data is reset, so the next read
* re-creates the operator row instead of writing to one that is gone.
*/
export function resetLocalUser() {
hydration = null;
currentUser = { ...DEFAULT_USER };
}
/** Write a patch to the profile row, re-creating it if it has been wiped. */
async function writeProfile(patch) {
await hydrate();
try {
currentUser = await entities.User.update(LOCAL_USER_ID, patch);
} catch {
currentUser = await entities.User.create({ ...DEFAULT_USER, ...patch, id: LOCAL_USER_ID });
}
return { ...currentUser };
}
export const auth = {
/** The current (local) user. Never throws and never hits the network. */
async me() {
return { ...(await hydrate()) };
},
/** Same value as `me()`, kept because some call sites use this name. */
async currentUser() {
return { ...(await hydrate()) };
},
/** Always true — the app has no unauthenticated state any more. */
async isAuthenticated() {
return true;
},
/** Patch the local profile. Persisted with the rest of the local data. */
async updateMe(data = {}) {
return writeProfile(data);
},
/**
* There is no session to end. Reset the profile back to its defaults and stay
* in the app, so the "Sign out" menu item does something honest instead of
* bouncing the user to an external login page.
*/
async logout() {
return writeProfile({ ...DEFAULT_USER });
},
};
export default auth;

View File

@@ -0,0 +1,149 @@
/**
* Local stand-ins for the integration calls the UI makes.
*
* None of these leave the browser. Files are read into local object/data URLs,
* outbound email/SMS are recorded locally so the surrounding flow (toasts,
* activity feeds, "sent" states) still completes, and the model-backed helpers
* return a well-formed local result shaped to whatever schema the caller asked
* for — so a page that awaits one renders its normal result state instead of an
* error, without anything being generated off-device.
*/
const noteKey = 'krow.local.outbox';
/** Append to a small local log, capped so it can never grow unbounded. */
function record(kind, payload) {
const entry = { kind, at: new Date().toISOString(), payload };
try {
const existing = JSON.parse(window.localStorage.getItem(noteKey) || '[]');
existing.unshift(entry);
window.localStorage.setItem(noteKey, JSON.stringify(existing.slice(0, 50)));
} catch {
/* storage is optional here — the console line below is the useful part */
}
console.info(`[local ${kind}]`, payload);
return entry;
}
/** Read a File/Blob into a data URL so it can be stored and previewed offline. */
function readAsDataUrl(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error || new Error('Could not read file'));
reader.readAsDataURL(file);
});
}
/**
* Small files become data URLs (persistable, survive a reload); large ones get a
* blob URL instead, which is valid for the session but keeps multi-megabyte
* payloads out of localStorage.
*/
const DATA_URL_LIMIT = 1.5 * 1024 * 1024;
async function localFileUrl(file) {
if (!file) return '';
if (file.size <= DATA_URL_LIMIT) return readAsDataUrl(file);
return URL.createObjectURL(file);
}
/* ------------------------------------------------------- schema-shaped result */
/** Build an empty-but-valid value for a JSON-schema node. */
function blankFromSchema(schema) {
if (!schema || typeof schema !== 'object') return null;
if (Array.isArray(schema.enum) && schema.enum.length) return schema.enum[0];
if (schema.default !== undefined) return schema.default;
switch (schema.type) {
case 'object': {
const out = {};
Object.entries(schema.properties || {}).forEach(([key, value]) => {
out[key] = blankFromSchema(value);
});
return out;
}
case 'array':
return [];
case 'number':
case 'integer':
return 0;
case 'boolean':
return false;
case 'string':
return '';
default:
return null;
}
}
export const Core = {
/**
* No model runs locally, so this resolves to an empty result in the exact
* shape the caller declared. Callers that asked for free text get a short
* explanatory string rather than a fabricated answer.
*/
async InvokeLLM({ prompt, response_json_schema } = {}) {
record('assist', { prompt: String(prompt || '').slice(0, 300) });
if (response_json_schema) return blankFromSchema(response_json_schema);
return 'Assisted suggestions are unavailable in this local build. Fill the fields in directly — everything else works as usual.';
},
async SendEmail({ to, subject, body } = {}) {
record('email', { to, subject, body });
return { status: 'recorded_locally', to, subject };
},
async SendSMS({ to, body, message } = {}) {
record('sms', { to, body: body || message });
return { status: 'recorded_locally', to };
},
/** Returns `{ file_url }`, which is what every upload call site destructures. */
async UploadFile({ file } = {}) {
const file_url = await localFileUrl(file);
record('upload', { name: file?.name, size: file?.size });
return { file_url, file_name: file?.name, file_size: file?.size };
},
async UploadPrivateFile({ file } = {}) {
const file_url = await localFileUrl(file);
return { file_url, file_uri: file_url, file_name: file?.name };
},
/** Local files are already directly addressable, so the URL passes through. */
async CreateFileSignedUrl({ file_uri, file_url } = {}) {
const url = file_url || file_uri || '';
return { signed_url: url, url };
},
/** A neutral local placeholder image, so image slots render something valid. */
async GenerateImage({ prompt } = {}) {
record('image', { prompt: String(prompt || '').slice(0, 200) });
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="640" height="360">
<rect width="640" height="360" fill="#0039A6"/>
<text x="320" y="188" font-family="system-ui,sans-serif" font-size="28"
fill="#ffffff" text-anchor="middle">KROW</text>
</svg>`;
const url = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
return { url, file_url: url };
},
/**
* Document parsing needs a service; locally the file is accepted and stored,
* and the caller is told extraction produced nothing so it falls back to
* manual entry instead of showing a failure.
*/
async ExtractDataFromUploadedFile({ json_schema } = {}) {
return {
status: 'success',
output: json_schema ? blankFromSchema(json_schema) : null,
details: 'Automatic extraction is unavailable in this local build.',
};
},
};
export const integrations = { Core };
export default integrations;

8231
src/api/local/schemas.json Normal file

File diff suppressed because it is too large Load Diff

1370
src/api/local/seedData.js Normal file

File diff suppressed because it is too large Load Diff

431
src/api/local/store.js Normal file
View File

@@ -0,0 +1,431 @@
/**
* Local collection store — the application's only data engine.
*
* Every entity read/write in the app funnels through here. There is no network
* call in this file and no server anywhere behind it: a collection lives in
* memory for the lifetime of the tab and is mirrored into localStorage so edits
* survive a reload. If localStorage is unavailable (private mode, quota, an
* embedded webview) the store silently degrades to memory-only — reads and
* writes keep working for the session, they just don't persist.
*
* The method surface (list/filter/get/create/update/delete/bulkCreate/schema/
* subscribe) matches what the pages already call, so no page needed rewriting
* to move off the previous hosted backend.
*/
import { seedData } from './seedData';
const KEY_PREFIX = 'krow.local.';
const SEED_STAMP_KEY = `${KEY_PREFIX}__seeded`;
/**
* Which version of the demo dataset this browser was initialised with. A
* collection is seeded only when its storage key is absent, so once a user has
* data it is never overwritten; this stamp is what tells a future migration
* which shape that stored data started from.
*/
const SEED_VERSION = '1';
const storageAvailable = (() => {
try {
const probe = `${KEY_PREFIX}__probe`;
window.localStorage.setItem(probe, '1');
window.localStorage.removeItem(probe);
return true;
} catch {
return false;
}
})();
const clone = (value) =>
value === undefined ? value : JSON.parse(JSON.stringify(value));
const nowIso = () => new Date().toISOString();
let idCounter = 0;
const newId = () => {
if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
idCounter += 1;
return `loc-${Date.now().toString(36)}-${idCounter}`;
};
/* ------------------------------------------------------------------ storage */
const memory = new Map(); // entityName -> records[]
const listeners = new Map(); // entityName -> Set<callback>
function readStored(name) {
if (!storageAvailable) return null;
try {
const raw = window.localStorage.getItem(KEY_PREFIX + name);
if (!raw) return null;
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : null;
} catch {
return null;
}
}
function persist(name) {
if (!storageAvailable) return;
try {
window.localStorage.setItem(KEY_PREFIX + name, JSON.stringify(memory.get(name) || []));
} catch (err) {
// Out of quota is the realistic failure here (a large data-URL upload, say).
// The in-memory copy is still correct, so the session continues unaffected.
console.warn(`[local store] could not persist ${name}:`, err?.message || err);
}
}
function seedStamp() {
if (!storageAvailable) return null;
try {
return window.localStorage.getItem(SEED_STAMP_KEY);
} catch {
return null;
}
}
function writeSeedStamp() {
if (!storageAvailable) return;
try {
window.localStorage.setItem(SEED_STAMP_KEY, SEED_VERSION);
} catch {
/* non-fatal — worst case the demo data is re-seeded next load */
}
}
/** Stamp seed rows so they look exactly like rows the app created itself. */
function normalizeSeed(rows) {
const created = nowIso();
return rows.map((row, index) => ({
id: row.id || `seed-${index}-${newId()}`,
created_date: row.created_date || created,
updated_date: row.updated_date || created,
created_by: row.created_by || 'demo@krow.local',
...row,
}));
}
function collection(name) {
if (!memory.has(name)) {
const stored = readStored(name);
if (stored) {
memory.set(name, stored);
} else {
const seed = seedData[name];
memory.set(name, seed ? normalizeSeed(clone(seed)) : []);
if (seed) persist(name);
}
}
return memory.get(name);
}
function emit(name) {
const subs = listeners.get(name);
if (!subs?.size) return;
const payload = clone(memory.get(name) || []);
subs.forEach((cb) => {
try {
cb(payload);
} catch (err) {
console.error(`[local store] subscriber for ${name} threw:`, err);
}
});
}
/* ------------------------------------------------------------------ querying */
const getPath = (record, path) =>
path.split('.').reduce((acc, key) => (acc == null ? undefined : acc[key]), record);
const eq = (a, b) => {
if (a === b) return true;
// Dates and numbers arrive as strings from form inputs often enough that a
// loose compare is the friendlier default for a demo-data layer.
if (a == null || b == null) return false;
if (typeof a === 'object' || typeof b === 'object') {
return JSON.stringify(a) === JSON.stringify(b);
}
return String(a) === String(b);
};
function matchesOperators(value, ops) {
return Object.entries(ops).every(([op, operand]) => {
switch (op) {
case '$eq':
return eq(value, operand);
case '$ne':
return !eq(value, operand);
case '$in':
return Array.isArray(operand) && operand.some((o) => eq(value, o));
case '$nin':
return Array.isArray(operand) && !operand.some((o) => eq(value, o));
case '$gt':
return value != null && value > operand;
case '$gte':
return value != null && value >= operand;
case '$lt':
return value != null && value < operand;
case '$lte':
return value != null && value <= operand;
case '$exists':
return operand ? value !== undefined && value !== null : value === undefined || value === null;
case '$contains':
if (Array.isArray(value)) return value.some((v) => eq(v, operand));
return String(value ?? '').toLowerCase().includes(String(operand ?? '').toLowerCase());
case '$regex':
try {
return new RegExp(operand, 'i').test(String(value ?? ''));
} catch {
return false;
}
default:
// Unknown operator: treat the whole object as a value comparison rather
// than silently dropping the condition.
return eq(value, ops);
}
});
}
function matches(record, query) {
if (!query) return true;
return Object.entries(query).every(([key, expected]) => {
if (expected === undefined) return true; // an unset filter is not a filter
if (key === '$or' && Array.isArray(expected)) {
return expected.some((sub) => matches(record, sub));
}
if (key === '$and' && Array.isArray(expected)) {
return expected.every((sub) => matches(record, sub));
}
const value = getPath(record, key);
if (expected && typeof expected === 'object' && !Array.isArray(expected)) {
const keys = Object.keys(expected);
if (keys.length && keys.every((k) => k.startsWith('$'))) {
return matchesOperators(value, expected);
}
return eq(value, expected);
}
// Equality against an array field means "contains" — matching how the
// pages use it (e.g. filtering records whose tags include a value).
if (Array.isArray(value) && !Array.isArray(expected)) {
return value.some((v) => eq(v, expected));
}
return eq(value, expected);
});
}
function compare(a, b) {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
if (typeof a === 'number' && typeof b === 'number') return a - b;
return String(a).localeCompare(String(b), undefined, { numeric: true });
}
function applySort(rows, sort) {
if (!sort) return rows;
const fields = (Array.isArray(sort) ? sort : String(sort).split(','))
.map((f) => String(f).trim())
.filter(Boolean);
if (!fields.length) return rows;
return rows.sort((a, b) => {
for (const field of fields) {
const desc = field.startsWith('-');
const key = desc ? field.slice(1) : field;
const result = compare(getPath(a, key), getPath(b, key));
if (result !== 0) return desc ? -result : result;
}
return 0;
});
}
function paginate(rows, limit, skip) {
const start = Number(skip) > 0 ? Number(skip) : 0;
const end = Number(limit) > 0 ? start + Number(limit) : undefined;
return rows.slice(start, end);
}
/* ------------------------------------------------------------------ entities */
let schemaCachePromise = null;
/** Entity schemas are a sizeable local JSON file, so they load on demand only. */
async function loadSchemas() {
if (!schemaCachePromise) {
schemaCachePromise = import('./schemas.json').then((m) => m.default || m);
}
return schemaCachePromise;
}
/** Who to stamp on created records. Resolved lazily to avoid an import cycle. */
let currentUserRef = () => null;
export function setCurrentUserResolver(fn) {
currentUserRef = fn;
}
function createEntity(name) {
const api = {
entityName: name,
async list(sort, limit, skip) {
return paginate(applySort(clone(collection(name)), sort), limit, skip);
},
async filter(query, sort, limit, skip) {
const rows = collection(name).filter((row) => matches(row, query));
return paginate(applySort(clone(rows), sort), limit, skip);
},
async get(id) {
const found = collection(name).find((row) => row.id === id);
if (!found) {
const error = new Error(`${name} ${id} not found`);
error.status = 404;
throw error;
}
return clone(found);
},
async findOne(query) {
const found = collection(name).find((row) => matches(row, query));
return found ? clone(found) : null;
},
async create(data = {}) {
const stamp = nowIso();
const record = {
...clone(data),
id: data.id || newId(),
created_date: data.created_date || stamp,
updated_date: stamp,
created_by: data.created_by || currentUserRef()?.email || 'local@krow.app',
};
collection(name).push(record);
persist(name);
emit(name);
return clone(record);
},
async bulkCreate(items = []) {
const created = [];
for (const item of items) {
// Reuse create() so every row gets identical stamping, but write once.
const stamp = nowIso();
const record = {
...clone(item),
id: item.id || newId(),
created_date: item.created_date || stamp,
updated_date: stamp,
created_by: item.created_by || currentUserRef()?.email || 'local@krow.app',
};
collection(name).push(record);
created.push(record);
}
persist(name);
emit(name);
return clone(created);
},
async update(id, data = {}) {
const rows = collection(name);
const index = rows.findIndex((row) => row.id === id);
if (index === -1) {
const error = new Error(`${name} ${id} not found`);
error.status = 404;
throw error;
}
rows[index] = {
...rows[index],
...clone(data),
id,
updated_date: nowIso(),
};
persist(name);
emit(name);
return clone(rows[index]);
},
async delete(id) {
const rows = collection(name);
const index = rows.findIndex((row) => row.id === id);
if (index !== -1) {
rows.splice(index, 1);
persist(name);
emit(name);
}
return { success: true, id };
},
async schema() {
const schemas = await loadSchemas();
return schemas[name] || { name, type: 'object', properties: {} };
},
/** Local pub/sub in place of the old websocket rooms. */
subscribe(callback) {
if (typeof callback !== 'function') return () => {};
if (!listeners.has(name)) listeners.set(name, new Set());
listeners.get(name).add(callback);
// Deliver the current contents immediately so a subscriber never has to
// do a separate first read.
Promise.resolve().then(() => callback(clone(collection(name))));
return () => listeners.get(name)?.delete(callback);
},
};
return api;
}
const entityCache = new Map();
/**
* `entities.Anything` resolves to a local collection, created on first touch.
* A page that reads an entity nobody has written to simply gets `[]`, which is
* the empty state the UI already renders.
*/
export const entities = new Proxy(
{},
{
get(_target, prop) {
if (typeof prop !== 'string') return undefined;
if (!entityCache.has(prop)) entityCache.set(prop, createEntity(prop));
return entityCache.get(prop);
},
has() {
return true;
},
}
);
/** Direct access for code that needs a collection by name at runtime. */
export const getEntity = (name) => entities[name];
/** Wipe local data and restore the bundled demo dataset. */
export function resetLocalData() {
const names = new Set([...memory.keys(), ...Object.keys(seedData)]);
memory.clear();
if (storageAvailable) {
names.forEach((name) => {
try {
window.localStorage.removeItem(KEY_PREFIX + name);
} catch {
/* ignore */
}
});
}
writeSeedStamp();
names.forEach((name) => {
collection(name);
emit(name);
});
}
// First run of a given seed version: record it so a later version bump can tell
// "never seeded" from "seeded, then emptied by the user".
if (seedStamp() !== SEED_VERSION) writeSeedStamp();

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View File

@@ -1,37 +0,0 @@
import { useEffect } from 'react';
import { Outlet } from 'react-router-dom';
import { useAuth } from '@/lib/AuthContext';
import UserNotRegisteredError from '@/components/UserNotRegisteredError';
const DefaultFallback = () => (
<div className="fixed inset-0 flex items-center justify-center">
<div className="w-8 h-8 border-4 border-slate-200 border-t-slate-800 rounded-full animate-spin"></div>
</div>
);
export default function ProtectedRoute({ fallback = <DefaultFallback />, unauthenticatedElement }) {
const { isAuthenticated, isLoadingAuth, authChecked, authError, checkUserAuth } = useAuth();
useEffect(() => {
if (!authChecked && !isLoadingAuth) {
checkUserAuth();
}
}, [authChecked, isLoadingAuth, checkUserAuth]);
if (isLoadingAuth || !authChecked) {
return fallback;
}
if (authError) {
if (authError.type === 'user_not_registered') {
return <UserNotRegisteredError />;
}
return unauthenticatedElement;
}
if (!isAuthenticated) {
return unauthenticatedElement;
}
return <Outlet />;
}

Some files were not shown because too many files have changed in this diff Show More