npm package: @propriety/court-calendar
Description: A React component library for managing court dates, cases, and evidence. Built with FullCalendar, MUI, and Dexie (IndexedDB caching).
Latest version: 1.0.78

# @propriety/court-calendar

A React component library for managing court dates, cases, and evidence. Built with FullCalendar, MUI, and Dexie (IndexedDB caching).

## Installation

```bash
npm install @propriety/court-calendar
```

You must also have the following peer dependencies installed:

```bash
npm install react react-dom @mui/material @mui/icons-material @mui/x-data-grid @mui/x-date-pickers @emotion/react @emotion/styled
```

## Usage

```tsx
import { CCalendar } from '@propriety/court-calendar';
import '@propriety/court-calendar/styles.css';

function App() {
    return <CCalendar apiKey="your-api-key" activeUser={1} />;
}
```

### Props

| Prop | Type | Description |
|------|------|-------------|
| `apiKey` | `string` | API key for authenticating with the Aventine API |
| `activeUser` | `number` | The current user's ID (used for chair assignments and edit tracking) |

### CSS Import

You **must** import the stylesheet separately:

```tsx
import '@propriety/court-calendar/styles.css';
```

### Theming

The component supports light and dark themes via the `data-theme` attribute on the document body:

```tsx
document.body.setAttribute('data-theme', 'dark'); // or 'light'
```

Components use the `.themed` CSS class to pick up theme variables.

---

## Development

### Prerequisites

- Node.js 20+
- npm

### Setup

```bash
git clone git@github.com:Aventine-Git/court-calendar.git
cd court-calendar
npm install
```

### Dev Server

```bash
npm run dev
```

This starts the Vite dev server using the `dev/` directory as the entry point (`dev/App.tsx` and `dev/main.tsx`). The dev app wraps the `CCalendar` component for local testing.

**Important:** The API only accepts requests from `localhost:8000`. The dev server must run on port 8000 or API calls will be rejected with CORS errors.

### Environment Variables

Create a `.env` file in the project root:

```
VITE_CALENDAR_API_KEY=your-api-key-here
```

This key is used by the dev app and is accessed via `import.meta.env.VITE_CALENDAR_API_KEY`.

### Build

```bash
npm run build
```

Runs TypeScript compilation (`tsc -b`) followed by the Vite library build. Output goes to `dist/`:

- `dist/index.mjs` - ES module bundle
- `dist/index.d.ts` - TypeScript type definitions
- `dist/court-calendar.css` - Compiled styles

The build externalizes React, React-DOM, MUI, and Emotion packages (they are peer dependencies and not bundled).

### Lint

```bash
npm run lint
```

Uses ESLint with TypeScript plugin. Biome is also configured for formatting (4-space indent, 120 char line width, single quotes).

---

## Testing

There are currently no automated tests. Testing is done manually through the dev server.

---

## API

All data is fetched from `https://utils.aventine.ai`. Every request requires the `x-api-key` header.

### Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/court-dates/all` | GET | Fetch all court dates |
| `/court-dates/create` | POST | Create a new court date |
| `/court-dates/{id}/update` | PUT | Update a court date |
| `/court-dates/{id}` | DELETE | Delete a court date |
| `/court-dates/{id}/cases` | GET | Fetch cases for a court date |
| `/court-cases/search?term=...&page=...` | GET | Search cases (paginated) |
| `/court-cases/filtering?page=...&page_size=...` | GET | List all cases (paginated) |
| `/court-cases/snooze/upload/{id}` | GET | Snooze upload deadline by 1 business day |
| `/users/all` | GET | Fetch all users |
| `/users/hearing-officers` | GET | Fetch hearing officers |
| `/utils/get-muni-names` | GET | Fetch municipality names |

### Limitations

- **Port restriction:** The API only accepts requests from `localhost:8000`. Running the dev server on any other port will result in CORS failures.
- **No offline support:** All CRUD operations require an active connection to the API. The IndexedDB cache is read-only (used for faster page loads, not offline editing).
- **Cache expiry:** Court dates cache expires after 5 minutes, cases cache after 1 hour. After expiry, data is re-fetched from the API on the next page load.
- **No pagination for court dates:** `getAllDates` fetches every court date in a single request. For large datasets this could be slow.
- **Municipality and user data loads on import:** `munis.ts` and `people.ts` fire fetch requests at module load time, not lazily.

---

## Architecture

### Components

| Component | Description |
|-----------|-------------|
| `CCalendar` | Root component. Manages state, events, filtering, and renders the calendar + modal. |
| `Toolbar` | Navigation and filter controls (date type, hearing type, case filters, user filter, view switcher, municipality dropdown, search). |
| `CalendarList` | MUI DataGrid table view of court dates with color-coded status, county, and location columns. |
| `Modal` | Dialog with 4 modes: Details, Edit, Create, Case Details. |
| `CreateEditCase` | Form for court date fields (dates, municipality, hearing details, chair assignments, case IDs). |
| `CaseViewer` | DataGrid listing cases for a selected court date. |
| `CaseDetails` | Read-only view of a single case with evidence and SCAR data. |

### Helpers

| Helper | Description |
|--------|-------------|
| `courtDates.ts` | CRUD operations for court dates. Sends datetime strings combining date + hearing time. |
| `cases.ts` | Case fetching (by court date, paginated, search). Settlement and evidence checks. |
| `cache.ts` | Dexie-based IndexedDB caching with TTL (5 min for dates, 1 hour for cases). |
| `formatter.ts` | Date formatting for API (`YYYY-MM-DD` or `YYYY-MM-DD HH:mm:00`). 12h/24h time conversion. Evidence string formatting. |
| `munis.ts` | Municipality name lookups. Fetches data once on module load. |
| `people.ts` | User and hearing officer lookups. Fetches data once on module load. |

### Key Types

```typescript
interface CourtDate {
    CourtDateID: number;
    CourtDate: Date;
    MuniCode: string;
    UploadDeadline: Date | null;
    CourtCases: number;
    Lifecycle: Lifecycle | null;    // Scheduled | Assigned | Uploaded | Adjourned
    HearingTime: string;
    HearingLink: string | null;
    Source: SourceType | null;      // manual | email | etrack
    Type: HearingType | null;       // Other | Virtual | InPerson
    FirstChair: number | null;
    SecondChair: number | null;
    HearingOfficer: number | null;
    IsAdjourned: boolean;
    AdjournmentDate: Date | null;
    Notes: string | null;
    NoticeFile: string | null;
}

interface Case {
    ParcelID: string;
    Municipality: string;
    SCARIndexNumber: string;
    SCARDeterminationAction: string;
    property_data: { Address: string; PropertyOwnerFull: string };
    evidence: Evidence | null;
    DateCompleted: Date | null;
    // ... plus Village SCAR fields
}
```

---

## CI/CD

Publishing is automated via GitHub Actions (`.github/workflows/publish.yml`).

**Trigger:** Every merged PR to `main`.

**Steps:**
1. Checkout code
2. Install dependencies (`npm ci`)
3. Build (`npm run build`)
4. Set version to `0.0.{run_number}` (auto-incrementing, no commit needed)
5. Publish to npm with provenance

**Required secrets:**
- `NPM_TOKEN` - npm access token with publish permissions for the `@propriety` scope

---

## Known Limitations

- **Port 8000 only:** The backend API enforces CORS restrictions that only allow requests from `localhost:8000`. This applies to the dev server and any consuming application running locally.
- **No test suite:** The project has no automated tests.
- **Module-level side effects:** Municipality names and user data are fetched automatically when their modules are imported, which means network requests happen at import time rather than on demand.
- **Cache is not invalidated on external changes:** If another user modifies a court date, the change won't appear until the local cache expires (5 minutes for dates, 1 hour for cases).
- **Single API key:** The component expects a single API key passed as a prop. There is no built-in token refresh or rotation mechanism.
- **No error UI:** API failures are logged to console but there are no user-facing error notifications (marked with TODO comments in the codebase).