Merge pull request #194 from Oloodi/cleanup_for_sprint3

Cleanup for sprint3
This commit is contained in:
Oloodi Admin
2026-01-10 23:50:30 -05:00
committed by GitHub
2433 changed files with 42216 additions and 300435 deletions

85
.geminiignore Normal file
View File

@@ -0,0 +1,85 @@
# =============================================================================
# KROW Workforce - .geminiignore
#
# Indicates to Gemini which files/folders to ignore during analysis
# to maintain relevant context and save tokens.
# =============================================================================
# -----------------------------------------------------------------------------
# Standard Ignores (Same as .gitignore)
# -----------------------------------------------------------------------------
node_modules/
dist/
build/
coverage/
.git/
.idea/
.vscode/
.DS_Store
secrets/
.env*
# -----------------------------------------------------------------------------
# Package Manager Locks (Too large / No semantic value)
# -----------------------------------------------------------------------------
package-lock.json
yarn.lock
pnpm-lock.yaml
pubspec.lock
# -----------------------------------------------------------------------------
# Build Artifacts & Caches
# -----------------------------------------------------------------------------
.firebase/
.vite/
.dart_tool/
.pub-cache/
.gradle/
__pycache__/
*.tsbuildinfo
*.cache
*.log
# -----------------------------------------------------------------------------
# Large Binary / Media Files
# -----------------------------------------------------------------------------
*.png
*.jpg
*.jpeg
*.gif
*.ico
*.svg
*.mp4
*.mov
*.pdf
*.zip
*.tar.gz
*.apk
*.aab
*.ipa
# -----------------------------------------------------------------------------
# Generated Code (Reduce noise unless specifically debugging)
# -----------------------------------------------------------------------------
# Data Connect generated SDKs are useful for reference, but can be verbose.
# Uncomment if you want Gemini to ignore them completely.
# **/dataconnect-generated/
# -----------------------------------------------------------------------------
# Documentation to KEEP (Force Include)
# -----------------------------------------------------------------------------
# Ensure these are never ignored even if a broad rule matches
!README.md
!CONTRIBUTING.md
!docs/*.md
!docs/**/*.md
# -----------------------------------------------------------------------------
# Specific Directories
# -----------------------------------------------------------------------------
# Prototypes: We WANT Gemini to see these for context if they are synced locally,
# even if they are ignored by Git. So we do NOT ignore them here.
# Temporary migration folders
_legacy/
krow-workforce-export-latest/

View File

@@ -1,29 +0,0 @@
---
name: 'API Validation and Backend Update from Base44'
about: 'Use this template to analyze Base44 API changes and update our backend.'
title: '[Base44] API Changes Validation and Backend Update'
labels: 'enhancement, platform:backend, sred-eligible'
---
### 🎯 Objective
Analyze API and event changes from the latest Base44 export, validate their impact on our system, and update our backend (Data Connect, Cloud Functions) to ensure compatibility and integrate new functionalities.
---
### 🔬 SR&ED Justification
* **Technological Uncertainty:** What is the impact of Base44 API schema changes (deprecated fields, new types, modified logic) on our Data Connect GraphQL schemas and Cloud Functions logic? Is there a risk of data corruption or contract breakage with client applications?
* **Systematic Investigation:** We will perform a differential analysis ("diff") of the API schemas, update our GraphQL schema accordingly, adapt business logic in connectors and functions, and then deploy to a test environment to validate end-to-end integration.
---
### 💻 Technical Implementation Notes
Refer to the [API Documentation Maintenance Guide](docs/06-maintenance-guide.md) for detailed steps on how to update the backend from a Base44 export.
---
### ✅ Acceptance Criteria
- [ ] The impact analysis of API changes is documented.
- [ ] Data Connect schemas and connectors are updated.
- [ ] Affected Cloud Functions are updated.
- [ ] Changes are deployed and validated in the `dev` environment.
- [ ] **Given** an operation (query or mutation) affected by the changes is executed, **when** the API is called, **then** it should return a correct and consistent response with the new schema, without errors.

View File

@@ -1,29 +0,0 @@
---
name: 'Frontend Update from Base44'
about: 'Use this template to integrate the latest frontend changes from a Base44 export.'
title: '[Base44] Frontend Update from Export'
labels: 'enhancement, platform:web, sred-eligible'
---
### 🎯 Objective
Integrate the latest UI changes (components, queries, mutations) generated from the most recent Base44 export to keep our application up-to-date with the platform schema.
---
### 🔬 SR&ED Justification
* **Technological Uncertainty:** How will the newly generated queries and mutations integrate with our existing state management and caching logic (e.g., TanStack Query) without introducing regressions or performance issues?
* **Systematic Investigation:** We will implement the new generated hooks, refactor affected components, and perform performance and non-regression tests to validate that the integration is robust and performant.
---
### 💻 Technical Implementation Notes
Refer to the [API Documentation Maintenance Guide](docs/06-maintenance-guide.md) for detailed steps on how to update the frontend from a Base44 export.
---
### ✅ Acceptance Criteria
- [ ] The Data Connect generated code is updated to the latest version.
- [ ] All components and pages affected by the changes are updated and function as expected.
- [ ] The code is linted and formatted correctly.
- [ ] The functionality is manually tested and works in the `dev` environment.
- [ ] **Given** I navigate to a page affected by the update, **when** data is loaded, **then** I should see the new data structured correctly with no console errors.

View File

@@ -1,31 +0,0 @@
---
name: SR&ED Task
about: Use this template for a new development task that may be eligible for SR&ED.
title: '[Category] Short description of the task'
labels: 'sred-eligible'
---
### 🎯 Objective
*(A concise, one-sentence summary of what this issue aims to accomplish. Ex: "Connect the Events page to the development backend.")*
---
### 🔬 SR&ED Justification
* **Technological Uncertainty:** What is the technical challenge or unknown we are trying to solve? *(Ex: "Can the Data Connect generated SDK be performantly integrated with TanStack Query in our existing React architecture?")*
* **Systematic Investigation:** What is our planned approach to resolve this uncertainty? *(Ex: "We will build a PoC on the Events page, measure load times, and document the optimal integration pattern.")*
---
### 💻 Technical Implementation Notes
* **Key Files to Modify:** `file1.js`, `file2.gql`, etc.
* **Suggested Approach:** A brief description of the technical steps. *(Ex: "1. Define `listEvents` query in GraphQL. 2. Generate the SDK. 3. Create a `useEvents` hook that uses `useQuery`...")*
* **Considerations:** Potential pitfalls or points to watch out for. *(Ex: "Ensure loading and error states are handled correctly.")*
---
### ✅ Acceptance Criteria
*A checklist of what must be true for the task to be considered "done."*
- [ ] The code is implemented following the technical notes.
- [ ] All new code is linted and formatted.
- [ ] The functionality is tested and works as expected in the `dev` environment.
- [ ] *(Example for a UI task)* **Given** I am on the Events page, **when** the page loads, **then** I should see a list of events coming from the `dev` backend.

199
.gitignore vendored
View File

@@ -1,85 +1,170 @@
# General
# ----------------------------------------------------------------
# macOS generated files
# ==============================================================================
# GLOBAL & OS
# ==============================================================================
.DS_Store
.DS_Store?
._*
.AppleDouble
.LSOverride
.Spotlight-V100
.Trashes
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
desktop.ini
$RECYCLE.BIN/
.Trash-*
# IDE configuration files
# IDE & Editors
.idea/
.vscode/
*.iml
*.iws
*.swp
*.swo
*~
\#*\#
.\#*
# Log and cache files
# Logs & Cache
*.log
*.cache
*.pyc
__pycache__/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
*.pid
*.seed
*.pid.lock
# Secrets (never commit these)
# The secrets/ directory is handled by its own .gitignore, but this is an extra layer of safety.
# Temporary Files
*.tmp
*.temp
tmp/
temp/
# ==============================================================================
# SECURITY (CRITICAL)
# ==============================================================================
# Secrets directory (contains API keys, service accounts)
secrets/
*.env
.env.*
# Environment files
.env
.env.local
.env.*.local
.env.development
.env.production
.env.test
!.env.example
# OS-specific files
Thumbs.db
ehthumbs.db
# Certificates & Keys
*.pem
*.key
*.jks
*.keystore
*.p12
*.cer
# GCP Service Account Keys
gcp_keys/
**/*.service-account.json
**/sa.json
# Frontend Web (Vite / React)
# ----------------------------------------------------------------
frontend-web/node_modules/
frontend-web/dist/
frontend-web/.env.local
frontend-web/coverage/
frontend-web/.vite/
/frontend-web/src/dataconnect-generated/
/internal-api-harness/src/dataconnect-generated/
frontend-web-free/src/dataconnect-generated/
# NPM Auth
.npmrc
**/.npmrc
!**/.npmrc.template
# ==============================================================================
# NODE.JS / WEB (React, Vite, Functions)
# ==============================================================================
node_modules/
dist/
dist-ssr/
coverage/
.nyc_output/
.vite/
.temp/
*.local
.eslintcache
.stylelintcache
.npm
.turbo
.vercel
# Vite timestamps
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# Generated Data Connect SDKs in web projects
# Generally ignored as they are regenerated on build
**/dataconnect-generated/
# ==============================================================================
# FLUTTER / MOBILE
# ==============================================================================
# Flutter/Dart
.dart_tool/
.pub-cache/
.flutter-plugins
.flutter-plugins-dependencies
# Mobile (Flutter)
# ----------------------------------------------------------------
# Android
.gradle/
**/android/app/libs/
**/android/key.properties
**/android/local.properties
mobile-apps/*/.dart_tool/
mobile-apps/*/.pub-cache/
mobile-apps/*/build/
mobile-apps/*/.flutter-plugins
mobile-apps/*/.flutter-plugins-dependencies
# Build outputs
build/
# iOS
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/flutter_export_environment.sh
**/ios/Podfile.lock
**/ios/Pods/
**/ios/.symlinks/
# Firebase / Backend
# ----------------------------------------------------------------
# Python virtual environments
firebase/functions/venv/
firebase/dataconnect/venv/
# ==============================================================================
# FIREBASE & BACKEND
# ==============================================================================
# Firebase Cache & Emulators
.firebase/
dataconnect/.dataconnect/
backend/dataconnect/.dataconnect/
# Firebase emulator logs
*.log
firebase-debug.log
firebase-debug.*.log
firestore-debug.log
ui-debug.log
# Debug Logs (Recursive)
**/firebase-debug.log
**/firebase-debug.*.log
**/firestore-debug.log
**/ui-debug.log
**/database-debug.log
**/pubsub-debug.log
# Generated SDKs (can be regenerated, so often ignored)
# Decide with your team if you want to commit these or not.
# For now, we will commit them to simplify setup for new developers.
# @dataconnect/
# Python Virtual Envs (if used)
venv/
env/
ENV/
__pycache__/
*.py[cod]
*$py.class
*.so
# ==============================================================================
# PROJECT SPECIFIC
# ==============================================================================
# Secure hashes are committed, but the raw user list is usually kept for reference
# unless it contains sensitive info. Here we explicitly ignore the raw file.
internal/launchpad/iap-users.txt
# Temporary files from this project
# ----------------------------------------------------------------
# The Base44 export directory should not be committed
/krow-workforce-export-latest/
# Generated Prototypes (must be synced locally via 'make sync-prototypes')
internal/launchpad/prototypes/web/*
!internal/launchpad/prototypes/web/.keep
internal/launchpad/prototypes/mobile/**/*
!internal/launchpad/prototypes/mobile/**/.keep
# The legacy Laravel project should not be committed
/legacy-web/
# The temporary mobile app folders should not be committed
/flutter-mobile-client-app/
/flutter-mobile-staff-app/
/inspiration/
# Temporary migration artifacts
_legacy/
krow-workforce-export-latest/

View File

@@ -1,80 +0,0 @@
# Base44 Entity Schemas Reference
This document serves as a developer reference for the original data schemas from the Base44 backend. It is used to ensure feature parity and data integrity during the migration to the new GCP/Firebase backend.
---
## User Schema
```
role (text, required)
- The role of the user in the app
- Options: admin, user
email (text, required)
- The email of the user
full_name (text, required)
- Full name of the user
user_role (text)
- User's role in the system
- Options: admin, procurement, operator, sector, client, vendor, workforce
company_name (text)
- Company or organization name
profile_picture (text)
- URL to profile picture
phone (text)
- Phone number
address (text)
- Address
preferred_vendor_id (text)
- ID of the client's preferred/default vendor
preferred_vendor_name (text)
- Name of the client's preferred vendor
backup_vendor_ids (array)
- List of backup vendor IDs
dashboard_layout (object)
- User's customized dashboard layout preferences (legacy, kept for backward compatibility)
- widgets (array): Ordered list of visible widgets
- hidden_widgets (array): List of hidden widgets
- layout_version (text): Layout version for migration
dashboard_layout_client (object)
- Client dashboard layout
- widgets (array)
- hidden_widgets (array)
- layout_version (text)
dashboard_layout_vendor (object)
- Vendor dashboard layout
- widgets (array)
- hidden_widgets (array)
- layout_version (text)
dashboard_layout_operator (object)
- Operator dashboard layout
- widgets (array)
- hidden_widgets (array)
- layout_version (text)
dashboard_layout_workforce (object)
- Workforce dashboard layout
- widgets (array)
- hidden_widgets (array)
- layout_version (text)
preferences (object)
- User preferences and settings
- theme (text): Default: "light", Options: light, dark
- notifications_enabled (boolean): Default: true
- email_notifications (boolean): Default: true
```

540
Makefile
View File

@@ -1,60 +1,21 @@
# KROW Workforce Project Makefile
# -------------------------------
# This Makefile provides a central place for common project commands.
# It is designed to be the main entry point for developers.
# This is the main entry point. It includes modular Makefiles from the 'makefiles/' directory.
# Use .PHONY to declare targets that are not files, to avoid conflicts.
.PHONY: help install dev build integrate-export prepare-export deploy-launchpad deploy-launchpad-full deploy-app admin-install admin-dev admin-build deploy-admin deploy-admin-full configure-iap-launchpad configure-iap-admin list-iap-users remove-iap-user setup-labels export-issues create-issues-from-file install-git-hooks mobile-client-install mobile-client-dev mobile-client-build mobile-staff-install mobile-staff-dev mobile-staff-build dataconnect-enable-apis dataconnect-init dataconnect-deploy dataconnect-sql-migrate dataconnect-generate-sdk dataconnect-sync dataconnect-bootstrap-db
# The default command to run if no target is specified (e.g., just 'make').
# The default command to run if no target is specified.
.DEFAULT_GOAL := help
# --- Flutter check ---
FLUTTER := $(shell which flutter)
ifeq ($(FLUTTER),)
#$(error "flutter not found in PATH. Please install Flutter and add it to your PATH.")
endif
# --- Include Modules ---
include makefiles/common.mk
include makefiles/web.mk
include makefiles/launchpad.mk
include makefiles/mobile.mk
include makefiles/dataconnect.mk
include makefiles/tools.mk
# --- Firebase & GCP Configuration ---
GCP_DEV_PROJECT_ID := krow-workforce-dev
GCP_STAGING_PROJECT_ID := krow-workforce-staging
IAP_SERVICE_ACCOUNT := service-933560802882@gcp-sa-iap.iam.gserviceaccount.com
# --- Main Help Command ---
.PHONY: help
# --- Cloud Run Configuration ---
CR_ADMIN_SERVICE_NAME := admin-console
CR_ADMIN_REGION := us-central1
CR_ADMIN_IMAGE_URI = us-docker.pkg.dev/$(GCP_PROJECT_ID)/gcr-io/$(CR_ADMIN_SERVICE_NAME)
# --- Environment Detection ---
ENV ?= dev
SERVICE ?= launchpad # Default service for IAP commands: 'launchpad' or 'admin'
# --- Conditional Variables by Environment ---
ifeq ($(ENV),staging)
GCP_PROJECT_ID := $(GCP_STAGING_PROJECT_ID)
FIREBASE_ALIAS := staging
HOSTING_TARGET := app-staging
SQL_TIER := db-n1-standard-1
else
GCP_PROJECT_ID := $(GCP_DEV_PROJECT_ID)
FIREBASE_ALIAS := dev
HOSTING_TARGET := app-dev
SQL_TIER := db-g1-small
endif
# --- Conditional Variables by Service for IAP commands ---
ifeq ($(SERVICE),admin)
IAP_SERVICE_NAME := $(CR_ADMIN_SERVICE_NAME)
IAP_SERVICE_REGION := $(CR_ADMIN_REGION)
IAP_PROJECT_ID := $(GCP_PROJECT_ID) # Admin console is env-specific
else
IAP_SERVICE_NAME := $(CR_LAUNCHPAD_SERVICE_NAME)
IAP_SERVICE_REGION := $(CR_LAUNCHPAD_REGION)
IAP_PROJECT_ID := $(GCP_DEV_PROJECT_ID) # Launchpad is dev-only
endif
# Shows this help message.
help:
@echo "--------------------------------------------------"
@echo " KROW Workforce - Available Makefile Commands"
@@ -69,479 +30,26 @@ help:
@echo " --- MOBILE APP DEVELOPMENT ---"
@echo " make mobile-client-install - Install dependencies for client app"
@echo " make mobile-client-dev - Run client app in dev mode"
@echo " make mobile-client-build - Build client app (requires ENV & PLATFORM, optional BUILD_TYPE=apk)"
@echo ""
@echo " make mobile-client-build - Build client app (requires ENV & PLATFORM)"
@echo " make mobile-staff-install - Install dependencies for staff app"
@echo " make mobile-staff-dev - Run staff app in dev mode"
@echo " make mobile-staff-build - Build staff app (requires ENV & PLATFORM, optional BUILD_TYPE=apk)"
@echo " make mobile-staff-build - Build staff app (requires ENV & PLATFORM)"
@echo ""
@echo " --- DEPLOYMENT ---"
@echo " make deploy-launchpad-hosting - Deploys internal launchpad to Firebase Hosting (Auth via Firebase)."
@echo " make deploy-admin-full [ENV=staging] - Deploys Admin Console to Cloud Run with IAP (default: dev)."
@echo " make deploy-app [ENV=staging] - Builds and deploys the main web app via Firebase Hosting (default: dev)."
@echo " make deploy-launchpad-hosting - Deploys internal launchpad to Firebase Hosting."
@echo " make deploy-app [ENV=staging] - Builds and deploys the main web app (default: dev)."
@echo ""
@echo " --- CLOUD IAP MANAGEMENT ---"
@echo " make list-iap-users [SERVICE=admin] - Lists IAP users for a service (default: launchpad)."
@echo " make remove-iap-user USER=... [SERVICE=admin] - Removes an IAP user from a service."
@echo ""
@echo " --- PROJECT MANAGEMENT & TOOLS ---"
@echo " make setup-labels - Creates/updates GitHub labels from labels.yml."
@echo " make export-issues [ARGS=\"--state=all --label=bug\"] - Exports GitHub issues to a markdown file. See scripts/export_issues.sh for options."
@echo " make create-issues-from-file - Bulk creates GitHub issues from a markdown file."
@echo " --- DEVELOPMENT TOOLS ---"
@echo " make install-git-hooks - Installs git pre-push hook to protect main/dev branches."
@echo " make sync-prototypes - Builds and copies prototypes from adjacent 'client-krow-poc' repo."
@echo ""
@echo " --- DATA CONNECT MANAGEMENT ---"
@echo " make dataconnect-enable-apis - Enables required GCP APIs for Data Connect."
@echo " make dataconnect-init - Initializes Firebase Data Connect (interactive wizard)."
@echo " make dataconnect-deploy - Deploys Data Connect schemas (GraphQL -> Cloud SQL)."
@echo " make dataconnect-sql-migrate - Applies Data Connect SQL migrations to Cloud SQL."
@echo " make dataconnect-generate-sdk - Regenerates the Data Connect SDK (frontend-web + internal-api-harness)."
@echo " make dataconnect-sync - Runs sql:migrate + deploy + sdk:generate in order."
@echo " make dataconnect-bootstrap-db - ONE-TIME: creates krow-sql, krow_db, links Data Connect, deploys, and generates initial SDK."
@echo " make check-gcloud-beta - Validates gcloud + gcloud beta group availability."
@echo ""
@echo " --- BASE44 EXPORT WORKFLOW ---"
@echo " make integrate-export - Integrates a new Base44 export from '../krow-workforce-export-latest'."
@echo " make prepare-export - Prepares a fresh Base44 export for local use."
@echo " make dataconnect-init - Initializes Firebase Data Connect."
@echo " make dataconnect-deploy - Deploys Data Connect schemas."
@echo " make dataconnect-sql-migrate - Applies SQL migrations."
@echo " make dataconnect-generate-sdk - Regenerates the Data Connect SDK."
@echo " make dataconnect-sync - Runs migrate + deploy + generate-sdk."
@echo " make dataconnect-bootstrap-db - ONE-TIME: Full Cloud SQL + Data Connect setup."
@echo ""
@echo " make help - Shows this help message."
@echo "--------------------------------------------------"
# --- Core Development ---
install:
@echo "--> Installing web frontend dependencies..."
@cd frontend-web && npm install
dev:
@echo "--> Ensuring web frontend dependencies are installed..."
@cd frontend-web && npm install
@echo "--> Starting web frontend development server on http://localhost:5173 ..."
@cd frontend-web && npm run dev
build:
@echo "--> Building web frontend for production..."
@cd frontend-web && VITE_APP_ENV=$(ENV) npm run build
launchpad-dev:
@echo "--> Starting local Launchpad server using Firebase Hosting emulator..."
@echo " - Generating secure email hashes..."
@node scripts/generate-allowed-hashes.js
@firebase serve --only hosting:launchpad --project=$(FIREBASE_ALIAS)
# --- Deployment ---
deploy-launchpad-hosting:
@echo "--> Deploying Internal Launchpad to Firebase Hosting..."
@echo " - Generating secure email hashes..."
@node scripts/generate-allowed-hashes.js
@echo " - Target: hosting:launchpad"
@echo " - Project: $(FIREBASE_ALIAS)"
@firebase deploy --only hosting:launchpad --project=$(FIREBASE_ALIAS)
@echo "--> ✅ Deployment to Firebase Hosting successful."
deploy-app: build
@echo "--> Deploying Frontend Web App to [$(ENV)] environment..."
@firebase deploy --only hosting:$(HOSTING_TARGET) --project=$(FIREBASE_ALIAS)
# --- Admin Console ---
admin-install:
@echo "--> Installing admin console dependencies..."
@cd admin-web && npm install
admin-dev:
@echo "--> Starting admin console development server on http://localhost:5174 ..."
@cd admin-web && npm run dev -- --port 5174
admin-build:
@echo "--> Building admin console for production..."
@node scripts/patch-admin-layout-for-env-label.js
@cd admin-web && VITE_APP_ENV=$(ENV) npm run build
# --- API Test Harness ---
harness-install:
@echo "--> Installing API Test Harness dependencies..."
@cd internal-api-harness && npm install
harness-dev: dataconnect-sync
@echo "--> Starting API Test Harness development server on http://localhost:5175 ..."
@cd internal-api-harness && npm run dev -- --port 5175
harness-build:
@echo "--> Building API Test Harness for production..."
@cd internal-api-harness && npm run build -- --mode $(ENV)
harness-deploy: harness-build
@echo "--> Deploying API Test Harness to [$(ENV)] environment..."
@firebase deploy --only hosting:api-harness-$(ENV) --project=$(FIREBASE_ALIAS)
deploy-admin: admin-build
@echo "--> Building and deploying Admin Console to Cloud Run [$(ENV)]..."
@echo " - Step 1: Building container image..."
@cd admin-web && gcloud builds submit \
--tag $(CR_ADMIN_IMAGE_URI) \
--project=$(GCP_PROJECT_ID)
@echo " - Step 2: Deploying to Cloud Run..."
@gcloud run deploy $(CR_ADMIN_SERVICE_NAME) \
--image $(CR_ADMIN_IMAGE_URI) \
--platform managed \
--region $(CR_ADMIN_REGION) \
--no-allow-unauthenticated \
--project=$(GCP_PROJECT_ID)
@echo " - Step 3: Enabling IAP on the service..."
@gcloud beta run services update $(CR_ADMIN_SERVICE_NAME) \
--region=$(CR_ADMIN_REGION) \
--project=$(GCP_PROJECT_ID) \
--iap
@echo "--> ✅ Admin Console deployment to Cloud Run successful."
deploy-admin-full: deploy-admin configure-iap-admin
@echo "✅ Admin Console deployed and IAP configured successfully!"
# --- Frontend Web Free ---
free-dev: dataconnect-sync
@echo "--> Starting free web development server on http://localhost:5174 ..."
@cd frontend-web-free && npm run dev -- --port 5174
# --- Cloud IAP Configuration ---
configure-iap-admin:
@echo "--> Configuring IAP for Cloud Run service [$(CR_ADMIN_SERVICE_NAME)] in [$(ENV)]..."
@echo " - Granting Cloud Run Invoker role to IAP Service Account..."
@gcloud run services add-iam-policy-binding $(CR_ADMIN_SERVICE_NAME) \
--region=$(CR_ADMIN_REGION) \
--project=$(GCP_PROJECT_ID) \
--member=\"serviceAccount:$(IAP_SERVICE_ACCOUNT)\" \
--role='roles/run.invoker' \
--quiet
@echo " - Adding users from iap-users.txt..."
@cd admin-web && \
grep -v '^#' iap-users.txt | grep -v '^$$' | while read -r member; do \
echo " Adding $$member as IAP-secured Web App User..."; \
gcloud beta iap web add-iam-policy-binding \
--project=$(GCP_PROJECT_ID) \
--resource-type=cloud-run \
--service=$(CR_ADMIN_SERVICE_NAME) \
--region=$(CR_ADMIN_REGION) \
--member=\"$$member\" \
--role='roles/iap.httpsResourceAccessor' \
--quiet; \
done
@echo "✅ IAP configuration for Admin Console complete."
list-iap-users:
@echo "--> Current IAP users for Cloud Run service [$(IAP_SERVICE_NAME)]:"
@gcloud beta iap web get-iam-policy \
--project=$(IAP_PROJECT_ID) \
--resource-type=cloud-run \
--service=$(IAP_SERVICE_NAME) \
--region=$(IAP_SERVICE_REGION)
remove-iap-user:
@if [ -z "$(USER)" ]; then \
echo "❌ Error: Please specify USER=user:email@example.com"; \
exit 1; \
fi
@echo "--> Removing IAP access for $(USER) from Cloud Run service [$(IAP_SERVICE_NAME)]..."
@gcloud beta iap web remove-iam-policy-binding \
--project=$(IAP_PROJECT_ID) \
--resource-type=cloud-run \
--service=$(IAP_SERVICE_NAME) \
--region=$(IAP_SERVICE_REGION) \
--member=\"$(USER)\" \
--role='roles/iap.httpsResourceAccessor' \
--quiet
@echo "✅ User removed from IAP."
# --- Project Management ---
setup-labels:
@echo "--> Setting up GitHub labels..."
@./scripts/setup-github-labels.sh
export-issues:
@echo "--> Exporting GitHub issues to documentation..."
@./scripts/export_issues.sh $(ARGS)
create-issues-from-file:
@echo "--> Creating GitHub issues from file..."
@./scripts/create_issues.py $(ARGS)
# --- Development Tools ---
install-git-hooks:
@echo "--> Installing Git hooks..."
@ln -sf ../../scripts/git-hooks/pre-push .git/hooks/pre-push
@echo "✅ pre-push hook installed successfully. Direct pushes to 'main' and 'dev' are now blocked."
# --- Base44 Export Workflow ---
integrate-export:
@echo "--> Integrating new Base44 export into frontend-web/..."
@if [ ! -d "../krow-workforce-export-latest" ]; then \
echo "❌ Error: Export directory '../krow-workforce-export-latest' not found."; \
exit 1; \
fi
@echo " - Creating frontend-web/.local-preserve to preserve local directories (src/dataconnect-generated, src/lib)..."
@mkdir -p frontend-web/.local-preserve
@if [ -d "frontend-web/src/dataconnect-generated" ]; then \
mv frontend-web/src/dataconnect-generated frontend-web/.local-preserve/dataconnect-generated; \
fi
@if [ -d "frontend-web/src/lib" ]; then \
mv frontend-web/src/lib frontend-web/.local-preserve/lib; \
fi
@if [ -d "frontend-web/src/api" ]; then \
mv frontend-web/src/api frontend-web/.local-preserve/api; \
fi
@if [ -f "frontend-web/src/firebase.js" ]; then \
mv frontend-web/src/firebase.js frontend-web/.local-preserve/firebase.js; \
fi
@if [ -d "frontend-web/src/components/auth" ]; then \
mv frontend-web/src/components/auth frontend-web/.local-preserve/components-auth; \
fi
@if [ -f "frontend-web/src/hooks/useAuth.js" ]; then \
mv frontend-web/src/hooks/useAuth.js frontend-web/.local-preserve/useAuth.js; \
fi
@if [ -f "frontend-web/src/pages/Login.jsx" ]; then \
mv frontend-web/src/pages/Login.jsx frontend-web/.local-preserve/Login.jsx; \
fi
@if [ -f "frontend-web/src/pages/Register.jsx" ]; then \
mv frontend-web/src/pages/Register.jsx frontend-web/.local-preserve/Register.jsx; \
fi
@echo " - Removing old src directory..."
@rm -rf frontend-web/src
@echo " - Copying new src directory..."
@cp -R ../krow-workforce-export-latest/src ./frontend-web/src
@echo " - Restoring preserved directories..."
@if [ -d "frontend-web/.local-preserve/dataconnect-generated" ]; then \
rm -rf frontend-web/src/dataconnect-generated; \
mv frontend-web/.local-preserve/dataconnect-generated frontend-web/src/dataconnect-generated; \
fi
@if [ -d "frontend-web/.local-preserve/lib" ]; then \
rm -rf frontend-web/src/lib; \
mv frontend-web/.local-preserve/lib frontend-web/src/lib; \
fi
@if [ -d "frontend-web/.local-preserve/api" ]; then \
rm -rf frontend-web/src/api; \
mv frontend-web/.local-preserve/api frontend-web/src/api; \
fi
@if [ -f "frontend-web/.local-preserve/firebase.js" ]; then \
mv frontend-web/.local-preserve/firebase.js frontend-web/src/firebase.js; \
fi
@if [ -d "frontend-web/.local-preserve/components-auth" ]; then \
mkdir -p frontend-web/src/components; \
rm -rf frontend-web/src/components/auth; \
mv frontend-web/.local-preserve/components-auth frontend-web/src/components/auth; \
fi
@if [ -f "frontend-web/.local-preserve/useAuth.js" ]; then \
mkdir -p frontend-web/src/hooks; \
rm -f frontend-web/src/hooks/useAuth.js; \
mv frontend-web/.local-preserve/useAuth.js frontend-web/src/hooks/useAuth.js; \
fi
@if [ -f "frontend-web/.local-preserve/Login.jsx" ]; then \
mkdir -p frontend-web/src/pages; \
rm -f frontend-web/src/pages/Login.jsx; \
mv frontend-web/.local-preserve/Login.jsx frontend-web/src/pages/Login.jsx; \
fi
@if [ -f "frontend-web/.local-preserve/Register.jsx" ]; then \
mkdir -p frontend-web/src/pages; \
rm -f frontend-web/src/pages/Register.jsx; \
mv frontend-web/.local-preserve/Register.jsx frontend-web/src/pages/Register.jsx; \
fi
@echo " - Deleting frontend-web/.local-preserve..."
@rm -rf frontend-web/.local-preserve
@echo " - Copying new index.html..."
@cp ../krow-workforce-export-latest/index.html ./frontend-web/index.html
@echo " - Patching base44Client.js for local development..."
@node scripts/patch-base44-client.js
@echo " - Patching queryKey in Layout.jsx for local development..."
@node scripts/patch-layout-query-key.js
@echo " - Patching Dashboard.jsx for environment label..."
@node scripts/patch-dashboard-for-env-label.js
@echo " - Patching index.html for title..."
@node scripts/patch-index-html.js
@echo "--> Integration complete. Next step: 'make prepare-export'."
prepare-export:
@echo "--> Preparing fresh Base44 export for local development..."
@node scripts/prepare-export.js
@echo "--> Preparation complete. You can now run 'make dev'."
# --- Data Connect / Backend ---
# Enable all required APIs for Firebase Data Connect + Cloud SQL
dataconnect-enable-apis:
@echo "--> Enabling Firebase & Data Connect APIs on project [$(GCP_PROJECT_ID)]..."
@gcloud services enable firebase.googleapis.com --project=$(GCP_PROJECT_ID)
@gcloud services enable firebasedataconnect.googleapis.com --project=$(GCP_PROJECT_ID)
@gcloud services enable sqladmin.googleapis.com --project=$(GCP_PROJECT_ID)
@gcloud services enable iam.googleapis.com --project=$(GCP_PROJECT_ID)
@gcloud services enable cloudresourcemanager.googleapis.com --project=$(GCP_PROJECT_ID)
@gcloud services enable secretmanager.googleapis.com --project=$(GCP_PROJECT_ID)
@echo "✅ APIs enabled for project [$(GCP_PROJECT_ID)]."
# Initialize Firebase Data Connect (interactive wizard).
# This wraps the command so we remember how to run it for dev/staging/prod.
dataconnect-init:
@echo "--> Initializing Firebase Data Connect for alias [$(FIREBASE_ALIAS)] (project: $(GCP_PROJECT_ID))..."
@firebase init dataconnect --project $(FIREBASE_ALIAS)
@echo "✅ Data Connect initialization command executed. Follow the interactive steps in the CLI."
# Deploy Data Connect schemas (GraphQL → Cloud SQL)
dataconnect-deploy:
@echo "--> Deploying Firebase Data Connect schemas to [$(ENV)] (project: $(FIREBASE_ALIAS))..."
@firebase deploy --only dataconnect --project=$(FIREBASE_ALIAS)
@echo "✅ Data Connect deployment completed for [$(ENV)]."
# Apply pending SQL migrations for Firebase Data Connect
dataconnect-sql-migrate:
@echo "--> Applying Firebase Data Connect SQL migrations to [$(ENV)] (project: $(FIREBASE_ALIAS))..."
@firebase dataconnect:sql:migrate --project=$(FIREBASE_ALIAS)
@echo "✅ Data Connect SQL migration completed for [$(ENV)]."
# Generate Data Connect client SDK for frontend-web and internal-api-harness
dataconnect-generate-sdk:
@echo "--> Generating Firebase Data Connect SDK for web frontend and API harness..."
@firebase dataconnect:sdk:generate --project=$(FIREBASE_ALIAS)
@echo "✅ Data Connect SDK generation completed for [$(ENV)]."
# Unified backend schema update workflow (schema -> deploy -> SDK)
dataconnect-sync:
@echo "--> [1/3] Applying SQL migrations..."
@firebase dataconnect:sql:migrate --project=$(FIREBASE_ALIAS)
@echo "--> [2/3] Deploying Data Connect..."
@firebase deploy --only dataconnect --project=$(FIREBASE_ALIAS)
@echo "--> [3/3] Regenerating SDK..."
@firebase dataconnect:sdk:generate --project=$(FIREBASE_ALIAS)
@echo "✅ Data Connect SQL, deploy, and SDK generation completed for [$(ENV)]."
# -------------------------------------------------------------------
# ONE-TIME FULL SETUP FOR CLOUD SQL + DATA CONNECT
# -------------------------------------------------------------------
# ⚠️ WARNING:
# This should only be executed when setting up a brand new environment
# (e.g., first-time dev setup or new GCP project).
# -------------------------------------------------------------------
# Comprueba que gcloud y el grupo beta están disponibles
check-gcloud-beta:
@command -v gcloud >/dev/null 2>&1 || { \
echo "❌ gcloud CLI not found. Please install it: https://cloud.google.com/sdk/docs/install"; \
exit 1; \
}
@gcloud beta --help >/dev/null 2>&1 || { \
echo "❌ 'gcloud beta' is not available. Run 'gcloud components update' or reinstall the SDK."; \
exit 1; \
}
@echo "✅ gcloud CLI and 'gcloud beta' are available."
dataconnect-bootstrap-db: check-gcloud-beta
@echo "🔍 Checking if Cloud SQL instance krow-sql already exists in [$(GCP_PROJECT_ID)]..."
@if gcloud sql instances describe krow-sql --project=$(GCP_PROJECT_ID) >/dev/null 2>&1; then \
echo "⚠️ Cloud SQL instance 'krow-sql' already exists in project $(GCP_PROJECT_ID)."; \
echo " If you really need to recreate it, delete the instance manually first."; \
exit 1; \
fi
@echo "⚠️ Creating Cloud SQL instance krow-sql (tier: $(SQL_TIER)) (ONLY RUN THIS ONCE PER PROJECT)..."
gcloud sql instances create krow-sql \
--database-version=POSTGRES_15 \
--tier=$(SQL_TIER) \
--region=us-central1 \
--storage-size=10 \
--storage-auto-increase \
--availability-type=zonal \
--backup-start-time=03:00 \
--project=$(GCP_PROJECT_ID)
@echo "⚠️ Creating Cloud SQL database krow_db..."
gcloud sql databases create krow_db \
--instance=krow-sql \
--project=$(GCP_PROJECT_ID)
@echo "⚠️ Creating Firebase Data Connect service identity..."
gcloud beta services identity create \
--service=firebasedataconnect.googleapis.com \
--project=$(GCP_PROJECT_ID)
@echo "⚠️ Enabling IAM authentication on Cloud SQL instance krow-sql..."
gcloud sql instances patch krow-sql \
--project=$(GCP_PROJECT_ID) \
--database-flags=cloudsql.iam_authentication=on \
--quiet
@echo "⚠️ Linking Data Connect service (krow-workforce-db) with Cloud SQL..."
firebase dataconnect:sql:setup krow-workforce-db --project=$(FIREBASE_ALIAS)
@echo "⚠️ Deploying initial Data Connect configuration..."
@firebase deploy --only dataconnect --project=$(FIREBASE_ALIAS)
@echo "⚠️ Generating initial Data Connect SDK..."
@firebase dataconnect:sdk:generate --project=$(FIREBASE_ALIAS)
@echo "🎉 Cloud SQL + Data Connect bootstrap completed successfully!"
# --- Mobile App Development ---
FLAVOR :=
ifeq ($(ENV),dev)
FLAVOR := dev
else ifeq ($(ENV),staging)
FLAVOR := staging
else ifeq ($(ENV),prod)
FLAVOR := production
endif
BUILD_TYPE ?= appbundle
mobile-client-install:
@echo "--> Installing Flutter dependencies for client app..."
@cd mobile-apps/client-app && $(FLUTTER) pub get
mobile-client-dev:
@echo "--> Running client app in dev mode..."
@echo "--> If using VS code, use the debug configurations"
@cd mobile-apps/client-app && $(FLUTTER) run --flavor dev -t lib/main_dev.dart
mobile-client-build:
@if [ "$(ENV)" != "dev" ] && [ "$(ENV)" != "staging" ] && [ "$(ENV)" != "prod" ]; then \
echo "ERROR: ENV must be one of dev, staging, or prod."; exit 1; \
fi
@if [ "$(PLATFORM)" != "android" ] && [ "$(PLATFORM)" != "ios" ]; then \
echo "ERROR: PLATFORM must be either android or ios."; exit 1; \
fi
@echo "--> Building client app for $(PLATFORM) with flavor $(FLAVOR)..."
@cd mobile-apps/client-app && \
$(FLUTTER) pub get && \
$(FLUTTER) pub run build_runner build --delete-conflicting-outputs && \
if [ "$(PLATFORM)" = "android" ]; then \
$(FLUTTER) build $(BUILD_TYPE) --flavor $(FLAVOR); \
elif [ "$(PLATFORM)" = "ios" ]; then \
$(FLUTTER) build ipa --flavor $(FLAVOR); \
fi
mobile-staff-install:
@echo "--> Installing Flutter dependencies for staff app..."
@cd mobile-apps/staff-app && $(FLUTTER) pub get
mobile-staff-dev:
@echo "--> Running staff app in dev mode..."
@echo "--> If using VS code, use the debug configurations"
@cd mobile-apps/staff-app && $(FLUTTER) run --flavor dev -t lib/main_dev.dart
mobile-staff-build:
@if [ "$(ENV)" != "dev" ] && [ "$(ENV)" != "staging" ] && [ "$(ENV)" != "prod" ]; then \
echo "ERROR: ENV must be one of dev, staging, or prod."; exit 1; \
fi
@if [ "$(PLATFORM)" != "android" ] && [ "$(PLATFORM)" != "ios" ]; then \
echo "ERROR: PLATFORM must be either android or ios."; exit 1; \
fi
@echo "--> Building staff app for $(PLATFORM) with flavor $(FLAVOR)..."
@cd mobile-apps/staff-app && \
$(FLUTTER) pub get && \
$(FLUTTER) pub run build_runner build --delete-conflicting-outputs && \
if [ "$(PLATFORM)" = "android" ]; then \
$(FLUTTER) build $(BUILD_TYPE) --flavor $(FLAVOR); \
elif [ "$(PLATFORM)" = "ios" ]; then \
$(FLUTTER) build ipa --flavor $(FLAVOR); \
fi
@echo "--------------------------------------------------"

112
README.md
View File

@@ -1,81 +1,71 @@
# Krow Workforce Management Platform
# KROW Workforce Monorepo
Krow is a comprehensive monorepo platform designed to streamline workforce management for events, hospitality, and large-scale enterprise operations. It connects clients, operators, vendors, and staff in a unified ecosystem, leveraging modern technology to optimize every step from procurement to payroll.
KROW is a comprehensive workforce management platform designed to streamline operations for events, hospitality, and enterprise staffing. This monorepo contains all components of the ecosystem, from the data layer to the user-facing applications.
## 🚀 What's in this Monorepo?
## 🚀 Repository Structure
- **/firebase/**: This directory is the backbone of our backend infrastructure. It contains:
- **Firestore Rules (`firestore.rules`):** Defines the security and access control logic for our NoSQL database, ensuring that users can only read and write data they are authorized to access.
- **Cloud Storage Rules (`storage.rules`):** Secures file uploads and downloads (like compliance documents and user avatars).
- **Data Connect (`dataconnect/`):** Houses the configuration for Firebase Data Connect, including the GraphQL schema, connectors, queries, and mutations that define our API layer.
### 📦 Apps (`/apps`)
These are the production-ready applications for our users:
- **`web-dashboard/`**: The primary React/Vite dashboard for Admin, Vendors, and Clients.
- **`mobile-client/`**: Flutter application for final clients to manage orders and billing.
- **`mobile-staff/`**: Flutter application for staff members (scheduling, clock-in/out, earnings).
- **/admin-web/**: The central "mission control" for platform administrators. This React/Vite web application provides a secure interface for:
- **User Management:** Inviting new users (the very first client or operator) and managing roles/permissions across the entire ecosystem.
- **Platform Analytics:** Monitoring application usage, top pages, and user activity.
- **System Logs:** A log explorer for diagnosing issues and monitoring the platform's health.
### ⚙️ Backend (`/backend`)
The core data engine powering all applications:
- **`dataconnect/`**: Firebase Data Connect configuration, GraphQL schemas (PostgreSQL), and auto-generated SDKs.
- **/frontend-web/**: The primary web application for core business operations, used by procurement managers, operators, and clients. This React/Vite application includes modules for:
- **Vendor Management:** Onboarding, compliance tracking, and performance scorecards.
- **Event & Order Management:** Creating and managing staffing orders.
- **Invoicing & Payroll:** Financial workflows for clients and staff.
- **Dashboards:** Role-specific dashboards for different user types.
### 🛠️ Internal (`/internal`)
Tools and resources for the development and operations team:
- **`launchpad/`**: A secure portal (DevOps Launchpad) to access internal resources, documentation, and infrastructure links.
- **`api-harness/`**: A technical tool for testing and validating the Data Connect API and Cloud Functions.
- **`prototypes/`**: Reference code and visual prototypes (synchronized from external sources).
- **/mobile-apps/**: This directory is planned for our future mobile applications. It is structured to contain two distinct apps:
- `client-app`: A dedicated application for clients to create orders, track events, and manage billing on the go.
- `staff-app`: An essential tool for workforce members to view schedules, clock in/out, manage their profiles, and track earnings.
- **/docs/**: The single source of truth for all project documentation. This includes:
- **Vision & Roadmaps:** High-level strategy, product goals, and technical direction.
- **Architecture:** Detailed diagrams and explanations of the system architecture.
- **API Specifications:** Documentation for our GraphQL API.
- **Development Guides:** Conventions, setup instructions, and maintenance procedures.
- **/scripts/**: A collection of automation scripts to streamline development and operational tasks. These scripts are primarily executed via the `Makefile` and handle tasks like database patching, environment setup, and code generation.
- **/secrets/**: A Git-ignored directory for storing sensitive credentials, API keys, and environment-specific configuration files. This ensures that no confidential information is ever committed to the repository.
## 📚 Documentation Overview
This section provides a quick guide to the most important documentation files in this monorepo, ordered by logical reading flow:
- **[00-vision.md](./docs/00-vision.md)**: The "Why" behind the KROW platform, outlining our core objectives and guiding principles.
- **[01-product-functional-roadmap.md](./docs/01-product-functional-roadmap.md)**: The "What" we are building, from a user-facing features perspective.
- **[02-architecture-overview.md](./docs/02-architecture-overview.md)**: Visual diagrams explaining the technical architecture of our web and mobile applications.
- **[03-backend-api-specification.md](./docs/03-backend-api-specification.md)**: The detailed technical specification for our custom backend API.
- **[04-strategy-technical-roadmap.md](./docs/04-strategy-technical-roadmap.md)**: Our strategic plan for building the platform, outlining phases and milestones.
- **[05-project-plan.md](./docs/05-project-plan.md)**: A detailed breakdown of tasks by milestone, ready for GitHub Issues.
- **[06-maintenance-guide.md](./docs/06-maintenance-guide.md)**: The operational manual for integrating updates from the Base44 visual builder.
- **[07-reference-base44-api-export.md](./docs/07-reference-base44-api-export.md)**: The raw API documentation exported from Base44, used as a reference.
- **[08-reference-base44-prompts.md](./docs/08-reference-base44-prompts.md)**: A collection of standardized prompts for interacting with the Base44 AI.
## 🤝 Contributing
New to the KROW team? Start here to set up your environment and understand our development practices: **[CONTRIBUTING.md](./CONTRIBUTING.md)**
### 📂 Support Directories
- **`/docs`**: Project vision, technical specifications, and guides.
- **`/makefiles`**: Modularized `Makefile` logic for project automation.
- **`/scripts`**: Automation scripts (security, hachage, environment setup).
- **`/firebase`**: Global Firebase configuration (Firestore/Storage rules).
## 🛠️ Tech Stack
- **Frontend:** React with Vite (JavaScript)
- **Styling:** Tailwind CSS
- **Backend:** Firebase (Firestore, Cloud Storage, Authentication, Data Connect)
- **Mobile:** Flutter (planned)
- **Frontend:** React (Vite)
- **Mobile:** Flutter
- **Backend:** Firebase (Data Connect, Auth, Hosting, Functions)
- **Database:** PostgreSQL (managed via Cloud SQL & Data Connect)
- **Infrastructure:** Google Cloud Platform (GCP)
## 📦 Getting Started
1. **Clone the repository:**
This project uses a modular `Makefile` for all common tasks.
1. **View available commands:**
```bash
git clone https://github.com/Oloodi/krow.git
cd krow
make help
```
2. **Install dependencies for the main web app:**
2. **Install dependencies (Web):**
```bash
cd frontend-web
npm install
make install
```
3. **Run the development server:**
3. **Run the Web Dashboard locally:**
```bash
npm run dev
make dev
```
The main application will be available at `http://localhost:5173`. For other packages, refer to their respective `README.md` files.
4. **Run the DevOps Launchpad locally:**
```bash
make launchpad-dev
```
## 📚 Documentation
- **[00-vision.md](./docs/00-vision.md)**: Project objectives and guiding principles.
- **[01-backend-api-specification.md](./docs/01-backend-api-specification.md)**: (Legacy) Reference for data schemas.
- **[02-codemagic-env-vars.md](./docs/02-codemagic-env-vars.md)**: Guide for CI/CD environment variables.
- **[03-contributing.md](./docs/03-contributing.md)**: Guidelines for new developers and setup checklist.
- **[04-sync-prototypes.md](./docs/04-sync-prototypes.md)**: How to sync prototypes for local dev and AI context.
## 🤝 Contributing
New to the team? Please read our **[Contributing Guide](./docs/03-contributing.md)** to get your environment set up and understand our workflow.
---
© 2026 KROW Workforce / Oloodi Technologies Inc.

View File

@@ -1,17 +0,0 @@
# This file specifies files that are *not* uploaded to Google Cloud
# using gcloud. It follows the same syntax as .gitignore, with the addition of
# "#!include" directives (which insert the entries of the given .gitignore-style
# file at that point).
#
# For more information, run:
# $ gcloud topic gcloudignore
#
.gcloudignore
# If you would like to upload your .git directory, .gitignore file or files
# from your .gitignore file, remove the corresponding line
# below:
.git
.gitignore
# Node.js dependencies:
node_modules/

24
admin-web/.gitignore vendored
View File

@@ -1,24 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -1,29 +0,0 @@
# STAGE 1: Build the React application
FROM node:20-alpine AS build
WORKDIR /app
# Copy package files and install dependencies
COPY package.json package-lock.json ./
RUN npm install
# Copy the rest of the application source code
COPY . .
# Build the application for production
RUN npm run build
# STAGE 2: Serve the static files with Nginx
FROM nginx:alpine
# Copy the built files from the build stage
COPY --from=build /app/dist /usr/share/nginx/html
# Copy the custom Nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose the port Nginx is listening on
EXPOSE 8080
# Command to run Nginx in the foreground
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -1,35 +0,0 @@
# Krow Admin Console
This is the central administration web application for the Krow platform, built with React and Vite.
## Overview
The Krow Admin Console provides a secure and centralized interface for platform administrators to perform high-level management tasks. Its primary functions are:
- **User Ecosystem Management:** Invite, view, and manage access for all users across the platform (Clients, Operators, Vendors, etc.).
- **Platform Health Monitoring:** Get a high-level overview of system activity, usage, and performance.
- **Diagnostics and Troubleshooting:** Access system logs for maintenance and issue resolution.
## Features
- **Dashboard:** A central hub displaying key platform metrics like total users, active vendors, and pending invitations.
- **User Management:** A complete interface to invite new users and manage existing ones, including role assignments.
- **Analytics:** A view of application usage statistics, such as top users and most visited pages.
- **Logs Explorer:** A simple tool to monitor and filter system logs for diagnostics.
## Getting Started
1. Navigate to the `admin-web` directory:
```bash
cd admin-web
```
2. Install the dependencies:
```bash
npm install
```
3. Start the development server:
```bash
npm run dev
```
The application will be available at `http://localhost:5173/admin-web/`.

View File

@@ -1,21 +0,0 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": false,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@@ -1,8 +0,0 @@
# List of authorized users for the Admin Console
# Format: one email per line, lines starting with # are comments
#
# IMPORTANT: These users must belong to the 'krowwithus.com' organization.
# This is a known limitation of enabling IAP directly on Cloud Run.
# See: https://docs.cloud.google.com/run/docs/securing/identity-aware-proxy-cloud-run#known_limitations
user:admin@krowwithus.com

View File

@@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>KROW Admin Console</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

View File

@@ -1,10 +0,0 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"jsx": "react-jsx"
},
"include": ["src/**/*.js", "src/**/*.jsx"]
}

View File

@@ -1,13 +0,0 @@
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ /index.html;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +0,0 @@
{
"name": "admin-web",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-slot": "^1.2.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.553.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.9.6",
"tailwind-merge": "^3.4.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@vitejs/plugin-react": "^5.1.0",
"autoprefixer": "^10.4.22",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.18",
"vite": "^7.2.2"
}
}

View File

@@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,24 +0,0 @@
import React from "react";
import Layout from "./pages/Layout";
import Dashboard from "./pages/Dashboard";
import UserManagement from "./pages/UserManagement";
import Analytics from "./pages/Analytics";
import Logs from "./pages/Logs";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
function App() {
return (
<Router>
<Layout>
<Routes>
<Route path="/admin-web/" element={<Dashboard />} />
<Route path="/admin-web/user-management" element={<UserManagement />} />
<Route path="/admin-web/analytics" element={<Analytics />} />
<Route path="/admin-web/logs" element={<Logs />} />
</Routes>
</Layout>
</Router>
);
}
export default App;

View File

@@ -1,58 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View File

@@ -1,6 +0,0 @@
import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs) {
return twMerge(clsx(inputs))
}

View File

@@ -1,10 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)

View File

@@ -1,61 +0,0 @@
import React from "react";
const topUsers = [
{ email: "ian.gomez@example.com", visits: 6950 },
{ email: "admin@krow.com", visits: 193 },
{ email: "paula.orte@example.com", visits: 81 },
{ email: "test.user@example.com", visits: 50 },
];
const topPages = [
{ name: "VendorManagement", visits: 718 },
{ name: "Dashboard", visits: 600 },
{ name: "unknown", visits: 587 },
{ name: "ClientDashboard", visits: 475 },
{ name: "Events", visits: 456 },
];
export default function Analytics() {
return (
<div>
<h1 className="text-3xl font-bold text-slate-800">Analytics</h1>
<p className="mt-2 text-base text-slate-600">
An overview of your application's data.
</p>
<div className="mt-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-white shadow-lg rounded-2xl p-6">
<h3 className="text-lg font-medium text-gray-900">Total Unique Users</h3>
<p className="mt-2 text-4xl font-bold text-blue-600">3</p>
</div>
</div>
</div>
<div className="mt-8 grid grid-cols-1 md:grid-cols-2 gap-8">
<div>
<h3 className="text-xl font-semibold text-gray-900">Top Users</h3>
<ul className="mt-4 space-y-4">
{topUsers.map((user) => (
<li key={user.email} className="bg-white shadow-lg rounded-2xl p-4">
<p className="font-semibold text-slate-800">{user.email}</p>
<p className="text-slate-500">{user.visits.toLocaleString()} visits</p>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-xl font-semibold text-gray-900">Top Pages</h3>
<ul className="mt-4 space-y-4">
{topPages.map((page) => (
<li key={page.name} className="bg-white shadow-lg rounded-2xl p-4 flex justify-between items-center">
<p className="font-semibold text-slate-800">{page.name}</p>
<p className="text-slate-500 font-medium">{page.visits.toLocaleString()}</p>
</li>
))}
</ul>
</div>
</div>
</div>
);
}

View File

@@ -1,38 +0,0 @@
import React from "react";
import { Users, Building2, Mail, FileText } from "lucide-react";
const stats = [
{ name: "Total Users", stat: "15", icon: Users, color: "bg-blue-500" },
{ name: "Active Vendors", stat: "8", icon: Building2, color: "bg-green-500" },
{ name: "Pending Invitations", stat: "3", icon: Mail, color: "bg-yellow-500" },
{ name: "Open Orders", stat: "12", icon: FileText, color: "bg-purple-500" },
];
export default function Dashboard() {
return (
<div>
<h1 className="text-3xl font-bold text-slate-800">Admin Dashboard</h1>
<p className="mt-2 text-base text-slate-600">
Welcome to the Krow Admin Console. Here is a quick overview of the platform.
</p>
<div className="mt-8">
<dl className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
{stats.map((item) => (
<div key={item.name} className="relative overflow-hidden rounded-2xl bg-white px-4 py-5 shadow-lg sm:px-6 sm:py-6">
<dt>
<div className={`absolute rounded-xl p-3 ${item.color}`}>
<item.icon className="h-8 w-8 text-white" aria-hidden="true" />
</div>
<p className="ml-20 truncate text-sm font-medium text-gray-500">{item.name}</p>
</dt>
<dd className="ml-20 flex items-baseline">
<p className="text-3xl font-semibold text-gray-900">{item.stat}</p>
</dd>
</div>
))}
</dl>
</div>
</div>
);
}

View File

@@ -1,88 +0,0 @@
import React from "react";
import { Link, useLocation } from "react-router-dom";
import { Building2, Users, BarChart3, Terminal, LayoutDashboard, LogOut } from "lucide-react";
const navigation = [
{ name: "Dashboard", href: "/admin-web/", icon: LayoutDashboard },
{ name: "User Management", href: "/admin-web/user-management", icon: Users },
{ name: "Analytics", href: "/admin-web/analytics", icon: BarChart3 },
{ name: "Logs Explorer", href: "/admin-web/logs", icon: Terminal },
];
export default function Layout({ children }) {
const location = useLocation();
return (
<div className="flex h-screen bg-slate-50">
{/* Sidebar */}
<div className="hidden md:flex md:flex-shrink-0">
<div className="flex flex-col w-64">
{/* Sidebar header */}
<div className="flex items-center h-16 flex-shrink-0 px-4 bg-white border-b border-slate-200">
<div className="flex items-center space-x-3">
<div className="w-8 h-8 rounded-lg flex items-center justify-center">
<img src="/logo.svg" alt="Krow Logo" className="h-full w-full" />
</div>
<div>
<h1 className="text-lg font-bold text-slate-800">KROW</h1>
<p className="text-xs text-slate-500">Admin Console</p>
</div>
</div>
{import.meta.env.VITE_APP_ENV && (
<span className={`ml-auto inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
import.meta.env.VITE_APP_ENV === 'staging' ? 'bg-amber-100 text-amber-800' : 'bg-blue-100 text-blue-800'
}`}>
{import.meta.env.VITE_APP_ENV === 'staging' ? 'Staging' : 'Dev'}
</span>
)}
</div>
{/* Sidebar content */}
<div className="flex-1 flex flex-col overflow-y-auto bg-white">
<nav className="flex-1 px-4 py-4">
{navigation.map((item) => (
<Link
key={item.name}
to={item.href}
className={`
${
location.pathname === item.href
? "bg-blue-50 text-blue-600"
: "text-slate-600 hover:bg-slate-100 hover:text-slate-900"
}
group flex items-center px-3 py-3 text-sm font-medium rounded-lg
`}
>
<item.icon
className="mr-3 h-6 w-6"
aria-hidden="true"
/>
{item.name}
</Link>
))}
</nav>
<div className="px-4 py-4 border-t border-slate-200">
<Link
to="#"
className="group flex items-center px-3 py-3 text-sm font-medium rounded-lg text-slate-600 hover:bg-slate-100 hover:text-slate-900"
>
<LogOut className="mr-3 h-6 w-6" />
Logout
</Link>
</div>
</div>
</div>
</div>
{/* Main content */}
<div className="flex flex-col w-0 flex-1 overflow-hidden">
<main className="flex-1 relative z-0 overflow-y-auto focus:outline-none">
<div className="py-8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
{children}
</div>
</div>
</main>
</div>
</div>
);
}

View File

@@ -1,54 +0,0 @@
import React from "react";
const logs = [
{ level: "INFO", message: "User admin@krow.com logged in.", timestamp: "2024-05-21T10:00:00Z" },
{ level: "WARN", message: "API response time is slow: 2500ms for /api/vendors", timestamp: "2024-05-21T10:01:00Z" },
{ level: "ERROR", message: "Failed to process payment for invoice INV-12345.", timestamp: "2024-05-21T10:02:00Z" },
{ level: "INFO", message: "New vendor 'New Staffing Co' invited by admin@krow.com.", timestamp: "2024-05-21T10:05:00Z" },
];
export default function Logs() {
return (
<div>
<h1 className="text-3xl font-bold text-slate-800">Logs Explorer</h1>
<p className="mt-2 text-base text-slate-600">
Monitor system logs across your application.
</p>
<div className="mt-8 flow-root">
<div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-2xl">
<table className="min-w-full divide-y divide-gray-300">
<thead className="bg-slate-50">
<tr>
<th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Level</th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Message</th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Timestamp</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 bg-white">
{logs.map((log, index) => (
<tr key={index}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
<span className={`inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${
log.level === 'ERROR' ? 'bg-red-100 text-red-800' :
log.level === 'WARN' ? 'bg-yellow-100 text-yellow-800' :
'bg-green-100 text-green-800'
}`}>
{log.level}
</span>
</td>
<td className="px-3 py-4 text-sm text-gray-500 font-mono">{log.message}</td>
<td className="px-3 py-4 text-sm text-gray-500">{new Date(log.timestamp).toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,146 +0,0 @@
import React, { useState } from "react";
import { Plus, Send } from "lucide-react";
const users = [
{ name: "Alex Johnson", email: "alex.j@krow.com", role: "Admin", status: "Active", avatar: "https://randomuser.me/api/portraits/men/1.jpg" },
{ name: "Samantha Lee", email: "samantha.lee@vendor.com", role: "Vendor", status: "Active", avatar: "https://randomuser.me/api/portraits/women/2.jpg" },
{ name: "Michael Chen", email: "michael.chen@client.com", role: "Client", status: "Pending", avatar: "https://randomuser.me/api/portraits/men/3.jpg" },
];
function InviteUserModal({ isOpen, onClose }) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity">
<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div className="relative transform overflow-hidden rounded-2xl bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<h3 className="text-xl font-semibold leading-6 text-slate-900">Send Invitation</h3>
<p className="mt-2 text-sm text-slate-500">The user will receive an email with a link to set up their account.</p>
<div className="mt-6 space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email address
</label>
<input
type="email"
name="email"
id="email"
className="mt-2 block w-full rounded-lg border-0 py-2.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
placeholder="colleague@company.com"
/>
</div>
<div>
<label htmlFor="role" className="block text-sm font-medium text-gray-700">
Access level
</label>
<select
id="role"
name="role"
className="mt-2 block w-full rounded-lg border-0 py-2.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-blue-600 sm:text-sm sm:leading-6"
defaultValue="User"
>
<option>Admin</option>
<option>Vendor</option>
<option>Client</option>
<option>User</option>
</select>
</div>
</div>
</div>
<div className="bg-slate-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
<button
type="button"
className="inline-flex w-full items-center justify-center rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 sm:ml-3 sm:w-auto"
onClick={onClose}
>
<Send className="h-5 w-5 mr-2" />
Send Invitation
</button>
<button
type="button"
className="mt-3 inline-flex w-full justify-center rounded-lg bg-white px-4 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
onClick={onClose}
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
);
}
export default function UserManagement() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<div className="sm:flex sm:items-center">
<div className="sm:flex-auto">
<h1 className="text-3xl font-bold text-slate-800">User Management</h1>
<p className="mt-2 text-base text-slate-600">
A list of all the users in your account including their name, email and role.
</p>
</div>
<div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<button
type="button"
className="inline-flex items-center justify-center rounded-lg border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 sm:w-auto"
onClick={() => setIsModalOpen(true)}
>
<Plus className="h-5 w-5 mr-2" />
Invite User
</button>
</div>
</div>
<div className="mt-8 flow-root">
<div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-2xl">
<table className="min-w-full divide-y divide-gray-300">
<thead className="bg-slate-50">
<tr>
<th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Name</th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Email</th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Role</th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 bg-white">
{users.map((user) => (
<tr key={user.email}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<div className="flex items-center">
<div className="h-11 w-11 flex-shrink-0">
<img className="h-11 w-11 rounded-full" src={user.avatar} alt="" />
</div>
<div className="ml-4">
<div className="font-medium text-gray-900">{user.name}</div>
</div>
</div>
</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.email}</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.role}</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
user.status === 'Active' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'
}`}>
{user.status}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
<InviteUserModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} />
</div>
);
}

View File

@@ -1,89 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: ["./index.html", "./src/**/*.{ts,tsx,js,jsx}"],
theme: {
extend: {
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
},
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))'
}
},
keyframes: {
'accordion-down': {
from: {
height: '0'
},
to: {
height: 'var(--radix-accordion-content-height)'
}
},
'accordion-up': {
from: {
height: 'var(--radix-accordion-content-height)'
},
to: {
height: '0'
}
}
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out'
}
}
},
plugins: [require("tailwindcss-animate")],
}

View File

@@ -1,13 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})

1
apps/mobile-staff/.keep Normal file
View File

@@ -0,0 +1 @@

View File

@@ -1,141 +0,0 @@
plugins {
id "com.android.application"
// START: FlutterFire Configuration
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
// END: FlutterFire Configuration
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
android {
namespace = "com.example.troywallet"
compileSdk = 36
ndkVersion = "25.1.8937393"
//ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.troywallet"
namespace("com.example.troywallet")
minSdk = 29
targetSdk = 35
versionCode = flutter.versionCode
versionName = flutter.versionName
multiDexEnabled true
}
// KeyStore //////////////////////////////////////////////////
// - kast.keystore..alias android..pw=android..pw=android
def keystorePropertiesFileNameDev = 'kast_key_dev.properties'
def keystorePropertiesDev = new Properties()
keystorePropertiesDev.load(new FileInputStream(rootProject.file(keystorePropertiesFileNameDev)))
def keystorePropertiesFileNameUat = 'kast_key_uat.properties'
def keystorePropertiesUat = new Properties()
keystorePropertiesUat.load(new FileInputStream(rootProject.file(keystorePropertiesFileNameUat)))
def keystorePropertiesFileNameStaging = 'kast_key_staging.properties'
def keystorePropertiesStaging = new Properties()
keystorePropertiesStaging.load(new FileInputStream(rootProject.file(keystorePropertiesFileNameStaging)))
def keystorePropertiesFileNameProd = 'kast_key_prod.properties'
def keystorePropertiesProd = new Properties()
keystorePropertiesProd.load(new FileInputStream(rootProject.file(keystorePropertiesFileNameProd)))
signingConfigs {
configDev {
keyAlias keystorePropertiesDev['keyAlias']
keyPassword keystorePropertiesDev['keyPassword']
storeFile file('../kast_android_dev.jks') //keystorePropertiesSit['storeFile']
storePassword keystorePropertiesDev['storePassword']
}
configUat {
keyAlias keystorePropertiesUat['keyAlias']
keyPassword keystorePropertiesUat['keyPassword']
storeFile file('../kast_android_uat.jks') //keystorePropertiesPat['storeFile']
storePassword keystorePropertiesUat['storePassword']
}
configStaging {
keyAlias keystorePropertiesStaging['keyAlias']
keyPassword keystorePropertiesStaging['keyPassword']
storeFile file('../kast_android_staging.jks') //keystorePropertiesPat['storeFile']
storePassword keystorePropertiesStaging['storePassword']
}
configProd {
keyAlias keystorePropertiesProd['keyAlias']
keyPassword keystorePropertiesProd['keyPassword']
storeFile file('../kast_android_prod.jks')
storePassword keystorePropertiesProd['storePassword']
}
}
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
applicationId "dev.kastcard.com"
resValue "string", "app_name", "KAST DEV"
resValue "string", "deeplink_prefix", "/dls"
resValue "drawable", "ic_launcher", "@mipmap/ic_launcher_dev"
signingConfig signingConfigs.configDev
}
uat {
dimension "default"
applicationId "uat.kastcard.com"
resValue "string", "app_name", "KAST UAT"
resValue "string", "deeplink_prefix", "/dlp"
resValue "drawable", "ic_launcher", "@mipmap/ic_launcher_uat"
signingConfig signingConfigs.configUat
}
staging {
dimension "default"
applicationId "stg.kastcard.com"
resValue "string", "app_name", "KAST Staging"
resValue "string", "deeplink_prefix", "/dlp"
resValue "drawable", "ic_launcher", "@mipmap/ic_launcher_staging"
signingConfig signingConfigs.configStaging
}
prod {
dimension "default"
applicationId "com.kastfinance.app"
resValue "string", "app_name", "KAST"
resValue "string", "deeplink_prefix", "/dlp"
resValue "drawable", "ic_launcher", "@mipmap/ic_launcher_prod"
signingConfig signingConfigs.configProd
}
}
buildTypes {
release {
shrinkResources false
minifyEnabled false
}
}
}
flutter {
source = "../.."
}
dependencies {
implementation "com.sumsub.sns:idensic-mobile-sdk-videoident:1.33.1"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
implementation(files("libs/mdisdk-release-1.2.54.aar"))
}

View File

@@ -47,7 +47,7 @@ workflows:
# =================================================================================
client-app-base: &client-app-base
name: Client App Base
working_directory: mobile-apps/client-app
working_directory: apps/mobile-client
instance_type: mac_mini_m2
max_build_duration: 60
environment:
@@ -57,15 +57,15 @@ workflows:
cache:
cache_paths:
- $HOME/.pub-cache
- $FCI_BUILD_DIR/mobile-apps/client-app/build
- $FCI_BUILD_DIR/mobile-apps/client-app/.dart_tool
- $FCI_BUILD_DIR/apps/mobile-client/build
- $FCI_BUILD_DIR/apps/mobile-client/.dart_tool
# =================================================================================
# Base workflow for staff_app
# =================================================================================
staff-app-base: &staff-app-base
name: Staff App Base
working_directory: mobile-apps/staff-app
working_directory: apps/mobile-staff
instance_type: mac_mini_m2
max_build_duration: 60
environment:
@@ -75,8 +75,8 @@ workflows:
cache:
cache_paths:
- $HOME/.pub-cache
- $FCI_BUILD_DIR/mobile-apps/staff-app/build
- $FCI_BUILD_DIR/mobile-apps/staff-app/.dart_tool
- $FCI_BUILD_DIR/apps/mobile-staff/build
- $FCI_BUILD_DIR/apps/mobile-staff/.dart_tool
# =================================================================================
# Client App Workflows - Android

View File

@@ -1,253 +0,0 @@
# DataConnect Inconsistencies between the Base44 frontend and the backend (Firebase Data Connect + PostgreSQL)
**Author:** José Salazar
**Date:** Dec 2025
---
## 📌 Purpose of this document
Document all findings during the integration of the new backend
(**Firebase Data Connect + PostgreSQL**) with the frontend generated by **Base44 AI**.
This document summarizes:
- Issues found
- camelCase vs snake_case inconsistencies
- Enum inconsistencies (uppercase/lowercase and dashes)
- Differences between what DataConnect returns vs what the frontend expects
- Recommended fixes
- Suggestions for Base44 AI to update its model
- Impact on queries, mutations, and the generated SDK
---
## 1⃣ Enums Different formats between Front and Backend
### Observation
In the frontend, enum values are in mixed formats, for example:
- UPPERCASE: SKILLED
- camelCase: fullTime
In the backend (DataConnect schema), enums are defined only in UPPERCASE, for example:
- FULL_TIME
- CROSS_TRAINED
- NOT_REQUIRED
- PENDING
DataConnect only accepts these exact values.
### Problem
When the frontend sends:
- "crossTrained" instead of CROSS_TRAINED
- "fluent" instead of FLUENT
### Impact
- Mutations can fail or return enum validation errors.
- Filters using enums return no results.
- Behavior changes depending on how Base44 AI generated the object.
### Recommendation
- Define a single standard: **ALL enums must be UPPERCASE** on the frontend and backend.
- Before sending to the backend, normalize enum values to uppercase.
### Suggestion for Base44 AI
- Adjust models so they always generate enums in UPPERCASE.
---
## 2⃣ Enums with dashes (“-”) Not valid in GraphQL
### Observation
In the legacy frontend, some enum values contain dashes, for example:
- CUSTOMER-SERVICE
- CROSS-TRAINED
- PART-TIME
But in GraphQL enums only allow letters, numbers, and underscores.
The backend had to define them as:
- CUSTOMER_SERVICE
- CROSS_TRAINED
- PART_TIME
### Problem
When the frontend sends "CUSTOMER-SERVICE" or "CROSS-TRAINED":
- The backend expects CUSTOMER_SERVICE or CROSS_TRAINED.
- There is no match between the frontend value and the DataConnect enum.
### Impact
- Enum filters return nothing.
- Mutations fail when trying to save invalid enum values.
- Compatibility between the Base44 model and the DataConnect schema breaks.
### Recommendation
- Standardize all enums to UPPERCASE SNAKE_CASE (e.g., CUSTOMER_SERVICE).
- Never use dashes “-” in enum values.
### Suggestion for Base44 AI
- Update models so enum values are always generated as
UPPERCASE_WITH_UNDERSCORE (e.g., CUSTOMER_SERVICE), without dashes.
---
## 3⃣ Field names Front in snake_case vs DataConnect in camelCase
### Observation
The original Base44 frontend uses snake_case field names, for example:
- contact_number
- vendor_id
- background_check_status
- hub_location
In DataConnect the schema is camelCase, and although you can map to the actual PostgreSQL column using @col, the GraphQL type remains camelCase, for example:
- contactNumber (mapped to "contact_number" in Postgres)
- vendorId (mapped to "vendor_id")
- backgroundCheckStatus (mapped to "background_check_status")
- hubLocation (mapped to "hub_location")
Meaning:
- In the database (PostgreSQL) names remain snake_case.
- In DataConnect and the SDK they are exposed as camelCase.
### Problem
The frontend still expects/reads fields like contact_number, but the SDK returns contactNumber.
A similar issue happens when the frontend sends payloads in snake_case:
- The GraphQL schema does not recognize contact_number.
- It only accepts contactNumber.
### Impact
- UI fails to show data because it reads keys that dont exist (snake_case).
- Mutations fail or ignore fields due to mismatched names.
- Filters with snake_case are invalid in GraphQL.
### Recommendation
- Agree that **all communication with DataConnect (frontend + SDK) uses camelCase**.
- Keep snake_case only at PostgreSQL level using @col, for example:
employeeName: String @col(name: "employee_name")
Thus:
- Frontend / SDK / GraphQL → camelCase (employeeName)
- PostgreSQL → snake_case (employee_name)
### Suggestion for Base44 AI
- Adjust generated frontend code so it uses camelCase when consuming the new backend.
- If Supabase or another backend is still used, document all mappings clearly.
---
## 4⃣ Fields used by the frontend but not listed in API Spec v3
### Observation
During integration we found that Base44 frontend uses fields not defined in the official document:
Reference file:
docs/03-backend-api-specification-v3.md
Examples in the Staff entity:
The frontend sends and displays fields like:
- notes
- rate
But these fields were not defined in the original v3 specification for Staff.
### Problem
- The frontend assumes these fields exist because the old database had them.
- The DataConnect schema does not include them.
- Sending these values in mutations causes validation errors.
### Impact
- Inconsistency between what the UI shows/edits and what is actually persisted.
- Risk of losing data the user believes is being saved.
- Hard to maintain a 1:1 mapping between the previous Base44 model and the new backend.
### Recommendation
- Validate which fields should truly exist for each entity (e.g., Staff).
- Align these three items:
1. API Spec v3
2. DataConnect Schema
3. Base44 Frontend
- If a field is no longer needed, remove it from the frontend.
- If important, add it formally to the API Spec and to the DataConnect schema.
---
## 5⃣ DataConnect vs Front Observed behavior
1. DataConnect:
- Always exposes fields in camelCase.
- Enforces enum restrictions exactly as defined (UPPERCASE, no dashes).
- Allows mapping to Postgres column names using @col, but GraphQL names remain camelCase.
2. Base44 Frontend:
- Uses snake_case in many areas.
- Uses enums in mixed formats (uppercase, camelCase, with dashes).
- Contains extra fields not included in API Spec v3.
---
## 6⃣ Suggested fixes (personal criteria)
1. Enums
- Standardize to UPPERCASE_SNAKE_CASE for all enum values.
- Apply a normalization layer in the frontend to convert any format into the official one before hitting the backend.
2. Field names
- Migrate the frontend to camelCase for any interaction with DataConnect.
- Keep snake_case only at the database layer using @col.
3. Extra fields
- Review all fields the frontend sends and compare with API Spec v3.
- Remove or add fields depending on what becomes the “source of truth” (Spec v3).
4. Documentation
- Keep this file updated as the Base44 → DataConnect migration reference.
- Add valid payload examples for each entity (Staff, Vendor, Invoice, etc.).
---
## 7⃣ Summary
- Always generate:
- Enums: UPPERCASE_SNAKE_CASE (e.g., FULL_TIME, CUSTOMER_SERVICE).
- Fields: camelCase (e.g., contactNumber, hubLocation, backgroundCheckStatus).
- Avoid:
- Dashes “-” in enum values.
- Spaces in enum values.
---
This document captures the current findings and serves as a guide to fully align the Base44 frontend with the backend based on Firebase Data Connect + PostgreSQL.

View File

@@ -1,51 +0,0 @@
mutation CreateActivityLog(
$title: String!,
$description: String!,
$activityType: ActivityType!,
$userId: String!,
$isRead: Boolean,
$iconType: String,
$iconColor: String
) @auth(level: USER) {
activityLog_insert(
data: {
title: $title
description: $description
activityType: $activityType
userId: $userId
isRead: $isRead
iconType: $iconType
iconColor: $iconColor
}
)
}
mutation UpdateActivityLog(
$id: UUID!,
$title: String,
$description: String,
$activityType: ActivityType,
$userId: String,
$isRead: Boolean,
$iconType: String,
$iconColor: String
) @auth(level: USER) {
activityLog_update(
id: $id,
data: {
title: $title
description: $description
activityType: $activityType
userId: $userId
isRead: $isRead
iconType: $iconType
iconColor: $iconColor
}
)
}
mutation DeleteActivityLog(
$id: UUID!
) @auth(level: USER) {
activityLog_delete(id: $id)
}

View File

@@ -1,50 +0,0 @@
query listActivityLog @auth(level: USER) {
activityLogs {
id
title
description
activityType
userId
isRead
iconType
iconColor
}
}
query getActivityLogById(
$id: UUID!
) @auth(level: USER) {
activityLog(id: $id) {
id
title
description
activityType
userId
isRead
iconType
iconColor
}
}
query filterActivityLog(
$userId: String,
$activityType: ActivityType,
$isRead: Boolean
) @auth(level: USER) {
activityLogs(
where: {
userId: { eq: $userId }
activityType: { eq: $activityType }
isRead: { eq: $isRead }
}
) {
id
title
description
activityType
userId
isRead
iconType
iconColor
}
}

View File

@@ -1,51 +0,0 @@
mutation CreateAssignment(
$assignmentNumber: String,
$orderId: UUID!,
$workforceId: UUID!,
$vendorId: UUID!,
$role: String!,
$assignmentStatus: AssignmentStatus!,
$scheduledStart: Timestamp!
) @auth(level: USER) {
assignment_insert(
data: {
assignmentNumber: $assignmentNumber
orderId: $orderId
workforceId: $workforceId
vendorId: $vendorId
role: $role
assignmentStatus: $assignmentStatus
scheduledStart: $scheduledStart
}
)
}
mutation UpdateAssignment(
$id: UUID!,
$assignmentNumber: String,
$orderId: UUID,
$workforceId: UUID,
$vendorId: UUID,
$role: String,
$assignmentStatus: AssignmentStatus,
$scheduledStart: Timestamp
) @auth(level: USER) {
assignment_update(
id: $id,
data: {
assignmentNumber: $assignmentNumber
orderId: $orderId
workforceId: $workforceId
vendorId: $vendorId
role: $role
assignmentStatus: $assignmentStatus
scheduledStart: $scheduledStart
}
)
}
mutation DeleteAssignment(
$id: UUID!
) @auth(level: USER) {
assignment_delete(id: $id)
}

View File

@@ -1,56 +0,0 @@
# dataconnect/connector/assignment/queries.gql
query listAssignment @auth(level: USER) {
assignments {
id
assignmentNumber
orderId
workforceId
vendorId
role
assignmentStatus
scheduledStart
}
}
query getAssignmentById(
$id: UUID!
) @auth(level: USER) {
assignment(id: $id) {
id
assignmentNumber
orderId
workforceId
vendorId
role
assignmentStatus
scheduledStart
}
}
query filterAssignment(
$assignmentNumber: String,
$orderId: UUID,
$workforceId: UUID,
$vendorId: UUID,
$assignmentStatus: AssignmentStatus
) @auth(level: USER) {
assignments(
where: {
assignmentNumber: { eq: $assignmentNumber }
orderId: { eq: $orderId }
workforceId: { eq: $workforceId }
vendorId: { eq: $vendorId }
assignmentStatus: { eq: $assignmentStatus }
}
) {
id
assignmentNumber
orderId
workforceId
vendorId
role
assignmentStatus
scheduledStart
}
}

View File

@@ -1,47 +0,0 @@
mutation CreateBusiness(
$businessName: String!,
$contactName: String!,
$email: String,
$sector: BusinessSector,
$rateGroup: BusinessRateGroup!,
$status: BusinessStatus
) @auth(level: USER) {
business_insert(
data: {
businessName: $businessName
contactName: $contactName
email: $email
sector: $sector
rateGroup: $rateGroup
status: $status
}
)
}
mutation UpdateBusiness(
$id: UUID!,
$businessName: String,
$contactName: String,
$email: String,
$sector: BusinessSector,
$rateGroup: BusinessRateGroup,
$status: BusinessStatus
) @auth(level: USER) {
business_update(
id: $id,
data: {
businessName: $businessName
contactName: $contactName
email: $email
sector: $sector
rateGroup: $rateGroup
status: $status
}
)
}
mutation DeleteBusiness(
$id: UUID!
) @auth(level: USER) {
business_delete(id: $id)
}

View File

@@ -1,51 +0,0 @@
query listBusiness @auth(level: USER) {
businesses {
id
businessName
contactName
email
sector
rateGroup
status
}
}
query getBusinessById(
$id: UUID!
) @auth(level: USER) {
business(id: $id) {
id
businessName
contactName
email
sector
rateGroup
status
}
}
query filterBusiness(
$businessName: String,
$contactName: String,
$sector: BusinessSector,
$rateGroup: BusinessRateGroup,
$status: BusinessStatus
) @auth(level: USER) {
businesses(
where: {
businessName: { eq: $businessName }
contactName: { eq: $contactName }
sector: { eq: $sector }
rateGroup: { eq: $rateGroup }
status: { eq: $status }
}
) {
id
businessName
contactName
email
sector
rateGroup
status
}
}

View File

@@ -1,47 +0,0 @@
mutation CreateCertification(
$employeeName: String!,
$certificationName: String!,
$certificationType: CertificationType,
$status: CertificationStatus,
$expiryDate: String!,
$validationStatus: CertificationValidationStatus
) @auth(level: USER) {
certification_insert(
data: {
employeeName: $employeeName
certificationName: $certificationName
certificationType: $certificationType
status: $status
expiryDate: $expiryDate
validationStatus: $validationStatus
}
)
}
mutation UpdateCertification(
$id: UUID!,
$employeeName: String,
$certificationName: String,
$certificationType: CertificationType,
$status: CertificationStatus,
$expiryDate: String,
$validationStatus: CertificationValidationStatus
) @auth(level: USER) {
certification_update(
id: $id,
data: {
employeeName: $employeeName
certificationName: $certificationName
certificationType: $certificationType
status: $status
expiryDate: $expiryDate
validationStatus: $validationStatus
}
)
}
mutation DeleteCertification(
$id: UUID!
) @auth(level: USER) {
certification_delete(id: $id)
}

View File

@@ -1,54 +0,0 @@
query listCertification @auth(level: USER) {
certifications {
id
employeeName
certificationName
certificationType
status
expiryDate
validationStatus
}
}
query getCertificationById(
$id: UUID!
) @auth(level: USER) {
certification(id: $id) {
id
employeeName
certificationName
certificationType
status
expiryDate
validationStatus
createdDate
updatedDate
createdBy
}
}
query filterCertification(
$employeeName: String,
$certificationName: String,
$certificationType: CertificationType,
$status: CertificationStatus,
$validationStatus: CertificationValidationStatus
) @auth(level: USER) {
certifications(
where: {
employeeName: { eq: $employeeName }
certificationName: { eq: $certificationName }
certificationType: { eq: $certificationType }
status: { eq: $status }
validationStatus: { eq: $validationStatus }
}
) {
id
employeeName
certificationName
certificationType
status
expiryDate
validationStatus
}
}

View File

@@ -1,18 +0,0 @@
connectorId: krow-connector
generate:
javascriptSdk:
- outputDir: ../../frontend-web/src/dataconnect-generated
package: "@dataconnect/generated"
packageJsonDir: ../../frontend-web
react: true
angular: false
- outputDir: ../../internal-api-harness/src/dataconnect-generated
package: "@dataconnect/generated"
packageJsonDir: ../../internal-api-harness
react: true
angular: false
- outputDir: ../../frontend-web-free/src/dataconnect-generated
package: "@dataconnect/generated"
packageJsonDir: ../../frontend-web-free
react: true
angular: false

View File

@@ -1,39 +0,0 @@
mutation CreateConversation(
$participants: String!,
$conversationType: ConversationType!,
$relatedTo: UUID!,
$status: ConversationStatus
) @auth(level: USER) {
conversation_insert(
data: {
participants: $participants
conversationType: $conversationType
relatedTo: $relatedTo
status: $status
}
)
}
mutation UpdateConversation(
$id: UUID!,
$participants: String,
$conversationType: ConversationType,
$relatedTo: UUID,
$status: ConversationStatus
) @auth(level: USER) {
conversation_update(
id: $id,
data: {
participants: $participants
conversationType: $conversationType
relatedTo: $relatedTo
status: $status
}
)
}
mutation DeleteConversation(
$id: UUID!
) @auth(level: USER) {
conversation_delete(id: $id)
}

View File

@@ -1,43 +0,0 @@
# dataconnect/connector/conversation/queries.gql
query listConversation @auth(level: USER) {
conversations {
id
participants
conversationType
relatedTo
status
}
}
query getConversationById(
$id: UUID!
) @auth(level: USER) {
conversation(id: $id) {
id
participants
conversationType
relatedTo
status
}
}
query filterConversation(
$conversationType: ConversationType,
$status: ConversationStatus,
$relatedTo: UUID
) @auth(level: USER) {
conversations(
where: {
conversationType: { eq: $conversationType }
status: { eq: $status }
relatedTo: { eq: $relatedTo }
}
) {
id
participants
conversationType
relatedTo
status
}
}

View File

@@ -1,35 +0,0 @@
mutation CreateEnterprise(
$enterpriseNumber: String!,
$enterpriseName: String!,
$enterpriseCode: String!
) @auth(level: USER) {
enterprise_insert(
data: {
enterpriseNumber: $enterpriseNumber
enterpriseName: $enterpriseName
enterpriseCode: $enterpriseCode
}
)
}
mutation UpdateEnterprise(
$id: UUID!,
$enterpriseNumber: String,
$enterpriseName: String,
$enterpriseCode: String
) @auth(level: USER) {
enterprise_update(
id: $id,
data: {
enterpriseNumber: $enterpriseNumber
enterpriseName: $enterpriseName
enterpriseCode: $enterpriseCode
}
)
}
mutation DeleteEnterprise(
$id: UUID!
) @auth(level: USER) {
enterprise_delete(id: $id)
}

View File

@@ -1,38 +0,0 @@
query listEnterprise @auth(level: USER) {
enterprises {
id
enterpriseNumber
enterpriseName
enterpriseCode
}
}
query getEnterpriseById(
$id: UUID!
) @auth(level: USER) {
enterprise(id: $id) {
id
enterpriseNumber
enterpriseName
enterpriseCode
}
}
query filterEnterprise(
$enterpriseNumber: String,
$enterpriseName: String,
$enterpriseCode: String
) @auth(level: USER) {
enterprises(
where: {
enterpriseNumber: { eq: $enterpriseNumber }
enterpriseName: { eq: $enterpriseName }
enterpriseCode: { eq: $enterpriseCode }
}
) {
id
enterpriseNumber
enterpriseName
enterpriseCode
}
}

View File

@@ -1,208 +0,0 @@
mutation CreateEvent(
$eventName: String!,
$isRapid: Boolean,
$isRecurring: Boolean,
$isMultiDay: Boolean,
$recurrenceType: RecurrenceType,
$recurrenceStartDate: Timestamp,
$recurrenceEndDate: Timestamp,
$scatterDates: Any,
$multiDayStartDate: Timestamp,
$multiDayEndDate: Timestamp,
$bufferTimeBefore: Float,
$bufferTimeAfter: Float,
$conflictDetectionEnabled: Boolean,
$detectedConflicts: Any,
$businessId: UUID!,
$businessName: String,
$vendorId: String,
$vendorName: String,
$hub: String,
$eventLocation: String,
$contractType: ContractType,
$poReference: String,
$status: EventStatus!,
$date: String!,
$shifts: Any,
$addons: Any,
$total: Float,
$clientName: String,
$clientEmail: String,
$clientPhone: String,
$invoiceId: UUID,
$notes: String,
$requested: Int,
$assignedStaff: Any,
$department: String,
$createdBy: String,
$orderType: String,
$recurringStartDate: String,
$recurringEndDate: String,
$recurringDays: Any,
$permanentStartDate: String,
$permanentDays: Any,
$includeBackup: Boolean,
$backupStaffCount: Int,
$recurringTime: String,
$permanentTime: String
) @auth(level: USER) {
event_insert(
data: {
eventName: $eventName
isRapid: $isRapid
isRecurring: $isRecurring
isMultiDay: $isMultiDay
recurrenceType: $recurrenceType
recurrenceStartDate: $recurrenceStartDate
recurrenceEndDate: $recurrenceEndDate
scatterDates: $scatterDates
multiDayStartDate: $multiDayStartDate
multiDayEndDate: $multiDayEndDate
bufferTimeBefore: $bufferTimeBefore
bufferTimeAfter: $bufferTimeAfter
conflictDetectionEnabled: $conflictDetectionEnabled
detectedConflicts: $detectedConflicts
businessId: $businessId
businessName: $businessName
vendorId: $vendorId
vendorName: $vendorName
hub: $hub
eventLocation: $eventLocation
contractType: $contractType
poReference: $poReference
status: $status
date: $date
shifts: $shifts
addons: $addons
total: $total
clientName: $clientName
clientEmail: $clientEmail
clientPhone: $clientPhone
invoiceId: $invoiceId
notes: $notes
orderType: $orderType
requested: $requested
assignedStaff: $assignedStaff
department: $department
createdBy: $createdBy
recurringStartDate: $recurringStartDate
recurringEndDate: $recurringEndDate
recurringDays: $recurringDays
permanentStartDate: $permanentStartDate
permanentDays: $permanentDays
includeBackup: $includeBackup
backupStaffCount: $backupStaffCount
recurringTime: $recurringTime
permanentTime: $permanentTime
}
)
}
mutation UpdateEvent(
$id: UUID!,
$eventName: String,
$isRapid: Boolean,
$isRecurring: Boolean,
$isMultiDay: Boolean,
$recurrenceType: RecurrenceType,
$recurrenceStartDate: Timestamp,
$recurrenceEndDate: Timestamp,
$scatterDates: Any,
$multiDayStartDate: Timestamp,
$multiDayEndDate: Timestamp,
$bufferTimeBefore: Float,
$bufferTimeAfter: Float,
$conflictDetectionEnabled: Boolean,
$detectedConflicts: Any,
$businessId: UUID,
$businessName: String,
$vendorId: String,
$vendorName: String,
$hub: String,
$eventLocation: String,
$contractType: ContractType,
$poReference: String,
$status: EventStatus,
$date: String,
$shifts: Any,
$addons: Any,
$total: Float,
$clientName: String,
$clientEmail: String,
$clientPhone: String,
$invoiceId: UUID,
$notes: String,
$requested: Int,
$orderType: String,
$department: String,
$assignedStaff: Any,
$createdBy: String,
$recurringStartDate: String,
$recurringEndDate: String,
$recurringDays: Any,
$permanentStartDate: String,
$permanentDays: Any,
$includeBackup: Boolean,
$backupStaffCount: Int,
$recurringTime: String,
$permanentTime: String
) @auth(level: USER) {
event_update(
id: $id,
data: {
eventName: $eventName
isRapid: $isRapid
isRecurring: $isRecurring
isMultiDay: $isMultiDay
recurrenceType: $recurrenceType
recurrenceStartDate: $recurrenceStartDate
recurrenceEndDate: $recurrenceEndDate
scatterDates: $scatterDates
multiDayStartDate: $multiDayStartDate
multiDayEndDate: $multiDayEndDate
bufferTimeBefore: $bufferTimeBefore
bufferTimeAfter: $bufferTimeAfter
conflictDetectionEnabled: $conflictDetectionEnabled
detectedConflicts: $detectedConflicts
businessId: $businessId
businessName: $businessName
vendorId: $vendorId
vendorName: $vendorName
hub: $hub
eventLocation: $eventLocation
contractType: $contractType
poReference: $poReference
status: $status
date: $date
shifts: $shifts
addons: $addons
total: $total
clientName: $clientName
clientEmail: $clientEmail
clientPhone: $clientPhone
invoiceId: $invoiceId
notes: $notes
orderType: $orderType
requested: $requested
assignedStaff: $assignedStaff
department: $department
createdBy: $createdBy
recurringStartDate: $recurringStartDate
recurringEndDate: $recurringEndDate
recurringDays: $recurringDays
permanentStartDate: $permanentStartDate
permanentDays: $permanentDays
includeBackup: $includeBackup
backupStaffCount: $backupStaffCount
recurringTime: $recurringTime
permanentTime: $permanentTime
}
)
}
mutation DeleteEvent(
$id: UUID!
) @auth(level: USER) {
event_delete(id: $id)
}

View File

@@ -1,193 +0,0 @@
query listEvents (
$orderByDate: OrderDirection
$orderByCreatedDate: OrderDirection,
$limit: Int
) @auth(level: USER) {
events(
orderBy: [
{ date: $orderByDate }
{ createdDate: $orderByCreatedDate }
]
limit: $limit
) {
id
eventName
status
date
isRapid
isRecurring
isMultiDay
recurrenceType
recurrenceStartDate
recurrenceEndDate
scatterDates
multiDayStartDate
multiDayEndDate
bufferTimeBefore
bufferTimeAfter
conflictDetectionEnabled
detectedConflicts
businessId
businessName
vendorId
vendorName
hub
eventLocation
contractType
poReference
shifts
addons
total
clientName
clientEmail
clientPhone
invoiceId
notes
requested
assignedStaff
orderType
department
createdBy
recurringStartDate
recurringEndDate
recurringDays
permanentStartDate
permanentDays
includeBackup
backupStaffCount
recurringTime
permanentTime
}
}
query getEventById(
$id: UUID!
) @auth(level: USER) {
event(id: $id) {
id
eventName
status
date
isRapid
isRecurring
isMultiDay
recurrenceType
recurrenceStartDate
recurrenceEndDate
scatterDates
multiDayStartDate
multiDayEndDate
bufferTimeBefore
bufferTimeAfter
conflictDetectionEnabled
detectedConflicts
businessId
businessName
vendorId
vendorName
hub
eventLocation
contractType
poReference
shifts
addons
total
clientName
clientEmail
clientPhone
invoiceId
notes
requested
orderType
department
assignedStaff
recurringStartDate
recurringEndDate
recurringDays
permanentStartDate
permanentDays
includeBackup
backupStaffCount
recurringTime
permanentTime
}
}
query filterEvents(
$status: EventStatus,
$businessId: UUID,
$vendorId: String,
$isRecurring: Boolean,
$isRapid: Boolean,
$isMultiDay: Boolean,
$recurrenceType: RecurrenceType,
$date: String,
$hub: String,
$eventLocation: String,
$contractType: ContractType,
$clientEmail: String
) @auth(level: USER) {
events(
where: {
status: { eq: $status }
businessId: { eq: $businessId }
vendorId: { eq: $vendorId }
isRecurring: { eq: $isRecurring }
isRapid: { eq: $isRapid }
isMultiDay: { eq: $isMultiDay }
recurrenceType: { eq: $recurrenceType }
date: { eq: $date }
hub: { eq: $hub }
eventLocation: { eq: $eventLocation }
contractType: { eq: $contractType }
clientEmail: { eq: $clientEmail }
}) {
id
eventName
status
date
isRapid
isRecurring
isMultiDay
recurrenceType
recurrenceStartDate
recurrenceEndDate
scatterDates
multiDayStartDate
multiDayEndDate
bufferTimeBefore
bufferTimeAfter
conflictDetectionEnabled
detectedConflicts
businessId
businessName
vendorId
vendorName
hub
eventLocation
contractType
poReference
shifts
addons
total
clientName
clientEmail
clientPhone
invoiceId
notes
requested
assignedStaff
orderType
department
createdBy
recurringStartDate
recurringEndDate
recurringDays
permanentStartDate
permanentDays
includeBackup
backupStaffCount
recurringTime
permanentTime
}
}

View File

@@ -1,51 +0,0 @@
mutation CreateInvoice(
$invoiceNumber: String!,
$amount: Float!,
$status: InvoiceStatus!,
$issueDate: Timestamp!,
$dueDate: Timestamp!,
$disputedItems: String,
$isAutoGenerated: Boolean
) @auth(level: USER) {
invoice_insert(
data: {
invoiceNumber: $invoiceNumber
amount: $amount
status: $status
issueDate: $issueDate
dueDate: $dueDate
disputedItems: $disputedItems
isAutoGenerated: $isAutoGenerated
}
)
}
mutation UpdateInvoice(
$id: UUID!,
$invoiceNumber: String,
$amount: Float,
$status: InvoiceStatus,
$issueDate: Timestamp,
$dueDate: Timestamp,
$disputedItems: String,
$isAutoGenerated: Boolean
) @auth(level: USER) {
invoice_update(
id: $id,
data: {
invoiceNumber: $invoiceNumber
amount: $amount
status: $status
issueDate: $issueDate
dueDate: $dueDate
disputedItems: $disputedItems
isAutoGenerated: $isAutoGenerated
}
)
}
mutation DeleteInvoice(
$id: UUID!
) @auth(level: USER) {
invoice_delete(id: $id)
}

View File

@@ -1,51 +0,0 @@
query listInvoice @auth(level: USER) {
invoices {
id
invoiceNumber
amount
status
issueDate
dueDate
disputedItems
isAutoGenerated
}
}
query getInvoiceById(
$id: UUID!
) @auth(level: USER) {
invoice(id: $id) {
id
invoiceNumber
amount
status
issueDate
dueDate
disputedItems
isAutoGenerated
}
}
query filterInvoices(
$invoiceNumber: String,
$status: InvoiceStatus,
$isAutoGenerated: Boolean,
$amount: Float
) @auth(level: USER) {
invoices(
where: {
invoiceNumber: { eq: $invoiceNumber }
status: { eq: $status }
isAutoGenerated: { eq: $isAutoGenerated }
amount: { eq: $amount }
}
) {
id
invoiceNumber
amount
status
issueDate
dueDate
isAutoGenerated
}
}

View File

@@ -1,39 +0,0 @@
mutation CreateMessage(
$conversationId: UUID!,
$senderName: String!,
$content: String!,
$readBy: String
) @auth(level: USER) {
message_insert(
data: {
conversationId: $conversationId
senderName: $senderName
content: $content
readBy: $readBy
}
)
}
mutation UpdateMessage(
$id: UUID!,
$conversationId: UUID,
$senderName: String,
$content: String,
$readBy: String
) @auth(level: USER) {
message_update(
id: $id,
data: {
conversationId: $conversationId
senderName: $senderName
content: $content
readBy: $readBy
}
)
}
mutation DeleteMessage(
$id: UUID!
) @auth(level: USER) {
message_delete(id: $id)
}

View File

@@ -1,39 +0,0 @@
query listMessage @auth(level: USER) {
messages {
id
conversationId
senderName
content
readBy
}
}
query getMessageById(
$id: UUID!
) @auth(level: USER) {
message(id: $id) {
id
conversationId
senderName
content
readBy
}
}
query filterMessage(
$conversationId: UUID,
$senderName: String
) @auth(level: USER) {
messages(
where: {
conversationId: { eq: $conversationId }
senderName: { eq: $senderName }
}
) {
id
conversationId
senderName
content
readBy
}
}

View File

@@ -1,39 +0,0 @@
mutation CreateOrder(
$orderNumber: String!,
$partnerId: UUID!,
$orderType: OrderType,
$orderStatus: OrderStatus
) @auth(level: USER) {
order_insert(
data: {
orderNumber: $orderNumber
partnerId: $partnerId
orderType: $orderType
orderStatus: $orderStatus
}
)
}
mutation UpdateOrder(
$id: UUID!,
$orderNumber: String,
$partnerId: UUID,
$orderType: OrderType,
$orderStatus: OrderStatus
) @auth(level: USER) {
order_update(
id: $id,
data: {
orderNumber: $orderNumber
partnerId: $partnerId
orderType: $orderType
orderStatus: $orderStatus
}
)
}
mutation DeleteOrder(
$id: UUID!
) @auth(level: USER) {
order_delete(id: $id)
}

View File

@@ -1,43 +0,0 @@
query listOrder @auth(level: USER) {
orders {
id
orderNumber
partnerId
orderType
orderStatus
}
}
query getOrderById(
$id: UUID!
) @auth(level: USER) {
order(id: $id) {
id
orderNumber
partnerId
orderType
orderStatus
}
}
query filterOrder(
$orderNumber: String,
$partnerId: UUID,
$orderType: OrderType,
$orderStatus: OrderStatus
) @auth(level: USER) {
orders(
where: {
orderNumber: { eq: $orderNumber }
partnerId: { eq: $partnerId }
orderType: { eq: $orderType }
orderStatus: { eq: $orderStatus }
}
) {
id
orderNumber
partnerId
orderType
orderStatus
}
}

View File

@@ -1,35 +0,0 @@
mutation CreatePartner(
$partnerName: String!,
$partnerNumber: String!,
$partnerType: PartnerType
) @auth(level: USER) {
partner_insert(
data: {
partnerName: $partnerName
partnerNumber: $partnerNumber
partnerType: $partnerType
}
)
}
mutation UpdatePartner(
$id: UUID!,
$partnerName: String,
$partnerNumber: String,
$partnerType: PartnerType
) @auth(level: USER) {
partner_update(
id: $id,
data: {
partnerName: $partnerName
partnerNumber: $partnerNumber
partnerType: $partnerType
}
)
}
mutation DeletePartner(
$id: UUID!
) @auth(level: USER) {
partner_delete(id: $id)
}

View File

@@ -1,40 +0,0 @@
# dataconnect/connector/partner/queries.gql
query listPartner @auth(level: USER) {
partners {
id
partnerName
partnerNumber
partnerType
}
}
query getPartnerById(
$id: UUID!
) @auth(level: USER) {
partner(id: $id) {
id
partnerName
partnerNumber
partnerType
}
}
query filterPartner(
$partnerName: String,
$partnerNumber: String,
$partnerType: PartnerType
) @auth(level: USER) {
partners(
where: {
partnerName: { eq: $partnerName }
partnerNumber: { eq: $partnerNumber }
partnerType: { eq: $partnerType }
}
) {
id
partnerName
partnerNumber
partnerType
}
}

View File

@@ -1,35 +0,0 @@
mutation CreateSector(
$sectorNumber: String!,
$sectorName: String!,
$sectorType: SectorType
) @auth(level: USER) {
sector_insert(
data: {
sectorNumber: $sectorNumber
sectorName: $sectorName
sectorType: $sectorType
}
)
}
mutation UpdateSector(
$id: UUID!,
$sectorNumber: String,
$sectorName: String,
$sectorType: SectorType
) @auth(level: USER) {
sector_update(
id: $id,
data: {
sectorNumber: $sectorNumber
sectorName: $sectorName
sectorType: $sectorType
}
)
}
mutation DeleteSector(
$id: UUID!
) @auth(level: USER) {
sector_delete(id: $id)
}

View File

@@ -1,38 +0,0 @@
query listSector @auth(level: USER) {
sectors {
id
sectorNumber
sectorName
sectorType
}
}
query getSectorById(
$id: UUID!
) @auth(level: USER) {
sector(id: $id) {
id
sectorNumber
sectorName
sectorType
}
}
query filterSector(
$sectorNumber: String,
$sectorName: String,
$sectorType: SectorType
) @auth(level: USER) {
sectors(
where: {
sectorNumber: { eq: $sectorNumber }
sectorName: { eq: $sectorName }
sectorType: { eq: $sectorType }
}
) {
id
sectorNumber
sectorName
sectorType
}
}

View File

@@ -1,39 +0,0 @@
mutation CreateShift(
$shiftName: String!,
$startDate: Timestamp!,
$endDate: Timestamp,
$assignedStaff: String
) @auth(level: USER) {
shift_insert(
data: {
shiftName: $shiftName
startDate: $startDate
endDate: $endDate
assignedStaff: $assignedStaff
}
)
}
mutation UpdateShift(
$id: UUID!,
$shiftName: String,
$startDate: Timestamp,
$endDate: Timestamp,
$assignedStaff: String
) @auth(level: USER) {
shift_update(
id: $id,
data: {
shiftName: $shiftName
startDate: $startDate
endDate: $endDate
assignedStaff: $assignedStaff
}
)
}
mutation DeleteShift(
$id: UUID!
) @auth(level: USER) {
shift_delete(id: $id)
}

View File

@@ -1,44 +0,0 @@
query listShift @auth(level: USER) {
shifts {
id
shiftName
startDate
endDate
assignedStaff
}
}
query getShiftById(
$id: UUID!
) @auth(level: USER) {
shift(id: $id) {
id
shiftName
startDate
endDate
assignedStaff
createdDate
updatedDate
createdBy
}
}
query filterShift(
$shiftName: String,
$startDate: Timestamp,
$endDate: Timestamp
) @auth(level: USER) {
shifts(
where: {
shiftName: { eq: $shiftName }
startDate: { eq: $startDate }
endDate: { eq: $endDate }
}
) {
id
shiftName
startDate
endDate
assignedStaff
}
}

View File

@@ -1,171 +0,0 @@
mutation CreateStaff(
$employeeName: String!,
$vendorId: String,
$vendorName: String,
$manager: String,
$contactNumber: String,
$phone:String # nuevo,
$email: String,
$department: StaffDepartment,
$hubLocation: String,
$eventLocation:String, # nuevo
$address:String, # nuevo
$city:String, # nuevo
$track: String,
$position: String,
$position2:String, # nuevo
$initial:String, # nuevo
$profileType: ProfileType,
$employmentType: EmploymentType,
$english: EnglishLevel,
$rate: Float,
$rating: Float,
$reliabilityScore: Int,
$backgroundCheckStatus: BackgroundCheckStatus,
$notes: String,
$accountingComments:String, # nuevo
$shiftCoveragePercentage:Int, # nuevo
$cancellationCount:Int, # nuevo
$noShowCount:Int, # nuevo
$totalShifts:Int, # nuevo
$invoiced:Boolean, # nuevo
$englishRequired:Boolean, # nuevo
$checkIn:String, # nuevo
$scheduleDays:String, # nuevo
$replacedBy:String, # nuevo
$action:String, # nuevo
$ro:String, # nuevo
$mon:String, # nuevo
) @auth(level: USER) {
staff_insert(
data: {
employeeName: $employeeName
vendorId: $vendorId
vendorName: $vendorName
manager: $manager
contactNumber: $contactNumber
phone:$phone
email: $email
department: $department
hubLocation: $hubLocation
eventLocation:$eventLocation
address:$address
city:$city
track: $track
position: $position
position2:$position2
initial:$initial
profileType: $profileType
employmentType: $employmentType
english: $english
rate: $rate
rating: $rating
reliabilityScore: $reliabilityScore
backgroundCheckStatus: $backgroundCheckStatus
notes: $notes
accountingComments:$accountingComments
shiftCoveragePercentage:$shiftCoveragePercentage
cancellationCount:$cancellationCount
noShowCount:$noShowCount
totalShifts:$totalShifts
invoiced:$invoiced
englishRequired:$englishRequired
checkIn:$checkIn
scheduleDays:$scheduleDays
replacedBy:$replacedBy
action:$action
ro:$ro
mon:$mon
}
)
}
mutation UpdateStaff(
$id: UUID!,
$employeeName: String,
$vendorId: String,
$vendorName: String,
$manager: String,
$contactNumber: String,
$phone: String,
$email: String,
$department: StaffDepartment,
$hubLocation: String,
$eventLocation: String,
$address: String,
$city: String,
$track: String,
$position: String,
$position2:String,
$initial:String,
$profileType: ProfileType,
$employmentType: EmploymentType,
$english: EnglishLevel,
$englishRequired:Boolean # nuevo
$rate: Float,
$rating: Float,
$reliabilityScore: Int,
$backgroundCheckStatus: BackgroundCheckStatus,
$notes: String,
$accountingComments:String,
$shiftCoveragePercentage:Int,
$cancellationCount:Int,
$noShowCount:Int,
$totalShifts:Int,
$invoiced:Boolean,
$checkIn:String,
$scheduleDays:String,
$replacedBy:String,
$action:String,
$ro:String,
$mon:String,
) @auth(level: USER) {
staff_update(
id: $id,
data: {
employeeName: $employeeName
vendorId: $vendorId
vendorName: $vendorName
manager: $manager
contactNumber: $contactNumber
phone:$phone # nuevo
email: $email
department: $department
hubLocation: $hubLocation
eventLocation:$eventLocation # nuevo
address:$address # nuevo
city:$city # nuevo
track: $track
position: $position
position2:$position2 # nuevo
initial:$initial # nuevo
profileType: $profileType
employmentType: $employmentType
english: $english
englishRequired:$englishRequired # nuevo
rate: $rate
rating: $rating
reliabilityScore: $reliabilityScore
backgroundCheckStatus: $backgroundCheckStatus
notes: $notes
accountingComments:$accountingComments # nuevo
shiftCoveragePercentage:$shiftCoveragePercentage # nuevo
cancellationCount:$cancellationCount # nuevo
noShowCount:$noShowCount # nuevo
totalShifts:$totalShifts # nuevo
invoiced:$invoiced # nuevo
checkIn:$checkIn # nuevo
scheduleDays:$scheduleDays # nuevo
replacedBy:$replacedBy # nuevo
action:$action # nuevo
ro:$ro # nuevo
mon:$mon # nuevo
}
)
}
mutation DeleteStaff(
$id: UUID!
) @auth(level: USER) {
staff_delete(id: $id)
}

View File

@@ -1,125 +0,0 @@
query listStaff @auth(level: USER) {
staffs {
id
employeeName
vendorId
vendorName
manager
contactNumber
phone # nuevo
email
department
hubLocation
eventLocation # nuevo
address # nuevo
city # nuevo
track
position
position2
initial # nuevo
profileType
employmentType
english
englishRequired # nuevo
rate
rating
reliabilityScore
backgroundCheckStatus
notes
accountingComments # nuevo
shiftCoveragePercentage # nuevo
cancellationCount # nuevo
noShowCount # nuevo
totalShifts # nuevo
invoiced # nuevo
checkIn # nuevo
scheduleDays # nuevo
replacedBy # nuevo
action # nuevo
ro # nuevo
mon # nuevo
}
}
query getStaffById(
$id: UUID!
) @auth(level: USER) {
staff(id: $id) {
id
employeeName
vendorId
vendorName
manager
contactNumber
phone # nuevo
email
department
hubLocation
eventLocation # nuevo
address # nuevo
city # nuevo
track
position
position2 # nuevo
initial # nuevo
profileType
employmentType
english
rate
rating
reliabilityScore
backgroundCheckStatus
notes
accountingComments # nuevo
shiftCoveragePercentage # nuevo
cancellationCount # nuevo
noShowCount # nuevo
totalShifts # nuevo
invoiced # nuevo
englishRequired # nuevo
checkIn # nuevo
scheduleDays # nuevo
replacedBy # nuevo
action # nuevo
ro # nuevo
mon # nuevo
}
}
query filterStaff(
$employeeName: String,
$vendorId: String,
$department: StaffDepartment,
$employmentType: EmploymentType,
$english: EnglishLevel,
$backgroundCheckStatus: BackgroundCheckStatus
) @auth(level: USER) {
staffs(
where: {
employeeName: { eq: $employeeName }
vendorId: { eq: $vendorId }
department: { eq: $department }
employmentType: { eq: $employmentType }
english: { eq: $english }
backgroundCheckStatus: { eq: $backgroundCheckStatus }
}
) {
id
employeeName
vendorId
vendorName
department
hubLocation
eventLocation # nuevo
position
position2
employmentType
english
rate
rating
reliabilityScore
backgroundCheckStatus
notes
invoiced # nuevo
}
}

View File

@@ -1,47 +0,0 @@
mutation CreateTeam(
$teamName: String!,
$ownerId: String!,
$ownerName: String!,
$ownerRole: TeamOwnerRole!,
$favoriteStaff: String,
$blockedStaff: String
) @auth(level: USER) {
team_insert(
data: {
teamName: $teamName
ownerId: $ownerId
ownerName: $ownerName
ownerRole: $ownerRole
favoriteStaff: $favoriteStaff
blockedStaff: $blockedStaff
}
)
}
mutation UpdateTeam(
$id: UUID!,
$teamName: String,
$ownerId: String,
$ownerName: String,
$ownerRole: TeamOwnerRole,
$favoriteStaff: String,
$blockedStaff: String
) @auth(level: USER) {
team_update(
id: $id,
data: {
teamName: $teamName
ownerId: $ownerId
ownerName: $ownerName
ownerRole: $ownerRole
favoriteStaff: $favoriteStaff
blockedStaff: $blockedStaff
}
)
}
mutation DeleteTeam(
$id: UUID!
) @auth(level: USER) {
team_delete(id: $id)
}

View File

@@ -1,53 +0,0 @@
query listTeam (
$orderByCreatedDate: OrderDirection
$limit: Int
) @auth(level: USER) {
teams(
orderBy: { createdDate: $orderByCreatedDate }
limit: $limit
) {
id
teamName
ownerId
ownerName
ownerRole
favoriteStaff
blockedStaff
}
}
query getTeamById(
$id: UUID!
) @auth(level: USER) {
team(id: $id) {
id
teamName
ownerId
ownerName
ownerRole
favoriteStaff
blockedStaff
}
}
query filterTeam(
$teamName: String,
$ownerId: String,
$ownerRole: TeamOwnerRole
) @auth(level: USER) {
teams(
where: {
teamName: { eq: $teamName }
ownerId: { eq: $ownerId }
ownerRole: { eq: $ownerRole }
}
) {
id
teamName
ownerId
ownerName
ownerRole
favoriteStaff
blockedStaff
}
}

View File

@@ -1,35 +0,0 @@
mutation CreateTeamHub(
$teamId: UUID!,
$hubName: String!,
$departments: String
) @auth(level: USER) {
teamHub_insert(
data: {
teamId: $teamId
hubName: $hubName
departments: $departments
}
)
}
mutation UpdateTeamHub(
$id: UUID!,
$teamId: UUID,
$hubName: String,
$departments: String
) @auth(level: USER) {
teamHub_update(
id: $id,
data: {
teamId: $teamId
hubName: $hubName
departments: $departments
}
)
}
mutation DeleteTeamHub(
$id: UUID!
) @auth(level: USER) {
teamHub_delete(id: $id)
}

View File

@@ -1,36 +0,0 @@
query listTeamHub @auth(level: USER) {
teamHubs {
id
teamId
hubName
departments
}
}
query getTeamHubById(
$id: UUID!
) @auth(level: USER) {
teamHub(id: $id) {
id
teamId
hubName
departments
}
}
query filterTeamHub(
$teamId: UUID,
$hubName: String
) @auth(level: USER) {
teamHubs(
where: {
teamId: { eq: $teamId }
hubName: { eq: $hubName }
}
) {
id
teamId
hubName
departments
}
}

View File

@@ -1,43 +0,0 @@
mutation CreateTeamMember(
$teamId: UUID!,
$memberName: String!,
$email: String!,
$role: TeamMemberRole,
$isActive: Boolean
) @auth(level: USER) {
teamMember_insert(
data: {
teamId: $teamId
memberName: $memberName
email: $email
role: $role
isActive: $isActive
}
)
}
mutation UpdateTeamMember(
$id: UUID!,
$teamId: UUID,
$memberName: String,
$email: String,
$role: TeamMemberRole,
$isActive: Boolean
) @auth(level: USER) {
teamMember_update(
id: $id,
data: {
teamId: $teamId
memberName: $memberName
email: $email
role: $role
isActive: $isActive
}
)
}
mutation DeleteTeamMember(
$id: UUID!
) @auth(level: USER) {
teamMember_delete(id: $id)
}

View File

@@ -1,48 +0,0 @@
query listTeamMember @auth(level: USER) {
teamMembers {
id
teamId
memberName
email
role
isActive
}
}
query getTeamMemberById(
$id: UUID!
) @auth(level: USER) {
teamMember(id: $id) {
id
teamId
memberName
email
role
isActive
}
}
query filterTeamMember(
$teamId: UUID,
$memberName: String,
$email: String,
$role: TeamMemberRole,
$isActive: Boolean
) @auth(level: USER) {
teamMembers(
where: {
teamId: { eq: $teamId }
memberName: { eq: $memberName }
email: { eq: $email }
role: { eq: $role }
isActive: { eq: $isActive }
}
) {
id
teamId
memberName
email
role
isActive
}
}

View File

@@ -1,36 +0,0 @@
mutation CreateTeamMemberInvite(
$teamId: UUID!,
$email: String!,
$inviteStatus: TeamMemberInviteStatus!
) @auth(level: USER) {
teamMemberInvite_insert(
data: {
teamId: $teamId
email: $email
inviteStatus: $inviteStatus
# inviteCode se genera por default con uuidV4()
}
)
}
mutation UpdateTeamMemberInvite(
$id: UUID!,
$teamId: UUID,
$email: String,
$inviteStatus: TeamMemberInviteStatus
) @auth(level: USER) {
teamMemberInvite_update(
id: $id,
data: {
teamId: $teamId
email: $email
inviteStatus: $inviteStatus
}
)
}
mutation DeleteTeamMemberInvite(
$id: UUID!
) @auth(level: USER) {
teamMemberInvite_delete(id: $id)
}

View File

@@ -1,41 +0,0 @@
query listTeamMemberInvite @auth(level: USER) {
teamMemberInvites {
id
teamId
inviteCode
email
inviteStatus
}
}
query getTeamMemberInviteById(
$id: UUID!
) @auth(level: USER) {
teamMemberInvite(id: $id) {
id
teamId
inviteCode
email
inviteStatus
}
}
query filterTeamMemberInvite(
$teamId: UUID,
$email: String,
$inviteStatus: TeamMemberInviteStatus
) @auth(level: USER) {
teamMemberInvites(
where: {
teamId: { eq: $teamId }
email: { eq: $email }
inviteStatus: { eq: $inviteStatus }
}
) {
id
teamId
inviteCode
email
inviteStatus
}
}

View File

@@ -1,45 +0,0 @@
mutation CreateUser(
$id: String!, # Firebase UID
$email: String!,
$fullName: String!,
$role: UserBaseRole!,
$userRole: String,
$photoUrl: String
) @auth(level: USER) {
user_insert(
data: {
id: $id
email: $email
fullName: $fullName
role: $role
userRole: $userRole
photoUrl: $photoUrl
}
)
}
mutation UpdateUser(
$id: String!,
$email: String,
$fullName: String,
$role: UserBaseRole,
$userRole: String,
$photoUrl: String
) @auth(level: USER) {
user_update(
id: $id,
data: {
email: $email
fullName: $fullName
role: $role
userRole: $userRole
photoUrl: $photoUrl
}
)
}
mutation DeleteUser(
$id: String!
) @auth(level: USER) {
user_delete(id: $id)
}

View File

@@ -1,48 +0,0 @@
query listUsers @auth(level: USER) {
users {
id
email
fullName
role
userRole
photoUrl
createdDate
updatedDate
}
}
query getUserById(
$id: String!
) @auth(level: USER) {
user(id: $id) {
id
email
fullName
role
userRole
photoUrl
}
}
query filterUsers(
$id: String,
$email: String,
$role: UserBaseRole,
$userRole: String
) @auth(level: USER) {
users(
where: {
id: { eq: $id }
email: { eq: $email }
role: { eq: $role }
userRole: { eq: $userRole }
}
) {
id
email
fullName
role
userRole
photoUrl
}
}

View File

@@ -1,52 +0,0 @@
mutation CreateVendor(
$vendorNumber: String!,
$legalName: String!,
$region: VendorRegion!,
$platformType: VendorPlatformType!,
$primaryContactEmail: String!,
$approvalStatus: VendorApprovalStatus!,
$isActive: Boolean
) @auth(level: USER) {
vendor_insert(
data: {
vendorNumber: $vendorNumber
legalName: $legalName
region: $region
platformType: $platformType
primaryContactEmail: $primaryContactEmail
approvalStatus: $approvalStatus
isActive: $isActive
}
)
}
mutation UpdateVendor(
$id: UUID!,
$vendorNumber: String,
$legalName: String,
$region: VendorRegion,
$platformType: VendorPlatformType,
$primaryContactEmail: String,
$approvalStatus: VendorApprovalStatus,
$isActive: Boolean
) @auth(level: USER) {
vendor_update(
id: $id,
data: {
vendorNumber: $vendorNumber
legalName: $legalName
region: $region
platformType: $platformType
primaryContactEmail: $primaryContactEmail
approvalStatus: $approvalStatus
isActive: $isActive
}
)
}
mutation DeleteVendor(
$id: UUID!
) @auth(level: USER) {
vendor_delete(id: $id)
}

View File

@@ -1,57 +0,0 @@
query listVendor @auth(level: USER) {
vendors {
id
vendorNumber
legalName
region
platformType
primaryContactEmail
approvalStatus
isActive
}
}
query getVendorById(
$id: UUID!
) @auth(level: USER) {
vendor(id: $id) {
id
vendorNumber
legalName
region
platformType
primaryContactEmail
approvalStatus
isActive
}
}
query filterVendors(
$region: VendorRegion,
$approvalStatus: VendorApprovalStatus,
$isActive: Boolean,
$vendorNumber:String,
$primaryContactEmail:String,
$legalName: String,
$platformType: VendorPlatformType
) @auth(level: USER) {
vendors(where:{
region: { eq: $region }
approvalStatus: { eq: $approvalStatus }
isActive: { eq: $isActive }
vendorNumber: { eq: $vendorNumber }
primaryContactEmail: { eq: $primaryContactEmail }
legalName: { eq: $legalName }
platformType: { eq: $platformType }
}) {
id
vendorNumber
legalName
region
platformType
primaryContactEmail
approvalStatus
isActive
}
}

View File

@@ -1,35 +0,0 @@
mutation CreateVendorDefaultSetting(
$vendorName: String!,
$defaultMarkupPercentage: Float!,
$defaultVendorFeePercentage: Float!
) @auth(level: USER) {
vendorDefaultSetting_insert(
data: {
vendorName: $vendorName
defaultMarkupPercentage: $defaultMarkupPercentage
defaultVendorFeePercentage: $defaultVendorFeePercentage
}
)
}
mutation UpdateVendorDefaultSetting(
$id: UUID!,
$vendorName: String,
$defaultMarkupPercentage: Float,
$defaultVendorFeePercentage: Float
) @auth(level: USER) {
vendorDefaultSetting_update(
id: $id,
data: {
vendorName: $vendorName
defaultMarkupPercentage: $defaultMarkupPercentage
defaultVendorFeePercentage: $defaultVendorFeePercentage
}
)
}
mutation DeleteVendorDefaultSetting(
$id: UUID!
) @auth(level: USER) {
vendorDefaultSetting_delete(id: $id)
}

View File

@@ -1,38 +0,0 @@
query listVendorDefaultSettings @auth(level: USER) {
vendorDefaultSettings {
id
vendorName
defaultMarkupPercentage
defaultVendorFeePercentage
}
}
query getVendorDefaultSettingById(
$id: UUID!
) @auth(level: USER) {
vendorDefaultSetting(id: $id) {
id
vendorName
defaultMarkupPercentage
defaultVendorFeePercentage
}
}
query filterVendorDefaultSettings(
$vendorName: String,
$defaultMarkupPercentage: Float,
$defaultVendorFeePercentage: Float
) @auth(level: USER) {
vendorDefaultSettings(
where: {
vendorName: { eq: $vendorName }
defaultMarkupPercentage: { eq: $defaultMarkupPercentage }
defaultVendorFeePercentage: { eq: $defaultVendorFeePercentage }
}
) {
id
vendorName
defaultMarkupPercentage
defaultVendorFeePercentage
}
}

View File

@@ -1,51 +0,0 @@
mutation CreateVendorRate(
$vendorName: String!,
$category: VendorRateCategory!,
$roleName: String!,
$employeeWage: Float!,
$markupPercentage: Float,
$vendorFeePercentage: Float,
$clientRate: Float!
) @auth(level: USER) {
vendorRate_insert(
data: {
vendorName: $vendorName
category: $category
roleName: $roleName
employeeWage: $employeeWage
markupPercentage: $markupPercentage
vendorFeePercentage: $vendorFeePercentage
clientRate: $clientRate
}
)
}
mutation UpdateVendorRate(
$id: UUID!,
$vendorName: String,
$category: VendorRateCategory,
$roleName: String,
$employeeWage: Float,
$markupPercentage: Float,
$vendorFeePercentage: Float,
$clientRate: Float
) @auth(level: USER) {
vendorRate_update(
id: $id,
data: {
vendorName: $vendorName
category: $category
roleName: $roleName
employeeWage: $employeeWage
markupPercentage: $markupPercentage
vendorFeePercentage: $vendorFeePercentage
clientRate: $clientRate
}
)
}
mutation DeleteVendorRate(
$id: UUID!
) @auth(level: USER) {
vendorRate_delete(id: $id)
}

View File

@@ -1,56 +0,0 @@
query listVendorRate @auth(level: USER) {
vendorRates {
id
vendorName
category
roleName
employeeWage
markupPercentage
vendorFeePercentage
clientRate
}
}
query getVendorRateById(
$id: UUID!
) @auth(level: USER) {
vendorRate(id: $id) {
id
vendorName
category
roleName
employeeWage
markupPercentage
vendorFeePercentage
clientRate
createdDate
updatedDate
createdBy
}
}
query filterVendorRates(
$vendorName: String,
$category: VendorRateCategory,
$roleName: String,
$minClientRate: Float,
$maxClientRate: Float
) @auth(level: USER) {
vendorRates(
where: {
vendorName: { eq: $vendorName }
category: { eq: $category }
roleName: { eq: $roleName }
clientRate: { ge: $minClientRate, le: $maxClientRate }
}
) {
id
vendorName
category
roleName
employeeWage
markupPercentage
vendorFeePercentage
clientRate
}
}

View File

@@ -1,43 +0,0 @@
mutation CreateWorkforce(
$workforceNumber: String!,
$vendorId: UUID!,
$firstName: String!,
$lastName: String!,
$employmentType: WorkforceEmploymentType
) @auth(level: USER) {
workforce_insert(
data: {
workforceNumber: $workforceNumber
vendorId: $vendorId
firstName: $firstName
lastName: $lastName
employmentType: $employmentType
}
)
}
mutation UpdateWorkforce(
$id: UUID!,
$workforceNumber: String,
$vendorId: UUID,
$firstName: String,
$lastName: String,
$employmentType: WorkforceEmploymentType
) @auth(level: USER) {
workforce_update(
id: $id,
data: {
workforceNumber: $workforceNumber
vendorId: $vendorId
firstName: $firstName
lastName: $lastName
employmentType: $employmentType
}
)
}
mutation DeleteWorkforce(
$id: UUID!
) @auth(level: USER) {
workforce_delete(id: $id)
}

View File

@@ -1,48 +0,0 @@
query listWorkforce @auth(level: USER) {
workforces {
id
workforceNumber
vendorId
firstName
lastName
employmentType
}
}
query getWorkforceById(
$id: UUID!
) @auth(level: USER) {
workforce(id: $id) {
id
workforceNumber
vendorId
firstName
lastName
employmentType
}
}
query filterWorkforce(
$workforceNumber: String,
$vendorId: UUID,
$firstName: String,
$lastName: String,
$employmentType: WorkforceEmploymentType
) @auth(level: USER) {
workforces(
where: {
workforceNumber: { eq: $workforceNumber }
vendorId: { eq: $vendorId }
firstName: { eq: $firstName }
lastName: { eq: $lastName }
employmentType: { eq: $employmentType }
}
) {
id
workforceNumber
vendorId
firstName
lastName
employmentType
}
}

View File

@@ -1,13 +0,0 @@
specVersion: "v1"
serviceId: "krow-workforce-db"
location: "us-central1"
schema:
source: "./schema"
datasource:
postgresql:
database: "krow_db"
cloudSql:
instanceId: "krow-sql"
# schemaValidation: "STRICT" # STRICT mode makes Postgres schema match Data Connect exactly.
# schemaValidation: "COMPATIBLE" # COMPATIBLE mode makes Postgres schema compatible with Data Connect.
connectorDirs: ["./connector"]

View File

@@ -1,8 +0,0 @@
connectorId: example
generate:
javascriptSdk:
- outputDir: ../../frontend-web/src/dataconnect-generated
package: "@dataconnect/generated"
packageJsonDir: ../../frontend-web
react: true
angular: false

View File

@@ -1,33 +0,0 @@
# Example mutations for a simple movie app
# Create a movie based on user input
mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!)
@auth(level: USER_EMAIL_VERIFIED, insecureReason: "Any email verified users can create a new movie.") {
movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl })
}
# Upsert (update or insert) a user's username based on their auth.uid
mutation UpsertUser($username: String!) @auth(level: USER) {
# The "auth.uid" server value ensures that users can only register their own user.
user_upsert(data: { id_expr: "auth.uid", username: $username })
}
# Add a review for a movie
mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
@auth(level: USER) {
review_upsert(
data: {
userId_expr: "auth.uid"
movieId: $movieId
rating: $rating
reviewText: $reviewText
# reviewDate defaults to today in the schema. No need to set it manually.
}
)
}
# Logged in user can delete their review for a movie
mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
# The "auth.uid" server value ensures that users can only delete their own reviews.
review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}

View File

@@ -1,78 +0,0 @@
# Example queries for a simple movie app.
# @auth() directives control who can call each operation.
# Anyone should be able to list all movies, so the auth level is set to PUBLIC
query ListMovies @auth(level: PUBLIC, insecureReason: "Anyone can list all movies.") {
movies {
id
title
imageUrl
genre
}
}
# List all users, only admins should be able to list all users, so we use NO_ACCESS
query ListUsers @auth(level: NO_ACCESS) {
users {
id
username
}
}
# Logged in users can list all their reviews and movie titles associated with the review
# Since the query uses the uid of the current authenticated user, we set auth level to USER
query ListUserReviews @auth(level: USER) {
user(key: { id_expr: "auth.uid" }) {
id
username
# <field>_on_<foreign_key_field> makes it easy to grab info from another table
# Here, we use it to grab all the reviews written by the user.
reviews: reviews_on_user {
rating
reviewDate
reviewText
movie {
id
title
}
}
}
}
# Get movie by id
query GetMovieById($id: UUID!) @auth(level: PUBLIC, insecureReason: "Anyone can get a movie by id.") {
movie(id: $id) {
id
title
imageUrl
genre
metadata: movieMetadata_on_movie {
rating
releaseYear
description
}
reviews: reviews_on_movie {
reviewText
reviewDate
rating
user {
id
username
}
}
}
}
# Search for movies, actors, and reviews
query SearchMovie($titleInput: String, $genre: String) @auth(level: PUBLIC, insecureReason: "Anyone can search for movies.") {
movies(
where: {
_and: [{ genre: { eq: $genre } }, { title: { contains: $titleInput } }]
}
) {
id
title
genre
imageUrl
}
}

View File

@@ -1,52 +0,0 @@
# Example schema for simple movie review app
# User table is keyed by Firebase Auth UID.
type User @table {
# `@default(expr: "auth.uid")` sets it to Firebase Auth UID during insert and upsert.
id: String! @default(expr: "auth.uid")
username: String! @col(dataType: "varchar(50)")
# The `user: User!` field in the Review table generates the following one-to-many query field.
# reviews_on_user: [Review!]!
# The `Review` join table the following many-to-many query field.
# movies_via_Review: [Movie!]!
}
# Movie is keyed by a randomly generated UUID.
type Movie @table {
# If you do not pass a 'key' to `@table`, Data Connect automatically adds the following 'id' column.
# Feel free to uncomment and customize it.
# id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
genre: String
}
# MovieMetadata is a metadata attached to a Movie.
# Movie <-> MovieMetadata is a one-to-one relationship
type MovieMetadata @table {
# @unique ensures each Movie can only one MovieMetadata.
movie: Movie! @unique
# The movie field adds the following foreign key field. Feel free to uncomment and customize it.
# movieId: UUID!
rating: Float
releaseYear: Int
description: String
}
# Reviews is a join table between User and Movie.
# It has a composite primary keys `userUid` and `movieId`.
# A user can leave reviews for many movies. A movie can have reviews from many users.
# User <-> Review is a one-to-many relationship
# Movie <-> Review is a one-to-many relationship
# Movie <-> User is a many-to-many relationship
type Review @table(name: "Reviews", key: ["movie", "user"]) {
user: User!
# The user field adds the following foreign key field. Feel free to uncomment and customize it.
# userUid: String!
movie: Movie!
# The movie field adds the following foreign key field. Feel free to uncomment and customize it.
# movieId: UUID!
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}

View File

@@ -1,25 +0,0 @@
enum VendorRateCategory {
KITCHEN_AND_CULINARY
CONCESSIONS
FACILITIES
BARTENDING
SECURITY
EVENT_STAFF
MANAGEMENT
TECHNICAL
OTHER
}
type VendorRate @table(name: "vendor_rates") {
id: UUID! @default(expr: "uuidV4()")
vendorName: String!
category: VendorRateCategory!
roleName: String!
employeeWage: Float!
markupPercentage: Float
vendorFeePercentage: Float
clientRate: Float!
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,23 +0,0 @@
enum ActivityType {
EVENT_CREATED
EVENT_UPDATED
STAFF_ASSIGNED
INVOICE_PAID
INVOICE_CREATED
VENDOR_APPROVED
MESSAGE_RECEIVED
}
type ActivityLog @table(name: "activity_logs") {
id: UUID! @default(expr: "uuidV4()")
title: String!
description: String!
activityType: ActivityType!
userId: String! # user_id (FK lógica a User.id)
isRead: Boolean @default(expr: "false")
iconType: String
iconColor: String
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,23 +0,0 @@
enum AssignmentStatus {
PENDING
CONFIRMED
CHECKED_IN
IN_PROGRESS
COMPLETED
CANCELLED
NO_SHOW
}
type Assignment @table(name: "assignments") {
id: UUID! @default(expr: "uuidV4()")
assignmentNumber: String
orderId: UUID! # order_id (FK lógica a Order.id)
workforceId: UUID! # workforce_id (FK lógica a Workforce.id)
vendorId: UUID! # vendor_id (FK lógica a Vendor.id)
role: String!
assignmentStatus: AssignmentStatus!
scheduledStart: Timestamp!
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,34 +0,0 @@
enum BusinessSector {
BON_APPETIT
EUREST
ARAMARK
EPICUREAN_GROUP
CHARTWELLS
OTHER
}
enum BusinessRateGroup {
STANDARD
PREMIUM
ENTERPRISE
CUSTOM
}
enum BusinessStatus {
ACTIVE
INACTIVE
PENDING
}
type Business @table(name: "business") {
id: UUID! @default(expr: "uuidV4()")
businessName: String!
contactName: String!
email: String
sector: BusinessSector
rateGroup: BusinessRateGroup!
status: BusinessStatus
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,37 +0,0 @@
enum CertificationType {
LEGAL
OPERATIONAL
SAFETY
TRAINING
LICENSE
OTHER
}
enum CertificationStatus {
CURRENT
EXPIRING_SOON
EXPIRED
PENDING_VALIDATION
}
enum CertificationValidationStatus {
APPROVED
PENDING_EXPERT_REVIEW
REJECTED
AI_VERIFIED
AI_FLAGGED
MANUAL_REVIEW_NEEDED
}
type Certification @table(name: "certification") {
id: UUID! @default(expr: "uuidV4()")
employeeName: String!
certificationName: String!
certificationType: CertificationType
status: CertificationStatus
expiryDate: String!
validationStatus: CertificationValidationStatus
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,26 +0,0 @@
enum ConversationType {
CLIENT_VENDOR
STAFF_CLIENT
STAFF_ADMIN
VENDOR_ADMIN
CLIENT_ADMIN
GROUP_STAFF
GROUP_EVENT_STAFF
}
enum ConversationStatus {
ACTIVE
ARCHIVED
CLOSED
}
type Conversation @table(name: "conversations") {
id: UUID! @default(expr: "uuidV4()")
participants: String! # participants (jsonb -> String, required array of strings)
conversationType: ConversationType!
relatedTo: UUID! # related_to (generic FK as string)
status: ConversationStatus
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,9 +0,0 @@
type Enterprise @table(name: "enterprise") {
id: UUID! @default(expr: "uuidV4()")
enterpriseNumber: String!
enterpriseName: String!
enterpriseCode: String!
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,84 +0,0 @@
enum EventStatus {
DRAFT
ACTIVE
PENDING
ASSIGNED
CONFIRMED
COMPLETED
CANCELED
PARTIAL
PARTIAL_STAFFED
FULLY_STAFFED
EVENT_CREATED
}
enum RecurrenceType {
SINGLE
DATE_RANGE
SCATTER
}
#enums cant start by a number, reason of C1099
enum ContractType {
W2
C1099
TEMP
CONTRACT
}
type Event @table(name: "events") {
id: UUID! @default(expr: "uuidV4()")
eventName: String!
isRapid: Boolean @default(expr: "false")
isRecurring: Boolean @default(expr: "false")
isMultiDay: Boolean @default(expr: "false")
recurrenceType: RecurrenceType
recurrenceStartDate: Timestamp
recurrenceEndDate: Timestamp
scatterDates: Any
multiDayStartDate: Timestamp
multiDayEndDate: Timestamp
bufferTimeBefore: Float @default(expr: "0")
bufferTimeAfter: Float @default(expr: "0")
conflictDetectionEnabled: Boolean @default(expr: "true")
detectedConflicts: Any
businessId: UUID!
businessName: String
vendorId: String @default(expr: "auth.uid")
vendorName: String
hub: String
eventLocation: String
contractType: ContractType
poReference: String
status: EventStatus!
date: String!
shifts: Any
addons: Any
total: Float
clientName: String
clientEmail: String
clientPhone: String
invoiceId: UUID
notes: String
requested: Int @default(expr: "0")
assignedStaff: Any
orderType: String
department: String
recurringStartDate: String
recurringEndDate: String
recurringDays: Any
permanentStartDate: String
permanentDays: Any
includeBackup: Boolean
backupStaffCount: Int
recurringTime: String
permanentTime: String
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

View File

@@ -1,26 +0,0 @@
enum InvoiceStatus {
DRAFT
PENDING_REVIEW
APPROVED
DISPUTED
UNDER_REVIEW
RESOLVED
OVERDUE
PAID
RECONCILED
CANCELLED
}
type Invoice @table(name: "invoices") {
id: UUID! @default(expr: "uuidV4()")
invoiceNumber: String!
amount: Float!
status: InvoiceStatus!
issueDate: Timestamp!
dueDate: Timestamp!
disputedItems: String
isAutoGenerated: Boolean @default(expr: "false") # is_auto_generated
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}

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