# ========================================================== # Reports Connector - For MVP # ========================================================== # This file exposes SOURCE-DATA queries for all report screens. # All aggregation (group by day/week, percentages, averages) # is intentionally done on the client (Flutter). # # Entities used: # - Shift # - Application # - Staff # - Invoice # - TimeSheet # # NOTE: # Data Connect does NOT support custom root resolvers. # These queries return raw rows from @table entities. # ========================================================== # ========================================================== # 1) COVERAGE REPORT (Next 7 days) # ========================================================== # Coverage definition: # - Needed: sum(Shift.workersNeeded) grouped by date # - Confirmed: count(Application) where status IN (...) # # Client groups by Shift.date and calculates: # coveragePercentage = confirmed / needed * 100 # ========================================================== query listShiftsForCoverage( $businessId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { shifts( where: { order: { businessId: { eq: $businessId } } date: { ge: $startDate, le: $endDate } } orderBy: { date: ASC } ) { id date workersNeeded filled status } } #I comment here because I have an error en sdk query listApplicationsForCoverage( $shiftIds: [UUID!]! #$statuses: [ApplicationStatus!]! ) @auth(level: USER) { applications( where: { shiftId: { in: $shiftIds } #status: { in: $statuses } } ) { id shiftId staffId status } } # ========================================================== # 2) DAILY OPS REPORT (Single day) # ========================================================== # Metrics derived on client: # - scheduledShifts = shifts.length # - workersConfirmed = confirmedApps / totalNeeded # - inProgressShifts = status == IN_PROGRESS # - completedShifts = status == COMPLETED # ========================================================== query listShiftsForDailyOpsByBusiness( $businessId: UUID! $date: Timestamp! ) @auth(level: USER) { shifts( where: { order: { businessId: { eq: $businessId } } date: { eq: $date } } orderBy: { startTime: ASC } ) { id title location startTime endTime workersNeeded filled status } } query listShiftsForDailyOpsByVendor( $vendorId: UUID! $date: Timestamp! ) @auth(level: USER) { shifts( where: { order: { vendorId: { eq: $vendorId } } date: { eq: $date } } orderBy: { startTime: ASC } ) { id title location startTime endTime workersNeeded filled status } } query listApplicationsForDailyOps( $shiftIds: [UUID!]! ) @auth(level: USER) { applications(where: { shiftId: { in: $shiftIds } }) { id shiftId staffId status checkInTime checkOutTime } } # ========================================================== # 3) FORECAST REPORT (Next N weeks) # ========================================================== # Projected spend formula (client): # projectedCost = workersNeeded * hours * hourlyRate # ========================================================== query listShiftsForForecastByBusiness( $businessId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { shifts( where: { order: { businessId: { eq: $businessId } } date: { ge: $startDate, le: $endDate } } orderBy: { date: ASC } ) { id date workersNeeded hours cost status } } query listShiftsForForecastByVendor( $vendorId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { shifts( where: { order: { vendorId: { eq: $vendorId } } date: { ge: $startDate, le: $endDate } } orderBy: { date: ASC } ) { id date workersNeeded hours cost status } } # ========================================================== # 4) NO-SHOW REPORT (Historical range) # ========================================================== # Now fully supported because ApplicationStatus includes NO_SHOW. # # Metrics: # - totalNoShows = count(status == NO_SHOW) # - noShowRate = noShows / totalApplications # - breakdown by staff # ========================================================== query listShiftsForNoShowRangeByBusiness( $businessId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { shifts( where: { order: { businessId: { eq: $businessId } } date: { ge: $startDate, le: $endDate } } ) { id date } } query listShiftsForNoShowRangeByVendor( $vendorId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { shifts( where: { order: { vendorId: { eq: $vendorId } } date: { ge: $startDate, le: $endDate } } ) { id date } } query listApplicationsForNoShowRange( $shiftIds: [UUID!]! ) @auth(level: USER) { applications(where: { shiftId: { in: $shiftIds } }) { id shiftId staffId status } } query listStaffForNoShowReport( $staffIds: [UUID!]! ) @auth(level: USER) { staffs(where: { id: { in: $staffIds } }) { id fullName noShowCount reliabilityScore } } # ========================================================== # 5) SPEND REPORT (Financial) # ========================================================== # Uses Invoice as source of truth for money. # Filter is now based on: # - businessId (business dashboard) # - vendorId (vendor dashboard) # since Invoice no longer has ownerId. # ========================================================== query listInvoicesForSpendByBusiness( $businessId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { invoices( where: { businessId: { eq: $businessId } issueDate: { ge: $startDate, le: $endDate } } orderBy: { issueDate: ASC } ) { id issueDate dueDate amount status invoiceNumber vendor { id companyName } business { id businessName } order { id eventName } } } query listInvoicesForSpendByVendor( $vendorId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { invoices( where: { vendorId: { eq: $vendorId } issueDate: { ge: $startDate, le: $endDate } } orderBy: { issueDate: ASC } ) { id issueDate dueDate amount status invoiceNumber vendor { id companyName } business { id businessName } order { id eventName } } } # Optional: Spend by Order (if you need it) query listInvoicesForSpendByOrder( $orderId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { invoices( where: { orderId: { eq: $orderId } issueDate: { ge: $startDate, le: $endDate } } orderBy: { issueDate: ASC } ) { id issueDate dueDate amount status invoiceNumber vendor { id companyName } business { id businessName } order { id eventName } } } query listTimesheetsForSpend( $startTime: Timestamp! $endTime: Timestamp! ) @auth(level: USER) { shiftRoles( where: { shift: { date: { ge: $startTime, le: $endTime } } } ) { id hours totalValue shift{ title location status date order{ business{ businessName } } } role{ costPerHour } } } # ========================================================== # 6) PERFORMANCE REPORT (Historical KPIs) # ========================================================== # Metrics derivable: # - fillRate # - completionRate # - onTimeRate # - avgFillTimeHours (NOW POSSIBLE 🎉) # # avgFillTimeHours formula: # (filledAt - createdAt) averaged across FILLED shifts # ========================================================== query listShiftsForPerformanceByBusiness( $businessId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { shifts( where: { order: { businessId: { eq: $businessId } } date: { ge: $startDate, le: $endDate } filledAt: { isNull: false } } ) { id workersNeeded filled status createdAt filledAt } } query listShiftsForPerformanceByVendor( $vendorId: UUID! $startDate: Timestamp! $endDate: Timestamp! ) @auth(level: USER) { shifts( where: { order: { vendorId: { eq: $vendorId } } date: { ge: $startDate, le: $endDate } filledAt: { isNull: false } } ) { id workersNeeded filled status createdAt filledAt } } query listApplicationsForPerformance( $shiftIds: [UUID!]! ) @auth(level: USER) { applications(where: { shiftId: { in: $shiftIds } }) { id shiftId staffId status checkInTime checkOutTime } } query listStaffForPerformance( $staffIds: [UUID!]! ) @auth(level: USER) { staffs(where: { id: { in: $staffIds } }) { id averageRating onTimeRate noShowCount reliabilityScore } }