Merge dev
This commit is contained in:
367
docs/BACKEND/API_GUIDES/00-initial-api-contracts.md
Normal file
367
docs/BACKEND/API_GUIDES/00-initial-api-contracts.md
Normal file
@@ -0,0 +1,367 @@
|
||||
# KROW Workforce API Contracts
|
||||
|
||||
This document captures all API contracts used by the Staff and Client mobile applications. The application backend is powered by **Firebase Data Connect (GraphQL)**, so traditional REST endpoints do not exist natively. For clarity and ease of reading for all engineering team members, the tables below formulate these GraphQL Data Connect queries and mutations into their **Conceptual REST Endpoints** alongside the actual **Data Connect Operation Name**.
|
||||
|
||||
---
|
||||
|
||||
## Staff Application
|
||||
|
||||
### Authentication / Onboarding Pages
|
||||
*(Pages: get_started_page.dart, intro_page.dart, phone_verification_page.dart, profile_setup_page.dart)*
|
||||
|
||||
#### Setup / User Validation API
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /users/{id}` |
|
||||
| **Data Connect OP** | `getUserById` |
|
||||
| **Purpose** | Retrieves the base user profile to determine authentication status and role access (e.g., if the user is STAFF). |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `id: UUID!` (Firebase UID) |
|
||||
| **Outputs** | `User { id, email, phone, role }` |
|
||||
| **Notes** | Required after OTP verification to route users appropriately. |
|
||||
|
||||
#### Create Default User API
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `POST /users` |
|
||||
| **Data Connect OP** | `createUser` |
|
||||
| **Purpose** | Inserts a base user record into the system during initial signup. |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `id: UUID!`, `role: UserBaseRole` |
|
||||
| **Outputs** | `id` of newly created User |
|
||||
| **Notes** | Used explicitly during the "Sign Up" flow if the user doesn't physically exist in the database. |
|
||||
|
||||
#### Get Staff Profile API
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /staff/user/{userId}` |
|
||||
| **Data Connect OP** | `getStaffByUserId` |
|
||||
| **Purpose** | Finds the specific Staff record associated with the base user ID. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `userId: UUID!` |
|
||||
| **Outputs** | `Staffs { id, userId, fullName, email, phone, photoUrl, status }` |
|
||||
| **Notes** | Needed to verify if a complete staff profile exists before allowing navigation to the main app dashboard. |
|
||||
|
||||
#### Update Staff Profile API
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `PUT /staff/{id}` |
|
||||
| **Data Connect OP** | `updateStaff` |
|
||||
| **Purpose** | Saves onboarding data across Personal Info, Experience, and Preferred Locations pages. |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `id: UUID!`, `fullName`, `email`, `phone`, `address`, etc. |
|
||||
| **Outputs** | `id` |
|
||||
| **Notes** | Called incrementally during the profile setup wizard as the user fills out step-by-step information. |
|
||||
|
||||
### Home Page & Benefits Overview
|
||||
*(Pages: worker_home_page.dart, benefits_overview_page.dart)*
|
||||
|
||||
#### Load Today/Tomorrow Shifts
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /staff/{staffId}/applications` |
|
||||
| **Data Connect OP** | `getApplicationsByStaffId` |
|
||||
| **Purpose** | Retrieves applications (shifts) assigned to the current staff member within a specific date range. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `staffId: UUID!`, `dayStart: Timestamp`, `dayEnd: Timestamp` |
|
||||
| **Outputs** | `Applications { shift, shiftRole, status, createdAt }` |
|
||||
| **Notes** | The frontend filters the query response for `CONFIRMED` applications to successfully display "Today's" and "Tomorrow's" shifts. |
|
||||
|
||||
#### List Recommended Shifts
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /shifts/recommended` |
|
||||
| **Data Connect OP** | `listShifts` |
|
||||
| **Purpose** | Fetches open shifts that are available for the staff to apply to. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | None directly mapped on load, but fetches available items logically. |
|
||||
| **Outputs** | `Shifts { id, title, orderId, cost, location, startTime, endTime, status }` |
|
||||
| **Notes** | Limits output to 10 on the frontend. Should ideally rely on an active backend `$status: OPEN` parameter. |
|
||||
|
||||
#### Benefits Summary API
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /staff/{staffId}/benefits` |
|
||||
| **Data Connect OP** | `listBenefitsDataByStaffId` |
|
||||
| **Purpose** | Retrieves accrued benefits (e.g., Sick time, Vacation) to display gracefully on the home screen. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `staffId: UUID!` |
|
||||
| **Outputs** | `BenefitsDatas { vendorBenefitPlan { title, total }, current }` |
|
||||
| **Notes** | Used by `benefits_overview_page.dart`. Derives available metrics via `usedHours = total - current`. |
|
||||
|
||||
### Find Shifts / Shift Details Pages
|
||||
*(Pages: shifts_page.dart, shift_details_page.dart)*
|
||||
|
||||
#### List Available Shifts Filtered
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /shifts` |
|
||||
| **Data Connect OP** | `filterShifts` |
|
||||
| **Purpose** | Used to fetch Open Shifts in specific regions when the worker searches in the "Find Shifts" tab. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `$status: ShiftStatus`, `$dateFrom: Timestamp`, `$dateTo: Timestamp` |
|
||||
| **Outputs** | `Shifts { id, title, location, cost, durationDays, order { business, vendor } }` |
|
||||
| **Notes** | Main driver for discovering available work. |
|
||||
|
||||
#### Get Shift Details
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /shifts/{id}` |
|
||||
| **Data Connect OP** | `getShiftById` |
|
||||
| **Purpose** | Gets deeper details for a single shift including exact uniform requirements and managers. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `id: UUID!` |
|
||||
| **Outputs** | `Shift { id, title, hours, cost, locationAddress, workersNeeded ... }` |
|
||||
| **Notes** | Invoked when users click into a full `shift_details_page.dart`. |
|
||||
|
||||
#### Apply To Shift
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `POST /applications` |
|
||||
| **Data Connect OP** | `createApplication` |
|
||||
| **Purpose** | Worker submits an intent to take an open shift (creates an application record). |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `shiftId: UUID!`, `staffId: UUID!`, `roleId: UUID!`, `status: ApplicationStatus!` (e.g. `PENDING` or `CONFIRMED`), `origin: ApplicationOrigin!` (e.g. `STAFF`); optional: `checkInTime`, `checkOutTime` |
|
||||
| **Outputs** | `application_insert.id` (Application ID) |
|
||||
| **Notes** | The app uses `status: CONFIRMED` and `origin: STAFF` when claiming; backend also supports `PENDING` for admin review flows. After creation, shift-role assigned count and shift filled count are updated. |
|
||||
|
||||
### Availability Page
|
||||
*(Pages: availability_page.dart)*
|
||||
|
||||
#### Get Default Availability
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /staff/{staffId}/availabilities` |
|
||||
| **Data Connect OP** | `listStaffAvailabilitiesByStaffId` |
|
||||
| **Purpose** | Fetches the standard Mon-Sun recurring availability for a staff member. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `staffId: UUID!` |
|
||||
| **Outputs** | `StaffAvailabilities { dayOfWeek, isAvailable, startTime, endTime }` |
|
||||
| **Notes** | Bound to Monday through Sunday configuration. |
|
||||
|
||||
#### Update Availability
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `PUT /staff/availabilities/{id}` |
|
||||
| **Data Connect OP** | `updateStaffAvailability` (or `createStaffAvailability` for new entries) |
|
||||
| **Purpose** | Upserts availability preferences. |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `staffId`, `dayOfWeek`, `isAvailable`, `startTime`, `endTime` |
|
||||
| **Outputs** | `id` |
|
||||
| **Notes** | Called individually per day edited. |
|
||||
|
||||
### Payments Page
|
||||
*(Pages: payments_page.dart, early_pay_page.dart)*
|
||||
|
||||
#### Get Recent Payments
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /staff/{staffId}/payments` |
|
||||
| **Data Connect OP** | `listRecentPaymentsByStaffId` |
|
||||
| **Purpose** | Loads the history of earnings and timesheets completed by the staff. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `staffId: UUID!` |
|
||||
| **Outputs** | `Payments { amount, processDate, shiftId, status }` |
|
||||
| **Notes** | Displays historical metrics under the comprehensive Earnings tab. |
|
||||
|
||||
### Compliance / Profiles
|
||||
*(Pages: certificates_page.dart, documents_page.dart, tax_forms_page.dart, form_i9_page.dart, form_w4_page.dart)*
|
||||
|
||||
#### Get Tax Forms
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /staff/{staffId}/tax-forms` |
|
||||
| **Data Connect OP** | `getTaxFormsByStaffId` |
|
||||
| **Purpose** | Check the filing status and detailed inputs of I9 and W4 forms. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `staffId: UUID!` |
|
||||
| **Outputs** | `TaxForms { formType, isCompleted, updatedDate }` |
|
||||
| **Notes** | Crucial requirement for staff to be eligible to apply for highly regulated shifts. |
|
||||
|
||||
#### Update Tax Forms
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `PUT /tax-forms/{id}` |
|
||||
| **Data Connect OP** | `updateTaxForm` |
|
||||
| **Purpose** | Submits state and filing for the given tax form type (W4/I9). |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `id`, `dataPoints...` |
|
||||
| **Outputs** | `id` |
|
||||
| **Notes** | Modifies the core compliance state variables directly. |
|
||||
|
||||
---
|
||||
|
||||
## Client Application
|
||||
|
||||
### Authentication / Intro
|
||||
*(Pages: client_sign_in_page.dart, client_get_started_page.dart)*
|
||||
|
||||
#### Client User Validation API
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /users/{id}` |
|
||||
| **Data Connect OP** | `getUserById` |
|
||||
| **Purpose** | Retrieves the base user profile to determine authentication status and role access (ensuring user is BUSINESS). |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `id: UUID!` (Firebase UID) |
|
||||
| **Outputs** | `User { id, email, phone, userRole }` |
|
||||
| **Notes** | Validates against conditional statements checking `userRole == BUSINESS` or `BOTH`. |
|
||||
|
||||
#### Get Businesses By User API
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/user/{userId}` |
|
||||
| **Data Connect OP** | `getBusinessesByUserId` |
|
||||
| **Purpose** | Maps the authenticated user to their client business context. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `userId: String!` |
|
||||
| **Outputs** | `Businesses { id, businessName, email, contactName }` |
|
||||
| **Notes** | Dictates the working scopes (Business ID) across the entire application lifecycle and binds the user. |
|
||||
|
||||
### Hubs Page
|
||||
*(Pages: client_hubs_page.dart, edit_hub_page.dart, hub_details_page.dart)*
|
||||
|
||||
#### List Hubs by Team
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /teams/{teamId}/hubs` |
|
||||
| **Data Connect OP** | `getTeamHubsByTeamId` |
|
||||
| **Purpose** | Fetches the primary working sites (Hubs) for a client context by using Team mapping. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `teamId: UUID!` |
|
||||
| **Outputs** | `TeamHubs { id, hubName, address, managerName, isActive }` |
|
||||
| **Notes** | `teamId` is derived first from `getTeamsByOwnerId(ownerId: businessId)`. |
|
||||
|
||||
#### Create / Update / Delete Hub
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `POST /team-hubs` / `PUT /team-hubs/{id}` / `DELETE /team-hubs/{id}` |
|
||||
| **Data Connect OP** | `createTeamHub` / `updateTeamHub` / `deleteTeamHub` |
|
||||
| **Purpose** | Provisions, Edits details directly, or Removes a Team Hub location. |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `id: UUID!`, optionally `hubName`, `address`, etc. |
|
||||
| **Outputs** | `id` |
|
||||
| **Notes** | Fired from `edit_hub_page.dart` mutations. |
|
||||
|
||||
### Orders Page
|
||||
*(Pages: create_order_page.dart, view_orders_page.dart, recurring_order_page.dart)*
|
||||
|
||||
#### Create Order
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `POST /orders` |
|
||||
| **Data Connect OP** | `createOrder` |
|
||||
| **Purpose** | Submits a new request for temporary staff requirements. |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `businessId`, `eventName`, `orderType`, `status` |
|
||||
| **Outputs** | `id` (Order ID) |
|
||||
| **Notes** | This explicitly invokes an order pipeline, meaning Shift instances are subsequently created through secondary mutations triggered after order instantiation. |
|
||||
|
||||
#### List Orders
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/orders` |
|
||||
| **Data Connect OP** | `listOrdersByBusinessId` |
|
||||
| **Purpose** | Retrieves all ongoing and past staff requests from the client. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!` |
|
||||
| **Outputs** | `Orders { id, eventName }` |
|
||||
| **Notes** | Populates the `view_orders_page.dart`. |
|
||||
|
||||
### Billing Pages
|
||||
*(Pages: billing_page.dart, pending_invoices_page.dart, completion_review_page.dart)*
|
||||
|
||||
#### List Invoices
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/invoices` |
|
||||
| **Data Connect OP** | `listInvoicesByBusinessId` |
|
||||
| **Purpose** | Fetches all invoices bound directly to the active business context (mapped directly in Firebase Schema). |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!` |
|
||||
| **Outputs** | `Invoices { id, amount, issueDate, status }` |
|
||||
| **Notes** | Used massively across all Billing view tabs. |
|
||||
|
||||
#### Mark / Dispute Invoice
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `PUT /invoices/{id}` |
|
||||
| **Data Connect OP** | `updateInvoice` |
|
||||
| **Purpose** | Actively marks an invoice as disputed or pays it directly (altering status). |
|
||||
| **Operation** | Mutation |
|
||||
| **Inputs** | `id: UUID!`, `status: InvoiceStatus` |
|
||||
| **Outputs** | `id` |
|
||||
| **Notes** | Disputing usually involves setting a `disputeReason` flag state dynamically via builder pattern. |
|
||||
|
||||
### Reports Page
|
||||
*(Pages: reports_page.dart, coverage_report_page.dart, performance_report_page.dart)*
|
||||
|
||||
#### Get Coverage Stats
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/coverage` |
|
||||
| **Data Connect OP** | `listShiftsForCoverage` |
|
||||
| **Purpose** | Provides data on Shifts grouped by Date for fulfillment calculations. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!`, `startDate: Timestamp!`, `endDate: Timestamp!` |
|
||||
| **Outputs** | `Shifts { id, date, workersNeeded, filled, status }` |
|
||||
| **Notes** | The frontend aggregates the raw backend rows to compose Coverage percentage natively. |
|
||||
|
||||
#### Get Daily Ops Stats
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/dailyops` |
|
||||
| **Data Connect OP** | `listShiftsForDailyOpsByBusiness` |
|
||||
| **Purpose** | Supplies current day operations and shift tracking progress. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!`, `date: Timestamp!` |
|
||||
| **Outputs** | `Shifts { id, title, location, workersNeeded, filled }` |
|
||||
| **Notes** | - |
|
||||
|
||||
#### Get Forecast Stats
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/forecast` |
|
||||
| **Data Connect OP** | `listShiftsForForecastByBusiness` |
|
||||
| **Purpose** | Retrieves scheduled future shifts to calculate financial run-rates. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!`, `startDate: Timestamp!`, `endDate: Timestamp!` |
|
||||
| **Outputs** | `Shifts { id, date, workersNeeded, hours, cost }` |
|
||||
| **Notes** | The App maps hours `x` cost to deliver Financial Dashboards. |
|
||||
|
||||
#### Get Performance KPIs
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/performance` |
|
||||
| **Data Connect OP** | `listShiftsForPerformanceByBusiness` |
|
||||
| **Purpose** | Fetches historical data allowing time-to-fill and completion-rate calculations. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!`, `startDate: Timestamp!`, `endDate: Timestamp!` |
|
||||
| **Outputs** | `Shifts { id, workersNeeded, filled, createdAt, filledAt }` |
|
||||
| **Notes** | Data Connect exposes timestamps so the App calculates `avgFillTimeHours`. |
|
||||
|
||||
#### Get No-Show Metrics
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/noshows` |
|
||||
| **Data Connect OP** | `listShiftsForNoShowRangeByBusiness` |
|
||||
| **Purpose** | Retrieves shifts where workers historically ghosted the platform. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!`, `startDate: Timestamp!`, `endDate: Timestamp!` |
|
||||
| **Outputs** | `Shifts { id, date }` |
|
||||
| **Notes** | Accompanies `listApplicationsForNoShowRange` cascading querying to generate full report. |
|
||||
|
||||
#### Get Spend Analytics
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| **Conceptual Endpoint** | `GET /business/{businessId}/spend` |
|
||||
| **Data Connect OP** | `listInvoicesForSpendByBusiness` |
|
||||
| **Purpose** | Detailed invoice aggregates for Spend metrics filtering. |
|
||||
| **Operation** | Query |
|
||||
| **Inputs** | `businessId: UUID!`, `startDate: Timestamp!`, `endDate: Timestamp!` |
|
||||
| **Outputs** | `Invoices { id, issueDate, dueDate, amount, status }` |
|
||||
| **Notes** | Used explicitly under the "Spend Report" graphings. |
|
||||
|
||||
---
|
||||
|
||||
*This document meticulously abstracts the underlying Data Connect Data-layer definitions implemented natively across the frontend. It maps the queries/mutations to recognizable REST equivalents for comprehensive and top-notch readability by external and internal developers alike.*
|
||||
@@ -0,0 +1,74 @@
|
||||
flowchart LR
|
||||
|
||||
subgraph C1["Login"]
|
||||
S_client_sign_in["client_sign_in_screen.dart"]
|
||||
S_client_sign_in --> S_client_sign_in_Q["Queries<br/>* user - getUserById<br/>* business - getBusinessesByUserId"]
|
||||
S_client_sign_in --> S_client_sign_in_F["Firebase<br/>* user - auth"]
|
||||
end
|
||||
|
||||
subgraph C2["Create account"]
|
||||
S_client_sign_up["client_sign_up_screen.dart"]
|
||||
S_client_sign_up --> S_client_sign_up_Q["Queries<br/>* business - getBusinessesByUserId"]
|
||||
S_client_sign_up --> S_client_sign_up_M["Mutations<br/>* user - createUser<br/>* business - createBusiness"]
|
||||
end
|
||||
|
||||
subgraph C3["Edit account"]
|
||||
S_edit_account_na["manual_or_unknown_screen.dart"]
|
||||
S_edit_account_na --> S_edit_account_na_M["Mutations<br/>* business - updateBusiness"]
|
||||
end
|
||||
|
||||
subgraph C4["Profile"]
|
||||
S_client_settings["client_settings_screen.dart"]
|
||||
S_client_settings --> S_client_settings_Q["Queries<br/>* user - getUserById<br/>* business - getBusinessesByUserId"]
|
||||
end
|
||||
|
||||
subgraph C5["Hubs"]
|
||||
S_client_hubs["client_hubs_screen.dart"]
|
||||
S_client_hubs --> S_client_hubs_Q["Queries<br/>* TeamHub - listTeamHubsByOwnerId"]
|
||||
S_client_hubs --> S_client_hubs_M["Mutations<br/>* TeamHub - createTeamHub<br/>* TeamHub - updateTeamHub<br/>* TeamHub - deleteTeamHub"]
|
||||
end
|
||||
|
||||
subgraph C6["Orders"]
|
||||
S_client_shifts["client_shifts_screen.dart"]
|
||||
S_client_shifts --> S_client_shifts_Q["Queries<br/>* order - getOrdersByBusinessId"]
|
||||
end
|
||||
|
||||
subgraph C7["RAPID Order"]
|
||||
S_rapid_order["rapid_order_flow_page.dart"]
|
||||
S_rapid_order --> S_rapid_order_M["Mutations<br/>* order - createOrder"]
|
||||
end
|
||||
|
||||
subgraph C8["One-time Order"]
|
||||
S_one_time["one_time_order_flow_page.dart"]
|
||||
S_one_time --> S_one_time_Q["Queries<br/>* role - listRolesByOwnerId"]
|
||||
S_one_time --> S_one_time_M["Mutations<br/>* ShiftRole - createShiftRole<br/>* order - createOrder"]
|
||||
end
|
||||
|
||||
subgraph C9["Permanent Placement"]
|
||||
S_permanent["permanent_order_flow_page.dart"]
|
||||
S_permanent --> S_permanent_Q["Queries<br/>* role - listRolesByOwnerId"]
|
||||
S_permanent --> S_permanent_M["Mutations<br/>* ShiftRole - createShiftRole<br/>* order - createOrder"]
|
||||
end
|
||||
|
||||
subgraph C10["Recurring Order"]
|
||||
S_recurring["recurring_order_flow_page.dart"]
|
||||
S_recurring --> S_recurring_Q["Queries<br/>* role - listRolesByOwnerId"]
|
||||
S_recurring --> S_recurring_M["Mutations<br/>* ShiftRole - createShiftRole<br/>* order - createOrder"]
|
||||
end
|
||||
|
||||
subgraph C11["Billing"]
|
||||
S_billing["client_billing_screen.dart"]
|
||||
S_billing --> S_billing_Q["Queries<br/>* account - getAccountsByOwnerId<br/>* invoice - listInvoicesByBusinessId<br/>* recentPayment - listRecentPaymentsByBusinessId"]
|
||||
S_billing --> S_billing_M["Mutations<br/>* account - createAccount<br/>* account - updateAccount<br/>* account - deleteAccount"]
|
||||
end
|
||||
|
||||
subgraph C12["Coverage"]
|
||||
S_coverage["coverage_dashboard.dart"]
|
||||
S_coverage --> S_coverage_Q["Queries<br/>* order - getOrdersByBusinessId<br/>* shift - getShiftsByBusinessId<br/>* application - getApplicationsByShiftId"]
|
||||
end
|
||||
|
||||
subgraph C13["Home"]
|
||||
S_client_home["client_home_screen.dart"]
|
||||
S_client_home --> S_client_home_Q["Queries<br/>* order - getOrdersByBusinessId<br/>* shift - getShiftsByBusinessId<br/>* application - getApplicationsByShiftId<br/>* recentPayment - listRecentPaymentsByBusinessId"]
|
||||
S_client_home --> S_client_home_M["Mutations<br/>* order - createOrder"]
|
||||
end
|
||||
@@ -0,0 +1,121 @@
|
||||
flowchart LR
|
||||
|
||||
subgraph L1["login/create user"]
|
||||
S_auth_phone["phone_verification_screen.dart"]
|
||||
S_auth_phone --> S_auth_phone_Q["Queries<br/>* user - getUserById<br/>* staff - getStaffByUserId"]
|
||||
S_auth_phone --> S_auth_phone_M["Mutations<br/>* user - createUser"]
|
||||
S_auth_phone --> S_auth_phone_F["Firebase<br/>* user - auth"]
|
||||
end
|
||||
|
||||
subgraph L2["Profile"]
|
||||
S_worker_profile["worker_profile_screen.dart"]
|
||||
S_worker_profile --> S_worker_profile_Q["Queries<br/>* user - getUserById<br/>* staff - getStaffByUserId"]
|
||||
end
|
||||
|
||||
subgraph L3["Personal info"]
|
||||
S_personal_info["personal_info_screen.dart"]
|
||||
S_personal_info --> S_personal_info_Q["Queries<br/>* staff - getStaffByUserId"]
|
||||
S_personal_info --> S_personal_info_M["Mutations<br/>* staff - UpdateStaff"]
|
||||
end
|
||||
|
||||
subgraph L4["Emergency Contact"]
|
||||
S_emergency["emergency_contact_screen.dart"]
|
||||
S_emergency --> S_emergency_Q["Queries<br/>* emergencyContact - getEmergencyContactsByStaffId"]
|
||||
S_emergency --> S_emergency_M["Mutations<br/>* conemergencyContacttact - updateEmergencyContact<br/>* emergencyContact - createEmergencyContact<br/>* contemergencyContactact - deleteEmergencyContact"]
|
||||
end
|
||||
|
||||
subgraph L5["Experience & skills"]
|
||||
S_experience["experience_screen.dart"]
|
||||
S_experience --> S_experience_Q["Queries<br/>* staff - getStaffByUserId"]
|
||||
S_experience --> S_experience_M["Mutations<br/>* staff - UpdateStaff"]
|
||||
end
|
||||
|
||||
subgraph L6["Attire"]
|
||||
S_attire["attire_screen.dart"]
|
||||
S_attire --> S_attire_Q["Queries<br/>* attireOption - filterAttireOptions<br/>* staff - getStaffByUserId"]
|
||||
S_attire --> S_attire_M["Mutations<br/>* staff - UpdateStaff"]
|
||||
end
|
||||
|
||||
subgraph L7["Documents"]
|
||||
S_documents["documents_screen.dart"]
|
||||
S_documents --> S_documents_Q["Queries<br/>* document - listDocuments<br/>* staffDocument - listStaffDocumentsByStaffId"]
|
||||
S_documents --> S_documents_M["Mutations<br/>* staffDocument - updateStaffDocument<br/>* staffDocument - createStaffDocument"]
|
||||
end
|
||||
|
||||
subgraph L8["Certificates"]
|
||||
S_certificates["certificates_screen.dart"]
|
||||
S_certificates --> S_certificates_Q["Queries<br/>* certificate - listCertificatesByStaffId"]
|
||||
S_certificates --> S_certificates_M["Mutations<br/>* certificate - UpdateCertificate<br/>* certificate - CreateCertificate<br/>* certificate - DeleteCertificate"]
|
||||
end
|
||||
|
||||
subgraph L9["Tax Documents"]
|
||||
S_tax_forms["tax_forms_screen.dart"]
|
||||
S_tax_forms --> S_tax_forms_Q["Queries<br/>* taxForm - getTaxFormsBystaffId"]
|
||||
S_tax_forms --> S_tax_forms_M["Mutations<br/>* taxForm - createTaxForm<br/>* taxForm - updateTaxForm"]
|
||||
end
|
||||
|
||||
subgraph L10["KROW University"]
|
||||
S_uni["krow_university_screen.dart"]
|
||||
S_uni --> S_uni_Q["Queries<br/>* course - listCourses<br/>* staffCourse - listStaffCoursesByStaffId<br/>* staff - getStaffByUserId<br/>* level - listLevels<br/>* certificate - listCertificatesByStaffId"]
|
||||
end
|
||||
|
||||
subgraph L11["Trainings"]
|
||||
S_trainings["trainings_screen.dart"]
|
||||
S_trainings --> S_trainings_Q["Queries<br/>* course - listCourses<br/>* staffCourse - listStaffCoursesByStaffId"]
|
||||
end
|
||||
|
||||
subgraph L12["Leaderboard"]
|
||||
S_leaderboard["leaderboard_screen.dart"]
|
||||
S_leaderboard --> S_leaderboard_Q["Queries<br/>* staffCourse - missing"]
|
||||
end
|
||||
|
||||
subgraph L13["Bank Account"]
|
||||
S_bank["bank_account_screen.dart"]
|
||||
S_bank --> S_bank_Q["Queries<br/>* account - getAccountsByOwnerId"]
|
||||
S_bank --> S_bank_M["Mutations<br/>* account - createAccount<br/>* account - updateAccount<br/>* account - deleteAccount"]
|
||||
end
|
||||
|
||||
subgraph L14["Earnings/Payments"]
|
||||
S_payments["payments_screen.dart"]
|
||||
S_payments --> S_payments_Q["Queries<br/>* recentPayment - listRecentPaymentsByStaffId"]
|
||||
end
|
||||
|
||||
subgraph L15["Timecard"]
|
||||
S_timecard["time_card_screen.dart"]
|
||||
S_timecard --> S_timecard_Q["Queries<br/>* application - getApplicationsByStaffId"]
|
||||
end
|
||||
|
||||
subgraph L16["Clock in"]
|
||||
S_clockin["clock_in_screen.dart"]
|
||||
S_clockin --> S_clockin_Q["Queries<br/>* application - getApplicationsByStaffId"]
|
||||
S_clockin --> S_clockin_M["Mutations<br/>* application - createApplication<br/>* application - updateApplicationStatus"]
|
||||
end
|
||||
|
||||
subgraph L17["Shifts"]
|
||||
S_shifts["shifts_screen.dart"]
|
||||
S_shifts --> S_shifts_Q["Queries<br/>* application - getApplicationsByStaffId<br/>* shiftRole - listShiftRolesByVendorId/listShiftRolesByRoleId<br/>* application - getApplicationsByStaffId"]
|
||||
S_shifts --> S_shifts_M["Mutations<br/>* application - updateApplicationStatus<br/>* application - createApplication"]
|
||||
end
|
||||
|
||||
subgraph L18["My availability"]
|
||||
S_availability["availability_screen.dart"]
|
||||
S_availability --> S_availability_Q["Queries<br/>* staffAvailability - listStaffAvailabilitiesByStaffId/getStaffAvailabilityByKey"]
|
||||
S_availability --> S_availability_M["Mutations<br/>* staffAvailability - updateStaffAvailability<br/>* staffAvailability - createStaffAvailability<br/>* staffAvailability - deleteStaffAvailability"]
|
||||
end
|
||||
|
||||
subgraph L19["Your Benefits Overview"]
|
||||
S_benefits["benefits_screen.dart"]
|
||||
S_benefits --> S_benefits_Q["Queries<br/>* benefitsData - listBenefitsDataByStaffId"]
|
||||
S_benefits --> S_benefits_M["Mutations<br/>* benefitsData - updateBenefitsData<br/>* benefitsData - createBenefitsData"]
|
||||
end
|
||||
|
||||
subgraph L20["Home"]
|
||||
S_home["worker_home_screen.dart"]
|
||||
S_home --> S_home_Q["Queries<br/>* application - getApplicationsByStaffId<br/>* shiftRole - listShiftRolesByVendorId/listShiftRolesByRoleId<br/>* benefitsData - getBenefitsDataByStaffId"]
|
||||
end
|
||||
|
||||
subgraph L21["Shift detail"]
|
||||
S_shift_detail["shift_details_screen.dart"]
|
||||
S_shift_detail --> S_shift_detail_Q["Queries<br/>* application - getApplicationsByStaffId"]
|
||||
S_shift_detail --> S_shift_detail_M["Mutations<br/>* application - updateApplicationStatus"]
|
||||
end
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
config:
|
||||
theme: mc
|
||||
layout: dagre
|
||||
---
|
||||
classDiagram
|
||||
direction TB
|
||||
class User {
|
||||
id: String
|
||||
email: String
|
||||
}
|
||||
|
||||
class Business {
|
||||
id: UUID
|
||||
userId: String
|
||||
businessName: String
|
||||
status: BusinessStatus
|
||||
}
|
||||
|
||||
class Vendor {
|
||||
id: UUID
|
||||
userId: String
|
||||
companyName: String
|
||||
}
|
||||
|
||||
class Order {
|
||||
id: UUID
|
||||
businessId: UUID
|
||||
vendorId: UUID
|
||||
status: OrderStatus
|
||||
}
|
||||
|
||||
class Shift {
|
||||
id: UUID
|
||||
orderId: UUID
|
||||
status: ShiftStatus
|
||||
}
|
||||
|
||||
class ShiftRole {
|
||||
shiftId: UUID
|
||||
roleId: UUID
|
||||
}
|
||||
|
||||
class Role {
|
||||
id: UUID
|
||||
name: String
|
||||
vendorId: UUID
|
||||
}
|
||||
|
||||
class Application {
|
||||
id: UUID
|
||||
shiftId: UUID
|
||||
staffId: UUID
|
||||
roleId: UUID
|
||||
}
|
||||
|
||||
class Invoice {
|
||||
id: UUID
|
||||
businessId: UUID
|
||||
vendorId: UUID
|
||||
orderId: UUID
|
||||
status: InvoiceStatus
|
||||
}
|
||||
|
||||
class InvoiceTemplate {
|
||||
id: UUID
|
||||
name: String
|
||||
ownerId: UUID
|
||||
businessId: UUID
|
||||
vendorId: UUID
|
||||
}
|
||||
|
||||
class RecentPayment {
|
||||
id: UUID
|
||||
invoiceId: UUID
|
||||
applicationId: UUID
|
||||
staffId: UUID
|
||||
}
|
||||
|
||||
class ClientFeedback {
|
||||
id: UUID
|
||||
businessId: UUID
|
||||
vendorId: UUID
|
||||
rating: Int
|
||||
}
|
||||
|
||||
class Team {
|
||||
id: UUID
|
||||
teamName: String
|
||||
ownerId: String
|
||||
}
|
||||
|
||||
class TeamMember {
|
||||
id: UUID
|
||||
teamId: UUID
|
||||
userId: String
|
||||
role: TeamMemberRole
|
||||
}
|
||||
|
||||
class TeamHub {
|
||||
id: UUID
|
||||
teamId: UUID
|
||||
hubName: String
|
||||
}
|
||||
|
||||
class Staff {
|
||||
id: UUID
|
||||
userId: String
|
||||
}
|
||||
|
||||
note for Staff "business can create a staff too"
|
||||
|
||||
Business "1" -- "*" Order : places
|
||||
Business "1" -- "*" Invoice : receives
|
||||
Business "1" -- "*" ClientFeedback : gives
|
||||
Business "1" --o "1" Team : (ownerId)
|
||||
Business "1" --o "*" InvoiceTemplate : (ownerId)
|
||||
User "1" -- "1" Business : owns
|
||||
Vendor "1" -- "*" Order : fulfills
|
||||
Vendor "1" -- "*" Invoice : issues
|
||||
Order "1" -- "*" Shift : contains
|
||||
Order "1" -- "1" Invoice : billed via
|
||||
Shift "1" -- "*" ShiftRole : requires
|
||||
ShiftRole "1" -- "1" Role
|
||||
ShiftRole "1" -- "*" Application : target for
|
||||
Application "1" -- "1" Staff
|
||||
Application "1" -- "1" RecentPayment
|
||||
Invoice "1" -- "*" RecentPayment : paid through
|
||||
Team "1" -- "*" TeamHub : contains
|
||||
Team "1" -- "*" TeamMember : has
|
||||
@@ -0,0 +1,181 @@
|
||||
classDiagram
|
||||
direction TB
|
||||
class User {
|
||||
id: String
|
||||
email: String
|
||||
fullName: String
|
||||
role: UserBaseRole
|
||||
}
|
||||
|
||||
class Staff {
|
||||
id: UUID
|
||||
userId: String
|
||||
fullName: String
|
||||
ownerId: UUID
|
||||
hubId: UUID
|
||||
rollId: UUID
|
||||
status: BackgroundCheckStatus
|
||||
}
|
||||
|
||||
class Account{
|
||||
id: UUID
|
||||
ownerId:UUID
|
||||
type:AccountType
|
||||
}
|
||||
|
||||
class Workforce {
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
staffId: UUID
|
||||
status: WorkforceStatus
|
||||
}
|
||||
|
||||
class Application {
|
||||
id: UUID
|
||||
shiftId: UUID
|
||||
staffId: UUID
|
||||
roleId: UUID
|
||||
status: ApplicationStatus
|
||||
}
|
||||
|
||||
class Assignment {
|
||||
id: UUID
|
||||
workforceId: UUID
|
||||
shiftId: UUID
|
||||
roleId: UUID
|
||||
status: AssignmentStatus
|
||||
}
|
||||
|
||||
class ShiftRole {
|
||||
id: UUID
|
||||
shiftId: UUID
|
||||
roleId: UUID
|
||||
}
|
||||
|
||||
class Shift {
|
||||
id: UUID
|
||||
orderId: UUID
|
||||
status: ShiftStatus
|
||||
}
|
||||
|
||||
class Order {
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
businessId: UUID
|
||||
status: OrderStatus
|
||||
}
|
||||
|
||||
class Vendor {
|
||||
id: UUID
|
||||
userId: String
|
||||
companyName: String
|
||||
}
|
||||
|
||||
class Business {
|
||||
id: UUID
|
||||
userId: String
|
||||
businessName: String
|
||||
}
|
||||
|
||||
class Role {
|
||||
id: UUID
|
||||
name: String
|
||||
vendorId: UUID
|
||||
roleCategoryId: UUID
|
||||
}
|
||||
|
||||
class StaffDocument {
|
||||
staffId: UUID
|
||||
documentId: UUID
|
||||
status: DocumentStatus
|
||||
}
|
||||
|
||||
class Document {
|
||||
id: UUID
|
||||
name: String
|
||||
documentType: DocumentType
|
||||
}
|
||||
|
||||
class StaffCourse {
|
||||
staffId: UUID
|
||||
courseId: UUID
|
||||
}
|
||||
|
||||
class Course {
|
||||
id: UUID
|
||||
title: String
|
||||
categoryId: UUID
|
||||
}
|
||||
|
||||
class Category {
|
||||
id: UUID
|
||||
label: String
|
||||
}
|
||||
|
||||
class StaffAvailability {
|
||||
staffId: UUID
|
||||
day: DayOfWeek
|
||||
slot: AvailabilitySlot
|
||||
}
|
||||
|
||||
class Certificate {
|
||||
staffId: UUID
|
||||
certificationType: ComplianceType
|
||||
status: CertificateStatus
|
||||
}
|
||||
|
||||
class BenefitData {
|
||||
staffId: UUID
|
||||
vendorBenefitPlanId: UUID
|
||||
current: int
|
||||
}
|
||||
|
||||
class Contact {
|
||||
staffId: UUID
|
||||
relationship: RelationshipType
|
||||
name: string
|
||||
}
|
||||
|
||||
class Invoice {
|
||||
orderId: UUID
|
||||
}
|
||||
|
||||
class RecentPayment {
|
||||
staffId: UUID
|
||||
applicationId: UUID
|
||||
status: RecentPaymentStatus
|
||||
}
|
||||
|
||||
class TaxForm {
|
||||
staffId: UUID
|
||||
formType: TaxFormType
|
||||
status: TaxFormStatus
|
||||
}
|
||||
|
||||
User "1" -- "1" Staff : has
|
||||
Staff "1" -- "*" Application : applies to
|
||||
Staff "1" -- "*" Workforce : part of
|
||||
Staff "1" -- "*" StaffDocument : has
|
||||
Staff "1" -- "*" Certificate : has
|
||||
Staff "1" -- "*" BenefitData : has
|
||||
Staff "1" -- "*" Contact : has
|
||||
Staff "1" -- "*" TaxForm : has
|
||||
Staff "1" -- "*" Account : has
|
||||
Staff "1" -- "*" StaffCourse : takes
|
||||
Staff "1" -- "*" StaffAvailability : sets
|
||||
Workforce "1" -- "*" Assignment : receives
|
||||
Assignment -- ShiftRole
|
||||
Vendor "1" -- "*" Order : receives
|
||||
Business "1" -- "*" Order : places
|
||||
Order "1" -- "1" Invoice : has
|
||||
Invoice "1" -- "*" RecentPayment : has
|
||||
RecentPayment "1" -- "1" Application : has
|
||||
Staff "1" --o "1" Vendor : (ownerId)
|
||||
Staff "1" --o "1" Business : (ownerId)
|
||||
Application -- ShiftRole
|
||||
ShiftRole "*" -- "1" Shift : belongs to
|
||||
ShiftRole "*" -- "1" Role : defines
|
||||
Shift "*" -- "1" Order : belongs to
|
||||
StaffDocument "1" -- "1" Document : references
|
||||
StaffCourse "1" -- "1" Course : references
|
||||
Course "1" -- "1" Category : belongs to
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
theme: mc
|
||||
---
|
||||
classDiagram
|
||||
class User {
|
||||
id: String
|
||||
email: String
|
||||
fullName: String
|
||||
}
|
||||
|
||||
class TeamMember {
|
||||
id: UUID
|
||||
teamId: UUID
|
||||
userId: String
|
||||
teamHubId: UUID
|
||||
role: TeamMemberRole
|
||||
inviteStatus: TeamMemberInviteStatus
|
||||
inviteCode: UUID
|
||||
}
|
||||
|
||||
class Team {
|
||||
id: UUID
|
||||
teamName: String
|
||||
ownerId: String
|
||||
}
|
||||
|
||||
class TeamHub {
|
||||
id: UUID
|
||||
teamId: UUID
|
||||
hubName: String
|
||||
}
|
||||
|
||||
class TeamHudDepartment {
|
||||
id: UUID
|
||||
name: String
|
||||
teamHubId: UUID
|
||||
}
|
||||
|
||||
class MemberTask {
|
||||
teamMemberId: UUID
|
||||
taskId: UUID
|
||||
}
|
||||
|
||||
class Task {
|
||||
id: UUID
|
||||
taskName: String
|
||||
status: TaskStatus
|
||||
priority: TaskPriority
|
||||
ownerId: UUID
|
||||
}
|
||||
|
||||
class Vendor {
|
||||
id: UUID
|
||||
companyName: String
|
||||
}
|
||||
|
||||
class Business {
|
||||
id: UUID
|
||||
businessName: String
|
||||
}
|
||||
|
||||
User "1" -- "1" TeamMember : has
|
||||
|
||||
Team "1" -- "*" TeamHub : contains
|
||||
Team "1" --o "1" Vendor : (ownerId)
|
||||
Team "1" --o "1" Business : (ownerId)
|
||||
|
||||
TeamHub "1" -- "*" TeamHudDepartment : has
|
||||
TeamHub "1" -- "*" TeamMember : is assigned to
|
||||
|
||||
TeamMember "*" -- "1" Team
|
||||
TeamMember "1" -- "*" MemberTask : has assigned
|
||||
|
||||
Task "1" -- "*" MemberTask : is assigned to
|
||||
Task --o Vendor : (ownerId)
|
||||
Task --o Business : (ownerId)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
classDiagram
|
||||
direction TB
|
||||
class User {
|
||||
id: String
|
||||
email: String
|
||||
fullName: String
|
||||
role: UserBaseRole
|
||||
}
|
||||
|
||||
class Staff {
|
||||
id: UUID
|
||||
userId: String
|
||||
fullName: String
|
||||
ownerId: UUID
|
||||
hubId: UUID
|
||||
rollId: UUID
|
||||
status: BackgroundCheckStatus
|
||||
}
|
||||
|
||||
class Vendor {
|
||||
id: UUID
|
||||
userId: String
|
||||
companyName: String
|
||||
}
|
||||
|
||||
class Business {
|
||||
id: UUID
|
||||
userId: String
|
||||
businessName: String
|
||||
}
|
||||
|
||||
class TeamMember {
|
||||
id: UUID
|
||||
teamId: UUID
|
||||
userId: String
|
||||
role: TeamMemberRole
|
||||
}
|
||||
|
||||
class ActivityLog {
|
||||
id: UUID
|
||||
userId: String
|
||||
activityType: ActivityType
|
||||
}
|
||||
|
||||
class UserConversation {
|
||||
conversationId: UUID
|
||||
userId: String
|
||||
}
|
||||
|
||||
class Conversation {
|
||||
id: UUID
|
||||
conversationType: ConversationType
|
||||
}
|
||||
|
||||
class Message{
|
||||
id: UUID
|
||||
conversationId: UUID
|
||||
content: String
|
||||
}
|
||||
|
||||
User <|-- Staff
|
||||
User <|-- Business
|
||||
User <|-- Vendor
|
||||
User <|-- TeamMember
|
||||
|
||||
|
||||
User "1" -- "*" ActivityLog : logs
|
||||
User "1" -- "*" UserConversation : participates in
|
||||
UserConversation "*" -- "1" Conversation : is part of
|
||||
Conversation "1" -- "*" Message : has
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
---
|
||||
classDiagram
|
||||
direction TB
|
||||
class User {
|
||||
id: String
|
||||
email: String
|
||||
}
|
||||
|
||||
class Vendor {
|
||||
id: UUID
|
||||
userId: String
|
||||
companyName: String
|
||||
tier: VendorTier
|
||||
}
|
||||
|
||||
class Staff {
|
||||
id: UUID
|
||||
userId: String
|
||||
fullName: String
|
||||
}
|
||||
|
||||
class VendorBenefitPlan {
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
title: String
|
||||
}
|
||||
|
||||
class InvoiceTemplate {
|
||||
id: UUID
|
||||
name: String
|
||||
ownerId: UUID
|
||||
}
|
||||
|
||||
class VendorRate {
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
roleName: String
|
||||
}
|
||||
|
||||
class AttireOption{
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
}
|
||||
|
||||
class Team {
|
||||
id: UUID
|
||||
teamName: String
|
||||
ownerId: String
|
||||
}
|
||||
|
||||
class TeamMember {
|
||||
id: UUID
|
||||
teamId: UUID
|
||||
userId: String
|
||||
role: TeamMemberRole
|
||||
}
|
||||
|
||||
class TeamHub {
|
||||
id: UUID
|
||||
teamId: UUID
|
||||
hubName: String
|
||||
}
|
||||
|
||||
class Business {
|
||||
id: UUID
|
||||
userId: String
|
||||
businessName: String
|
||||
}
|
||||
|
||||
class ClientFeedback {
|
||||
id: UUID
|
||||
businessId: UUID
|
||||
vendorId: UUID
|
||||
rating: Int
|
||||
}
|
||||
|
||||
class Order {
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
businessId: UUID
|
||||
status: OrderStatus
|
||||
}
|
||||
|
||||
class Shift {
|
||||
id: UUID
|
||||
orderId: UUID
|
||||
status: ShiftStatus
|
||||
}
|
||||
|
||||
class Role {
|
||||
id: UUID
|
||||
name: String
|
||||
vendorId: UUID
|
||||
}
|
||||
|
||||
class ShiftRole {
|
||||
shiftId: UUID
|
||||
roleId: UUID
|
||||
}
|
||||
|
||||
class Workforce {
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
staffId: UUID
|
||||
status: WorkforceStatus
|
||||
}
|
||||
|
||||
class Application {
|
||||
id: UUID
|
||||
shiftId: UUID
|
||||
staffId: UUID
|
||||
roleId: UUID
|
||||
status: ApplicationStatus
|
||||
}
|
||||
|
||||
class Assignment {
|
||||
id: UUID
|
||||
workforceId: UUID
|
||||
shiftId: UUID
|
||||
roleId: UUID
|
||||
status: AssignmentStatus
|
||||
}
|
||||
|
||||
class Invoice {
|
||||
id: UUID
|
||||
vendorId: UUID
|
||||
businessId: UUID
|
||||
orderId: UUID
|
||||
status: InvoiceStatus
|
||||
}
|
||||
|
||||
class RecentPayment {
|
||||
id: UUID
|
||||
staffId: UUID
|
||||
applicationId: UUID
|
||||
invoiceId: UUID
|
||||
status: RecentPaymentStatus
|
||||
}
|
||||
|
||||
note for Vendor "All tables has relationship with vendor"
|
||||
|
||||
User "1" -- "1" Vendor : has
|
||||
Vendor "1" o-- "1" Staff : (ownerId)
|
||||
Vendor "1" -- "*" VendorBenefitPlan : offers
|
||||
Vendor "1" -- "*" AttireOption
|
||||
Vendor o-- InvoiceTemplate : (ownerId)
|
||||
Vendor "1" -- "*" VendorRate : has
|
||||
Vendor "1" -- "*" ClientFeedback : receives
|
||||
Vendor "1" -- "*" Order : receives
|
||||
Business "1" -- "*" Order : places
|
||||
Order "1" -- "*" Shift : contains
|
||||
Shift "1" -- "*" ShiftRole : requires
|
||||
Role "1" -- "*" ShiftRole : fills
|
||||
ShiftRole "1" -- "*" Application : receives
|
||||
Assignment "1" -- "1" ShiftRole
|
||||
Assignment "1" -- "1" Workforce
|
||||
Invoice "1" -- "1" Order
|
||||
Invoice "1" -- "*" RecentPayment : details
|
||||
Vendor "1" o-- "1" Team
|
||||
Team "1" -- "*" TeamHub : contains
|
||||
Team "1" -- "*" TeamMember : has
|
||||
294
docs/BACKEND/DATACONNECT_GUIDES/DOCUMENTS/backend_manual.md
Normal file
294
docs/BACKEND/DATACONNECT_GUIDES/DOCUMENTS/backend_manual.md
Normal file
@@ -0,0 +1,294 @@
|
||||
# Krow Workforce – Backend Manual
|
||||
Firebase Data Connect + Cloud SQL (PostgreSQL)
|
||||
|
||||
---
|
||||
|
||||
## 1. Backend Overview
|
||||
|
||||
This project uses Firebase Data Connect with Cloud SQL (PostgreSQL) as the main backend system.
|
||||
|
||||
The architecture is based on:
|
||||
|
||||
- GraphQL Schemas → Define database tables
|
||||
- Connectors (Queries & Mutations) → Data access layer
|
||||
- Cloud SQL → Real database
|
||||
- Auto-generated SDK → Used by Web & Mobile apps
|
||||
- Makefile → Automates backend workflows
|
||||
|
||||
The goal is to keep the backend scalable, structured, and aligned with Web and Mobile applications.
|
||||
|
||||
---
|
||||
|
||||
## 2. Project Structure
|
||||
|
||||
```
|
||||
dataconnect/
|
||||
│
|
||||
├── dataconnect.yaml
|
||||
├── schema/
|
||||
│ ├── Staff.gql
|
||||
│ ├── Vendor.gql
|
||||
│ ├── Business.gql
|
||||
│ └── ...
|
||||
│
|
||||
├── connector/
|
||||
│ ├── staff/
|
||||
│ │ ├── queries.gql
|
||||
│ │ └── mutations.gql
|
||||
│ ├── invoice/
|
||||
│ └── ...
|
||||
│
|
||||
├── connector/connector.yaml
|
||||
│
|
||||
docs/backend-diagrams/
|
||||
│ ├── business_uml_diagram.mmd
|
||||
│ ├── staff_uml_diagram.mmd
|
||||
│ ├── team_uml_diagram.mmd
|
||||
│ ├── user_uml_diagram.mmd
|
||||
│ └── vendor_uml_diagram_simplify.mmd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. dataconnect.yaml (Main Configuration)
|
||||
|
||||
```yaml
|
||||
specVersion: "v1"
|
||||
serviceId: "krow-workforce-db"
|
||||
location: "us-central1"
|
||||
|
||||
schema:
|
||||
source: "./schema"
|
||||
datasource:
|
||||
postgresql:
|
||||
database: "krow_db"
|
||||
cloudSql:
|
||||
instanceId: "krow-sql"
|
||||
|
||||
connectorDirs: ["./connector"]
|
||||
```
|
||||
|
||||
### Purpose
|
||||
|
||||
| Field | Description |
|
||||
|------|------------|
|
||||
| serviceId | Data Connect service name |
|
||||
| schema.source | Where GraphQL schemas live |
|
||||
| datasource | Cloud SQL connection |
|
||||
| connectorDirs | Where queries/mutations are |
|
||||
|
||||
---
|
||||
|
||||
## 4. Database Schemas
|
||||
|
||||
All database schemas are located in:
|
||||
|
||||
```
|
||||
dataconnect/schema/
|
||||
```
|
||||
|
||||
Each `.gql` file represents a table:
|
||||
|
||||
- Staff.gql
|
||||
- Invoice.gql
|
||||
- ShiftRole.gql
|
||||
- Application.gql
|
||||
- etc.
|
||||
|
||||
Schemas define:
|
||||
|
||||
- Fields
|
||||
- Enums
|
||||
- Relationships (`@ref`)
|
||||
- Composite keys (`key: []`)
|
||||
|
||||
---
|
||||
|
||||
## 5. Queries & Mutations (Connectors)
|
||||
|
||||
Located in:
|
||||
|
||||
```
|
||||
dataconnect/connector/<entity>/
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
dataconnect/connector/staff/queries.gql
|
||||
dataconnect/connector/staff/mutations.gql
|
||||
```
|
||||
|
||||
Each folder represents one entity.
|
||||
|
||||
This layer defines:
|
||||
|
||||
- listStaff
|
||||
- getStaffById
|
||||
- createStaff
|
||||
- updateStaff
|
||||
- deleteStaff
|
||||
- etc.
|
||||
|
||||
---
|
||||
|
||||
## 6. connector.yaml (SDK Generator)
|
||||
|
||||
```yaml
|
||||
connectorId: example
|
||||
generate:
|
||||
dartSdk:
|
||||
- outputDir: ../../mobile/staff/staff_app_mvp/lib/dataconnect_generated
|
||||
package: dataconnect_generated/generated.dart
|
||||
- outputDir: ../../mobile/client/client_app_mvp/lib/dataconnect_generated
|
||||
package: dataconnect_generated/generated.dart
|
||||
```
|
||||
|
||||
This file generates the SDK for:
|
||||
|
||||
- Staff Mobile App
|
||||
- Client Mobile App
|
||||
|
||||
---
|
||||
|
||||
## 7. What is the SDK?
|
||||
|
||||
The SDK is generated using:
|
||||
|
||||
```bash
|
||||
firebase dataconnect:sdk:generate
|
||||
```
|
||||
|
||||
It allows the apps to:
|
||||
|
||||
- Call queries/mutations
|
||||
- Use strong typing
|
||||
- Avoid manual GraphQL
|
||||
- Reduce runtime errors
|
||||
|
||||
Example in Flutter:
|
||||
|
||||
```dart
|
||||
client.listStaff();
|
||||
client.createInvoice();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Makefile – Automation Commands
|
||||
|
||||
### Main Commands
|
||||
|
||||
| Command | Purpose |
|
||||
|--------|---------|
|
||||
| dataconnect-enable-apis | Enable required APIs |
|
||||
| dataconnect-init | Initialize Data Connect |
|
||||
| dataconnect-deploy | Deploy schemas |
|
||||
| dataconnect-sql-migrate | Apply DB migrations |
|
||||
| dataconnect-generate-sdk | Generate SDK |
|
||||
| dataconnect-sync | Full backend update |
|
||||
| dataconnect-test | Test without breaking |
|
||||
| dataconnect-seed | Insert seed data |
|
||||
| dataconnect-bootstrap-db | Create Cloud SQL |
|
||||
|
||||
---
|
||||
|
||||
## 9. Correct Backend Workflow
|
||||
|
||||
### Production Flow
|
||||
|
||||
```bash
|
||||
make dataconnect-sync
|
||||
```
|
||||
|
||||
Steps:
|
||||
|
||||
1. Deploy schema
|
||||
2. Run SQL migrations
|
||||
3. Generate SDK
|
||||
|
||||
---
|
||||
|
||||
### Safe Test Flow
|
||||
|
||||
```bash
|
||||
make dataconnect-test
|
||||
```
|
||||
|
||||
This runs:
|
||||
|
||||
- Deploy dry-run
|
||||
- SQL diff
|
||||
- Shows errors without changing DB
|
||||
|
||||
---
|
||||
|
||||
## 10. Seed Data
|
||||
|
||||
Current command:
|
||||
|
||||
```make
|
||||
dataconnect-seed:
|
||||
@firebase dataconnect:execute seeds/seed_min.graphql --project=$(FIREBASE_ALIAS)
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- Validate schema
|
||||
- Detect missing tables
|
||||
- Prevent bad inserts
|
||||
|
||||
---
|
||||
|
||||
## 11. UML Diagrams
|
||||
|
||||
Located in:
|
||||
|
||||
```
|
||||
docs/backend-diagrams/
|
||||
```
|
||||
|
||||
Divided by role:
|
||||
|
||||
| File | Scope |
|
||||
|------|-------|
|
||||
| user_uml_diagram.mmd | User |
|
||||
| staff_uml_diagram.mmd | Staff |
|
||||
| vendor_uml_diagram_simplify.mmd | Vendor |
|
||||
| business_uml_diagram.mmd | Business |
|
||||
| team_uml_diagram.mmd | Teams |
|
||||
|
||||
Used with Mermaid to visualize relationships.
|
||||
|
||||
---
|
||||
|
||||
## 12. Core Business Workflow
|
||||
|
||||
```text
|
||||
Order
|
||||
→ Shift
|
||||
→ ShiftRole
|
||||
→ Application
|
||||
→ Workforce
|
||||
→ Assignment
|
||||
→ Invoice
|
||||
→ RecentPayment
|
||||
```
|
||||
|
||||
This represents the full work & payment lifecycle.
|
||||
|
||||
---
|
||||
|
||||
## 13. Final Notes
|
||||
|
||||
This backend is designed to:
|
||||
|
||||
- Scale efficiently
|
||||
- Maintain data consistency
|
||||
- Align Web & Mobile models
|
||||
- Support reporting and billing
|
||||
- Avoid duplicated data
|
||||
|
||||
---
|
||||
|
||||
END OF MANUAL
|
||||
File diff suppressed because it is too large
Load Diff
183
docs/BACKEND/DATACONNECT_GUIDES/backend_cloud_run_functions.md
Normal file
183
docs/BACKEND/DATACONNECT_GUIDES/backend_cloud_run_functions.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Backend Cloud Run / Functions Guide
|
||||
|
||||
## 1) Validate Shift Acceptance (Worker)
|
||||
**Best fit:** Cloud Run
|
||||
|
||||
**Why backend logic is required**
|
||||
- Shift acceptance must be enforced server‑side to prevent bypassing the client.
|
||||
- It must be race‑condition safe (two accepts at the same time).
|
||||
- It needs to be extensible for future eligibility rules.
|
||||
|
||||
**Proposed backend solution**
|
||||
Add a single command endpoint:
|
||||
- `POST /shifts/:shiftId/accept`
|
||||
|
||||
**Backend flow**
|
||||
- Verify Firebase Auth token + permissions (worker identity).
|
||||
- Run an extensible validation pipeline (pluggable rules):
|
||||
- `NoOverlapRule` (M4)
|
||||
- Future rules can be added without changing core logic.
|
||||
- Apply acceptance in a DB transaction (atomic).
|
||||
- Return a clear error payload on rejection:
|
||||
- `409 CONFLICT` (overlap) with `{ code, message, conflictingShiftIds }`
|
||||
|
||||
---
|
||||
|
||||
## 2) Validate Shift Creation by a Client (Minimum Hours — soft check)
|
||||
**Best fit:** Cloud Run
|
||||
|
||||
**Why backend logic is required**
|
||||
- Creation rules must be enforced server‑side so clients can’t bypass validations by skipping the UI or calling APIs directly.
|
||||
- We want a scalable rule system so new creation checks can be added without rewriting core logic.
|
||||
|
||||
**Proposed backend solution**
|
||||
Add/route creation through a backend validation layer (Cloud Run endpoint or a dedicated “create order” command).
|
||||
|
||||
**On create**
|
||||
- Compute shift duration and compare against vendor minimum (current: **5 hours**).
|
||||
- Return a consistent validation response when below minimum, e.g.:
|
||||
- `200 OK` with `{ valid: false, severity: "SOFT", code: "MIN_HOURS", message, minHours: 5 }`
|
||||
- (or `400` only if we decide it should block creation; for now it’s a soft check)
|
||||
|
||||
**FE note**
|
||||
- Show the same message before submission (UX feedback), but backend remains the source of truth.
|
||||
|
||||
---
|
||||
|
||||
## 3) Enforce Cancellation Policy (no cancellations within 24 hours)
|
||||
**Best fit:** Cloud Run
|
||||
|
||||
**Why backend logic is required**
|
||||
- Cancellation restrictions must be enforced server‑side to prevent policy bypass.
|
||||
- Ensures consistent behavior across web/mobile and future clients.
|
||||
|
||||
**Proposed backend solution**
|
||||
Add a backend command endpoint for cancel:
|
||||
- `POST /shifts/:shiftId/cancel` (or `/orders/:id/cancel` depending on ownership model)
|
||||
|
||||
**Backend checks**
|
||||
- If `now >= shiftStart - 24h`, reject cancellation.
|
||||
|
||||
**Error response**
|
||||
- `403 FORBIDDEN` (or `409 CONFLICT`) with `{ code: "CANCEL_WINDOW_LOCKED", message, windowHours: 24, penalty: <TBD> }`
|
||||
- Once penalty is finalized, include it in the response and logs/audit trail.
|
||||
|
||||
---
|
||||
|
||||
## 4) Implement Worker Documentation Upload Process
|
||||
**Best fit:** Cloud Functions v2 + Cloud Storage
|
||||
|
||||
**Why backend logic is required**
|
||||
- Uploads must be stored securely and reliably linked to the correct worker profile.
|
||||
- Requires server‑side auth and auditing.
|
||||
|
||||
**Proposed backend solution**
|
||||
- HTTP/Callable Function: `uploadInit(workerId, docType)` → returns signed upload URL + `documentId`.
|
||||
- Client uploads directly to Cloud Storage.
|
||||
- Storage trigger (`onFinalize`) or `uploadComplete(documentId)`:
|
||||
- Validate uploader identity/ownership.
|
||||
- Store metadata in DB (type, path, status, timestamps).
|
||||
- Link document to worker profile.
|
||||
- Enforce access control (worker/self, admins, authorized client reviewers).
|
||||
|
||||
---
|
||||
|
||||
## 5) Parse Uploaded Documentation for Verification
|
||||
**Best fit:** Cloud Functions (event‑driven) or Cloud Run worker (async)
|
||||
|
||||
**Why backend logic is required**
|
||||
- Parsing should run asynchronously.
|
||||
- Store structured results for review while keeping manual verification as the final authority.
|
||||
|
||||
**Proposed backend solution**
|
||||
- Trigger on Storage upload finalize:
|
||||
- OCR/AI extract key fields → store structured output (`parsedFields`, `confidence`, `aiStatus`).
|
||||
- Keep manual review:
|
||||
- Client can approve/override AI results.
|
||||
- Persist reviewer decision + audit trail.
|
||||
|
||||
---
|
||||
|
||||
## 6) Support Attire Upload for Workers
|
||||
**Best fit:** Cloud Functions v2 + Cloud Storage
|
||||
|
||||
**Why backend logic is required**
|
||||
- Attire images must be securely stored and linked to the correct worker profile.
|
||||
- Requires server‑side authorization.
|
||||
|
||||
**Proposed backend solution**
|
||||
- HTTP/Callable Function: `attireUploadInit(workerId)` → signed upload URL + `attireAssetId`.
|
||||
- Client uploads to Cloud Storage.
|
||||
- Storage trigger (`onFinalize`) or `attireUploadComplete(attireAssetId)`:
|
||||
- Validate identity/ownership.
|
||||
- Store metadata and link to worker profile.
|
||||
|
||||
---
|
||||
|
||||
## 7) Verify Attire Images Against Shift Dress Code
|
||||
**Best fit:** Cloud Functions (trigger) or Cloud Run worker (async)
|
||||
|
||||
**Why backend logic is required**
|
||||
- Verification must be enforced server‑side.
|
||||
- Must remain reviewable/overrideable by the client even if AI passes.
|
||||
|
||||
**Proposed backend solution**
|
||||
- Async verification triggered after upload or when tied to a shift:
|
||||
- Evaluate dress code rules (and optional AI).
|
||||
- Store results `{ status, reasons, evidence, confidence }`.
|
||||
- Client can manually approve/override; audit every decision.
|
||||
|
||||
---
|
||||
|
||||
## 8) Support Shifts Requiring “Awaiting Confirmation” Status
|
||||
**Best fit:** Cloud Run (domain state transitions)
|
||||
|
||||
**Why backend logic is required**
|
||||
- State transitions must be enforced server‑side.
|
||||
- Prevent invalid bookings and support future workflow rules.
|
||||
|
||||
**Proposed backend solution**
|
||||
- Add status flow: `AWAITING_CONFIRMATION → BOOKED/ACTIVE` (per lifecycle).
|
||||
- Command endpoint: `POST /shifts/:id/confirm`.
|
||||
|
||||
**Backend validates**
|
||||
- Caller is the assigned worker.
|
||||
- Shift is still eligible (not started/canceled/overlapped, etc.).
|
||||
- Persist transition + audit event.
|
||||
|
||||
---
|
||||
|
||||
## 9) Enable NFC‑Based Clock‑In and Clock‑Out
|
||||
**Best fit:** Cloud Run (secure API) + optional Cloud Functions for downstream events
|
||||
|
||||
**Why backend logic is required**
|
||||
- Clock‑in/out is security‑sensitive and must be validated server‑side.
|
||||
- Requires strong auditing and anti‑fraud checks.
|
||||
|
||||
**Proposed backend solution**
|
||||
API endpoints:
|
||||
- `POST /attendance/clock-in`
|
||||
- `POST /attendance/clock-out`
|
||||
|
||||
**Validate**
|
||||
- Firebase identity.
|
||||
- NFC tag legitimacy (mapped to hub/location).
|
||||
- Time window rules + prevent duplicates/inconsistent sequences.
|
||||
|
||||
**Persist**
|
||||
- Store immutable events + derived attendance record.
|
||||
- Emit audit logs/alerts if needed.
|
||||
|
||||
---
|
||||
|
||||
## 10) Update Recurring & Permanent Orders (Backend)
|
||||
**Best fit:** Cloud Run
|
||||
|
||||
**Why backend logic is required**
|
||||
Updating a recurring or permanent order is not a single update. It may affect **N shifts** and **M shift roles**, and requires extra validations, such as:
|
||||
- Prevent editing shifts that already started.
|
||||
- Prevent removing or reducing roles with assigned staff.
|
||||
- Control whether changes apply to future only, from a given date, or all.
|
||||
- Ensure data consistency (all‑or‑nothing updates).
|
||||
|
||||
These operations can take time and must be enforced server‑side, even if the client is bypassed.
|
||||
Reference in New Issue
Block a user