@propriety/court-calendar 0.0.13 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,9 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(dir:*)",
5
- "Bash(ls:*)"
6
- ]
7
- }
8
- }
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(dir:*)",
5
+ "Bash(ls:*)",
6
+ "Bash(git log:*)"
7
+ ]
8
+ }
9
+ }
@@ -0,0 +1,28 @@
1
+ name: Build & Publish to npm
2
+
3
+ on:
4
+ pull_request:
5
+ types: [closed]
6
+ branches: [main]
7
+
8
+ permissions:
9
+ id-token: write
10
+ contents: read
11
+
12
+ jobs:
13
+ publish:
14
+ if: github.event.pull_request.merged == true
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: actions/setup-node@v4
21
+ with:
22
+ node-version: 24
23
+ registry-url: https://registry.npmjs.org
24
+
25
+ - run: npm ci
26
+ - run: npm run build
27
+ - run: npm version "0.0.${{ github.run_number }}" --no-git-tag-version
28
+ - run: npm publish --access public
package/README.md CHANGED
@@ -0,0 +1,237 @@
1
+ # @propriety/court-calendar
2
+
3
+ A React component library for managing court dates, cases, and evidence. Built with FullCalendar, MUI, and Dexie (IndexedDB caching).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @propriety/court-calendar
9
+ ```
10
+
11
+ You must also have the following peer dependencies installed:
12
+
13
+ ```bash
14
+ npm install react react-dom @mui/material @mui/icons-material @mui/x-data-grid @mui/x-date-pickers @emotion/react @emotion/styled
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```tsx
20
+ import { CCalendar } from '@propriety/court-calendar';
21
+ import '@propriety/court-calendar/styles.css';
22
+
23
+ function App() {
24
+ return <CCalendar apiKey="your-api-key" activeUser={1} />;
25
+ }
26
+ ```
27
+
28
+ ### Props
29
+
30
+ | Prop | Type | Description |
31
+ |------|------|-------------|
32
+ | `apiKey` | `string` | API key for authenticating with the Aventine API |
33
+ | `activeUser` | `number` | The current user's ID (used for chair assignments and edit tracking) |
34
+
35
+ ### CSS Import
36
+
37
+ You **must** import the stylesheet separately:
38
+
39
+ ```tsx
40
+ import '@propriety/court-calendar/styles.css';
41
+ ```
42
+
43
+ ### Theming
44
+
45
+ The component supports light and dark themes via the `data-theme` attribute on the document body:
46
+
47
+ ```tsx
48
+ document.body.setAttribute('data-theme', 'dark'); // or 'light'
49
+ ```
50
+
51
+ Components use the `.themed` CSS class to pick up theme variables.
52
+
53
+ ---
54
+
55
+ ## Development
56
+
57
+ ### Prerequisites
58
+
59
+ - Node.js 20+
60
+ - npm
61
+
62
+ ### Setup
63
+
64
+ ```bash
65
+ git clone git@github.com:Aventine-Git/court-calendar.git
66
+ cd court-calendar
67
+ npm install
68
+ ```
69
+
70
+ ### Dev Server
71
+
72
+ ```bash
73
+ npm run dev
74
+ ```
75
+
76
+ 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.
77
+
78
+ **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.
79
+
80
+ ### Environment Variables
81
+
82
+ Create a `.env` file in the project root:
83
+
84
+ ```
85
+ VITE_CALENDAR_API_KEY=your-api-key-here
86
+ ```
87
+
88
+ This key is used by the dev app and is accessed via `import.meta.env.VITE_CALENDAR_API_KEY`.
89
+
90
+ ### Build
91
+
92
+ ```bash
93
+ npm run build
94
+ ```
95
+
96
+ Runs TypeScript compilation (`tsc -b`) followed by the Vite library build. Output goes to `dist/`:
97
+
98
+ - `dist/index.mjs` - ES module bundle
99
+ - `dist/index.d.ts` - TypeScript type definitions
100
+ - `dist/court-calendar.css` - Compiled styles
101
+
102
+ The build externalizes React, React-DOM, MUI, and Emotion packages (they are peer dependencies and not bundled).
103
+
104
+ ### Lint
105
+
106
+ ```bash
107
+ npm run lint
108
+ ```
109
+
110
+ Uses ESLint with TypeScript plugin. Biome is also configured for formatting (4-space indent, 120 char line width, single quotes).
111
+
112
+ ---
113
+
114
+ ## Testing
115
+
116
+ There are currently no automated tests. Testing is done manually through the dev server.
117
+
118
+ ---
119
+
120
+ ## API
121
+
122
+ All data is fetched from `https://utils.aventine.ai`. Every request requires the `x-api-key` header.
123
+
124
+ ### Endpoints
125
+
126
+ | Endpoint | Method | Description |
127
+ |----------|--------|-------------|
128
+ | `/court-dates/all` | GET | Fetch all court dates |
129
+ | `/court-dates/create` | POST | Create a new court date |
130
+ | `/court-dates/{id}/update` | PUT | Update a court date |
131
+ | `/court-dates/{id}` | DELETE | Delete a court date |
132
+ | `/court-dates/{id}/cases` | GET | Fetch cases for a court date |
133
+ | `/court-cases/search?term=...&page=...` | GET | Search cases (paginated) |
134
+ | `/court-cases/filtering?page=...&page_size=...` | GET | List all cases (paginated) |
135
+ | `/court-cases/snooze/upload/{id}` | GET | Snooze upload deadline by 1 business day |
136
+ | `/users/all` | GET | Fetch all users |
137
+ | `/users/hearing-officers` | GET | Fetch hearing officers |
138
+ | `/utils/get-muni-names` | GET | Fetch municipality names |
139
+
140
+ ### Limitations
141
+
142
+ - **Port restriction:** The API only accepts requests from `localhost:8000`. Running the dev server on any other port will result in CORS failures.
143
+ - **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).
144
+ - **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.
145
+ - **No pagination for court dates:** `getAllDates` fetches every court date in a single request. For large datasets this could be slow.
146
+ - **Municipality and user data loads on import:** `munis.ts` and `people.ts` fire fetch requests at module load time, not lazily.
147
+
148
+ ---
149
+
150
+ ## Architecture
151
+
152
+ ### Components
153
+
154
+ | Component | Description |
155
+ |-----------|-------------|
156
+ | `CCalendar` | Root component. Manages state, events, filtering, and renders the calendar + modal. |
157
+ | `Toolbar` | Navigation and filter controls (date type, hearing type, case filters, user filter, view switcher, municipality dropdown, search). |
158
+ | `CalendarList` | MUI DataGrid table view of court dates with color-coded status, county, and location columns. |
159
+ | `Modal` | Dialog with 4 modes: Details, Edit, Create, Case Details. |
160
+ | `CreateEditCase` | Form for court date fields (dates, municipality, hearing details, chair assignments, case IDs). |
161
+ | `CaseViewer` | DataGrid listing cases for a selected court date. |
162
+ | `CaseDetails` | Read-only view of a single case with evidence and SCAR data. |
163
+
164
+ ### Helpers
165
+
166
+ | Helper | Description |
167
+ |--------|-------------|
168
+ | `courtDates.ts` | CRUD operations for court dates. Sends datetime strings combining date + hearing time. |
169
+ | `cases.ts` | Case fetching (by court date, paginated, search). Settlement and evidence checks. |
170
+ | `cache.ts` | Dexie-based IndexedDB caching with TTL (5 min for dates, 1 hour for cases). |
171
+ | `formatter.ts` | Date formatting for API (`YYYY-MM-DD` or `YYYY-MM-DD HH:mm:00`). 12h/24h time conversion. Evidence string formatting. |
172
+ | `munis.ts` | Municipality name lookups. Fetches data once on module load. |
173
+ | `people.ts` | User and hearing officer lookups. Fetches data once on module load. |
174
+
175
+ ### Key Types
176
+
177
+ ```typescript
178
+ interface CourtDate {
179
+ CourtDateID: number;
180
+ CourtDate: Date;
181
+ MuniCode: string;
182
+ UploadDeadline: Date | null;
183
+ CourtCases: number;
184
+ Lifecycle: Lifecycle | null; // Scheduled | Assigned | Uploaded | Adjourned
185
+ HearingTime: string;
186
+ HearingLink: string | null;
187
+ Source: SourceType | null; // manual | email | etrack
188
+ Type: HearingType | null; // Other | Virtual | InPerson
189
+ FirstChair: number | null;
190
+ SecondChair: number | null;
191
+ HearingOfficer: number | null;
192
+ IsAdjourned: boolean;
193
+ AdjournmentDate: Date | null;
194
+ Notes: string | null;
195
+ NoticeFile: string | null;
196
+ }
197
+
198
+ interface Case {
199
+ ParcelID: string;
200
+ Municipality: string;
201
+ SCARIndexNumber: string;
202
+ SCARDeterminationAction: string;
203
+ property_data: { Address: string; PropertyOwnerFull: string };
204
+ evidence: Evidence | null;
205
+ DateCompleted: Date | null;
206
+ // ... plus Village SCAR fields
207
+ }
208
+ ```
209
+
210
+ ---
211
+
212
+ ## CI/CD
213
+
214
+ Publishing is automated via GitHub Actions (`.github/workflows/publish.yml`).
215
+
216
+ **Trigger:** Every merged PR to `main`.
217
+
218
+ **Steps:**
219
+ 1. Checkout code
220
+ 2. Install dependencies (`npm ci`)
221
+ 3. Build (`npm run build`)
222
+ 4. Set version to `0.0.{run_number}` (auto-incrementing, no commit needed)
223
+ 5. Publish to npm with provenance
224
+
225
+ **Required secrets:**
226
+ - `NPM_TOKEN` - npm access token with publish permissions for the `@propriety` scope
227
+
228
+ ---
229
+
230
+ ## Known Limitations
231
+
232
+ - **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.
233
+ - **No test suite:** The project has no automated tests.
234
+ - **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.
235
+ - **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).
236
+ - **Single API key:** The component expects a single API key passed as a prop. There is no built-in token refresh or rotation mechanism.
237
+ - **No error UI:** API failures are logged to console but there are no user-facing error notifications (marked with TODO comments in the codebase).
@@ -1,5 +1,5 @@
1
1
  import { ModalMode, CalendarFilterCtx, Case, CourtDate } from '../../types';
2
- export default function CCModal({ modalIsOpen, modalMode, setModalMode, setIsOpen, selectedCourtDate, updateCourtDateInMemory, filterCtx, setFilterCtx, selectedCases, updateCases, deleteCases, isFetchingCases, apiKey, }: {
2
+ export default function CCModal({ modalIsOpen, modalMode, setModalMode, setIsOpen, selectedCourtDate, updateCourtDateInMemory, filterCtx, setFilterCtx, selectedCases, updateCases, deleteCases, isFetchingCases, apiKey, activeUser, }: {
3
3
  modalIsOpen: boolean;
4
4
  modalMode: ModalMode;
5
5
  setModalMode: (mode: ModalMode) => void;
@@ -13,4 +13,5 @@ export default function CCModal({ modalIsOpen, modalMode, setModalMode, setIsOpe
13
13
  deleteCases: (courtDateId: string) => void;
14
14
  isFetchingCases: boolean;
15
15
  apiKey: string;
16
+ activeUser: number;
16
17
  }): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { CourtDate } from '../types';
2
2
  export declare function getAllDates(apiKey: string): Promise<Array<CourtDate>>;
3
- export declare function updateCourtDate(courtDateId: number, updatedData: Partial<CourtDate>, apiKey: string, courtCases?: string[]): Promise<boolean>;
3
+ export declare function updateCourtDate(courtDateId: number, updatedData: Partial<CourtDate>, apiKey: string, courtCases?: string[], user?: number): Promise<boolean>;
4
4
  export declare function deleteCourtDate(courtDateId: number, apiKey: string): Promise<boolean>;
5
- export declare function createCourtDate(courtDate: Date, muniCode: string, apiKey: string, courtCases?: string[]): Promise<number | null>;
5
+ export declare function createCourtDate(courtDate: Date, muniCode: string, apiKey: string, courtCases?: string[], hearingTime?: string): Promise<number | null>;
6
6
  export declare function snoozeUploadDeadline(courtDateId: number, apiKey: string): Promise<boolean>;
7
7
  export declare function isVillageDate(muniCode: string): boolean;
@@ -1,3 +1,4 @@
1
1
  import { Evidence } from '../types';
2
2
  export declare function formatDateForAPI(date: Date): string;
3
+ export declare function formatDateTimeForAPI(date: Date, hearingTime?: string): string;
3
4
  export declare function formatEvidence(evidence: Evidence): string;