# ListPublishedShifts

## Overview

listPublishedShifts returns shifts in CONFIRMED shift schedules for a date range and/or site, paginated. It is the query behind the shift schedule view — the committed schedule that workers and planners see once shifts have been published.

## Business Rules

- Accepts a date range (`startDate` / `endDate`), an optional `siteId`, and pagination parameters
- Returns only shifts whose ShiftSchedule is `CONFIRMED` — slots in a DRAFT period and withdrawn slots are excluded
- Filters to shifts whose `date` falls within the given range, inclusive
- When `siteId` is given, filters to shifts at that Site
- Includes staffed and unstaffed slots alike; use `listOpenShifts` to see only those no ACTIVE placement staffs
- Results are paginated, ordered by `date` ascending **by default**; `orderBy` / `orderDirection` override the sort field (`date`, `plannedStartAt`, `createdAt`, `id`), and `id` is always appended as a tiebreaker so paging is stable across rows sharing the sort value

## Process Flow

```mermaid
flowchart TD
    A[Receive startDate, endDate, siteId, pagination params] --> B[Filter Shift where shiftSchedule status = CONFIRMED]
    B --> C[Filter date within startDate..endDate]
    C --> D{siteId provided?}
    D -- Yes --> E[Filter by siteId]
    D -- No --> F[Skip site filter]
    E --> G[Sort by the requested field, default date, then id]
    F --> G
    G --> H[Apply pagination]
    H --> I[Return paginated shifts in CONFIRMED shiftSchedules]
```

## External Dependencies

- [organization::Site](../model/Shift.md) — the Site a shift is filtered by (cross-module reference resolved via `siteId`)

## Error Scenarios

- **INVALID_DATE_RANGE**: `endDate` precedes `startDate`
- **INVALID_PAGINATION**: pagination cursor or limit is malformed

## Test Cases

- returns only shifts with status PUBLISHED
- excludes PLANNED and CANCELLED shifts
- filters shifts to the given date range
- filters shifts to the given siteId when provided
- includes both assigned and open published shifts
- paginates results according to the given limit and cursor
- throws INVALID_DATE_RANGE when endDate precedes startDate
- throws INVALID_PAGINATION when limit is not a positive integer
