@propriety/court-calendar 1.0.128 → 1.0.129

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.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: library-api-reviewer
3
+ description: Reviews changes to @propriety/court-calendar for public API compatibility — breaking prop changes, removed exports, peer dep violations. Invoke before opening a PR or publishing.
4
+ ---
5
+
6
+ You are a library API compatibility reviewer for `@propriety/court-calendar`.
7
+
8
+ Analyze the current diff or staged changes and identify:
9
+
10
+ 1. **Removed or renamed exports** — check `src/index.ts` for anything removed or renamed that consumers may depend on
11
+ 2. **Breaking prop changes** — removed props, changed types (stricter or different), new required props added to existing components
12
+ 3. **Peer dependency violations** — code that directly imports from `@mui/`, `@emotion/`, `react`, `react-dom` without accounting for them being peer deps (they must not be bundled)
13
+ 4. **Type regressions** — public TypeScript types that became more restrictive or were removed
14
+ 5. **Dexie schema changes** — any changes to IndexedDB stores in `src/` must handle version migrations
15
+ 6. **Context API surface** — changes to `ReferenceDataContext` shape affect all consumers of `useReferenceData()`
16
+
17
+ Output a verdict:
18
+ - **BREAKING** — consumers on the previous version will break on upgrade; needs a major version bump
19
+ - **NON-BREAKING** — new features or additions only; minor version bump appropriate
20
+ - **PATCH** — bug fixes with no API surface changes
21
+
22
+ Include specifics: which files changed, what the impact is, and the recommended semver bump.
@@ -0,0 +1,19 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": "Edit|Write",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node -e \"const i=JSON.parse(process.env.CLAUDE_TOOL_INPUT||'{}');const f=i.file_path||'';if((f.endsWith('.ts')||f.endsWith('.tsx'))&&!f.includes('dist/')){const cp=require('child_process');try{cp.execSync('npx biome format --write '+JSON.stringify(f),{stdio:'pipe',cwd:'c:/Users/Optiplex-BFHN/projects/court-calendar'})}catch(e){}}\""
10
+ },
11
+ {
12
+ "type": "command",
13
+ "command": "node -e \"const i=JSON.parse(process.env.CLAUDE_TOOL_INPUT||'{}');const f=i.file_path||'';if((f.endsWith('.ts')||f.endsWith('.tsx'))&&!f.includes('.test.')&&f.includes('src/')){const cp=require('child_process');try{cp.execSync('npx vitest run --related '+JSON.stringify(f),{stdio:'pipe',cwd:'c:/Users/Optiplex-BFHN/projects/court-calendar',timeout:30000})}catch(e){}}\""
14
+ }
15
+ ]
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: gen-test
3
+ description: Generate Vitest + React Testing Library tests for a court-calendar component or hook
4
+ ---
5
+
6
+ Generate tests for the `@propriety/court-calendar` library targeting the file or subject passed as $ARGUMENTS.
7
+
8
+ ## Project Test Conventions
9
+ - Test files live in `src/__tests__/` named `<subject>.test.ts(x)`
10
+ - Shared fixtures: `src/__tests__/fixtures.ts` — read this first for reusable test data
11
+ - Shared helpers: `src/__tests__/helpers/`
12
+ - Hook test utilities: `src/__tests__/hooks/`
13
+ - Vitest globals (`describe`, `it`, `expect`, `vi`) are auto-imported — no import needed
14
+ - Use `renderHook` from `@testing-library/react` for custom hooks
15
+ - Mock context modules: `vi.mock('@/context/ReferenceDataContext', () => ({ useReferenceData: vi.fn(() => ({ allUsers: [], allHearingOfficers: [], allMuniNames: [], isLoading: false })) }))`
16
+
17
+ ## Steps
18
+ 1. Read the target file to understand its props, return values, and side effects
19
+ 2. Read `src/__tests__/fixtures.ts` for available court case / event test data
20
+ 3. Scan `src/__tests__/helpers/` and `src/__tests__/hooks/` for setup utilities to reuse
21
+ 4. Write tests covering: happy path, edge cases, null/undefined inputs, and loading states
22
+ 5. Place output in `src/__tests__/<subject>.test.ts(x)`
23
+
24
+ Target: $ARGUMENTS
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: release-prep
3
+ description: Prepare @propriety/court-calendar for npm publish — runs all checks, prompts for version bump, builds dist
4
+ ---
5
+
6
+ Run the full release checklist for `@propriety/court-calendar`. Stop and report on first failure.
7
+
8
+ ## Steps (sequential — stop on failure)
9
+ 1. `npx tsc --noEmit` — type check must pass with zero errors
10
+ 2. `npx vitest run` — all tests must pass
11
+ 3. `npx biome check src/` — no lint or format violations
12
+ 4. Ask the user: **patch / minor / major** version bump? (default: patch)
13
+ - Update the `version` field in `package.json` accordingly
14
+ 5. `npm run build` — must succeed
15
+ 6. Verify `dist/index.mjs` and `dist/index.d.ts` exist and are non-empty
16
+ 7. Report: new version number, dist file sizes, ready to `npm publish`
17
+
18
+ Bump type from $ARGUMENTS (default: patch if not provided).
package/CLAUDE.md ADDED
@@ -0,0 +1,41 @@
1
+ # court-calendar
2
+
3
+ Published npm package: `@propriety/court-calendar` — a React component library built with Vite in library mode.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ npm run build # tsc -b && vite build → dist/
9
+ npx vitest run # run all tests
10
+ npx biome format --write <file> # format a file
11
+ npx biome check src/ # lint entire src
12
+ ```
13
+
14
+ ## Architecture
15
+
16
+ - **Entry**: `src/index.ts` → exports `CCalendar` component from `src/_components/CCalendar.tsx`
17
+ - **Path alias**: `@/` resolves to `./src/`
18
+ - **Build output**: `dist/index.mjs` (ES module) + `dist/index.d.ts` (types)
19
+ - **Peer deps** (not bundled): React, react-dom, MUI 5, Emotion — `vite.config.ts` externalizes these
20
+
21
+ ## Key Pattern: ReferenceDataContext
22
+
23
+ Reference data (users, hearing officers, municipalities) is fetched once in `ReferenceDataProvider` (wrapped inside `CCalendar`) and consumed via `useReferenceData()` in child components. Do **not** fetch this data at the module level or use top-level await.
24
+
25
+ Context is at `src/context/ReferenceDataContext.tsx` and provides: `allUsers`, `allHearingOfficers`, `allMuniNames`, `getTownshipName`, `getCountyName`, `isLoading`.
26
+
27
+ ## Runtime Config
28
+
29
+ `import.meta.env` values are baked in at **library build time**, not consumer runtime. Pass runtime config (API keys, endpoints) as React props and thread them through context — never read env vars in library code.
30
+
31
+ ## Component Props
32
+
33
+ `CCalendar` accepts:
34
+ - `apiKey` — runtime API key (passed through context, not env var)
35
+ - `activeUser` — the currently logged-in user object
36
+
37
+ ## Tooling
38
+
39
+ - **Formatter/Linter**: Biome — not Prettier or ESLint
40
+ - **Tests**: Vitest + @testing-library/react. Files: `src/__tests__/`, fixtures: `src/__tests__/fixtures.ts`
41
+ - **Pre-commit**: Husky runs on commit — run `npx biome format --write` before staging
package/dist/index.mjs CHANGED
@@ -20697,7 +20697,7 @@ function N1({
20697
20697
  bottom: 24,
20698
20698
  left: "50%",
20699
20699
  transform: "translateX(-50%)",
20700
- zIndex: 9999,
20700
+ zIndex: 100,
20701
20701
  pointerEvents: "none",
20702
20702
  animation: v === "in" ? "lb-slide-up 0.3s cubic-bezier(0.34,1.56,0.64,1) forwards" : v === "out" ? "lb-slide-down 0.45s ease forwards" : "none"
20703
20703
  },
@@ -20788,14 +20788,7 @@ function N1({
20788
20788
  children: b
20789
20789
  }
20790
20790
  ),
20791
- /* @__PURE__ */ a(
20792
- Zr,
20793
- {
20794
- size: 14,
20795
- thickness: 5,
20796
- sx: { color: "#8b5cf6", flexShrink: 0 }
20797
- }
20798
- )
20791
+ /* @__PURE__ */ a(Zr, { size: 14, thickness: 5, sx: { color: "#8b5cf6", flexShrink: 0 } })
20799
20792
  ]
20800
20793
  }
20801
20794
  ),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@propriety/court-calendar",
3
3
  "private": false,
4
- "version": "1.0.128",
4
+ "version": "1.0.129",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -1,8 +1,8 @@
1
- import { useEffect, useRef, useState } from "react";
2
- import humphreyImg from "@/assets/humphrey.png";
3
- import Box from "@mui/material/Box";
4
- import Typography from "@mui/material/Typography";
5
- import CircularProgress from "@mui/material/CircularProgress";
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import humphreyImg from '@/assets/humphrey.png';
3
+ import Box from '@mui/material/Box';
4
+ import Typography from '@mui/material/Typography';
5
+ import CircularProgress from '@mui/material/CircularProgress';
6
6
 
7
7
  interface LoadingBarProps {
8
8
  isFetchingDates: boolean;
@@ -13,8 +13,7 @@ interface LoadingBarProps {
13
13
  isFiltering?: boolean;
14
14
  }
15
15
 
16
- const GRADIENT =
17
- "linear-gradient(135deg, #3b82f6 0%, #8b5cf6 50%, #06b6d4 100%)";
16
+ const GRADIENT = 'linear-gradient(135deg, #3b82f6 0%, #8b5cf6 50%, #06b6d4 100%)';
18
17
 
19
18
  export default function LoadingBar({
20
19
  isFetchingDates,
@@ -26,19 +25,19 @@ export default function LoadingBar({
26
25
  }: LoadingBarProps) {
27
26
  const isLoading = isFetchingDates || isFetchingCases || isSearching || isFiltering;
28
27
  const [visible, setVisible] = useState(false);
29
- const [animating, setAnimating] = useState<"in" | "out" | "idle">("idle");
28
+ const [animating, setAnimating] = useState<'in' | 'out' | 'idle'>('idle');
30
29
  const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
31
30
 
32
31
  useEffect(() => {
33
32
  if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
34
33
  if (isLoading) {
35
34
  setVisible(true);
36
- setAnimating("in");
35
+ setAnimating('in');
37
36
  } else {
38
- setAnimating("out");
37
+ setAnimating('out');
39
38
  hideTimerRef.current = setTimeout(() => {
40
39
  setVisible(false);
41
- setAnimating("idle");
40
+ setAnimating('idle');
42
41
  }, 500);
43
42
  }
44
43
  return () => {
@@ -54,14 +53,14 @@ export default function LoadingBar({
54
53
  : Math.min(loadedCount / totalCount, 1);
55
54
 
56
55
  const label = isFetchingDates
57
- ? "Loading court dates…"
56
+ ? 'Loading court dates…'
58
57
  : isSearching
59
- ? "Searching…"
58
+ ? 'Searching…'
60
59
  : isFiltering && !isFetchingCases
61
- ? "Filtering…"
60
+ ? 'Filtering…'
62
61
  : progress !== null
63
62
  ? `Loading cases — ${loadedCount} / ${totalCount}`
64
- : "Loading…";
63
+ : 'Loading…';
65
64
 
66
65
  return (
67
66
  <>
@@ -69,48 +68,47 @@ export default function LoadingBar({
69
68
  {/* Outer positioning wrapper */}
70
69
  <Box
71
70
  sx={{
72
- position: "fixed",
71
+ position: 'fixed',
73
72
  bottom: 24,
74
- left: "50%",
75
- transform: "translateX(-50%)",
76
- zIndex: 9999,
77
- pointerEvents: "none",
73
+ left: '50%',
74
+ transform: 'translateX(-50%)',
75
+ zIndex: 100,
76
+ pointerEvents: 'none',
78
77
  animation:
79
- animating === "in"
80
- ? "lb-slide-up 0.3s cubic-bezier(0.34,1.56,0.64,1) forwards"
81
- : animating === "out"
82
- ? "lb-slide-down 0.45s ease forwards"
83
- : "none",
78
+ animating === 'in'
79
+ ? 'lb-slide-up 0.3s cubic-bezier(0.34,1.56,0.64,1) forwards'
80
+ : animating === 'out'
81
+ ? 'lb-slide-down 0.45s ease forwards'
82
+ : 'none',
84
83
  }}
85
84
  >
86
85
  {/* Gradient border shell + glow shadow */}
87
86
  <Box
88
87
  sx={{
89
88
  background: GRADIENT,
90
- borderRadius: "26px",
91
- p: "1.5px",
89
+ borderRadius: '26px',
90
+ p: '1.5px',
92
91
  boxShadow:
93
- "0 0 18px 2px rgba(139,92,246,0.35), 0 0 8px 1px rgba(59,130,246,0.25), 0 4px 16px rgba(0,0,0,0.18)",
92
+ '0 0 18px 2px rgba(139,92,246,0.35), 0 0 8px 1px rgba(59,130,246,0.25), 0 4px 16px rgba(0,0,0,0.18)',
94
93
  }}
95
94
  >
96
95
  {/* Inner content card */}
97
96
  <Box
98
97
  sx={{
99
- backgroundColor: "var(--bg)",
100
- borderRadius: "24px",
98
+ backgroundColor: 'var(--bg)',
99
+ borderRadius: '24px',
101
100
  px: 2.25,
102
101
  pt: 1,
103
102
  pb: progress !== null ? 1.25 : 1,
104
103
  minWidth: 220,
105
104
  maxWidth: 340,
106
- fontFamily:
107
- '"Roboto","Helvetica","Arial",sans-serif',
105
+ fontFamily: '"Roboto","Helvetica","Arial",sans-serif',
108
106
  }}
109
107
  >
110
108
  <Box
111
109
  sx={{
112
- display: "flex",
113
- alignItems: "center",
110
+ display: 'flex',
111
+ alignItems: 'center',
114
112
  gap: 1,
115
113
  mb: progress !== null ? 0.75 : 0,
116
114
  }}
@@ -118,7 +116,7 @@ export default function LoadingBar({
118
116
  {/* Humphrey avatar with gradient ring */}
119
117
  <Box
120
118
  sx={{
121
- position: "relative",
119
+ position: 'relative',
122
120
  flexShrink: 0,
123
121
  width: 28,
124
122
  height: 28,
@@ -126,43 +124,39 @@ export default function LoadingBar({
126
124
  >
127
125
  <Box
128
126
  sx={{
129
- position: "absolute",
127
+ position: 'absolute',
130
128
  inset: -1.5,
131
- borderRadius: "50%",
129
+ borderRadius: '50%',
132
130
  background: GRADIENT,
133
131
  zIndex: 0,
134
132
  }}
135
133
  />
136
134
  <Box
137
- component="img"
135
+ component='img'
138
136
  src={humphreyImg}
139
137
  sx={{
140
- position: "relative",
138
+ position: 'relative',
141
139
  zIndex: 1,
142
140
  width: 28,
143
141
  height: 28,
144
- borderRadius: "50%",
145
- objectFit: "cover",
146
- border: "1.5px solid var(--bg)",
142
+ borderRadius: '50%',
143
+ objectFit: 'cover',
144
+ border: '1.5px solid var(--bg)',
147
145
  }}
148
146
  />
149
147
  </Box>
150
148
  <Typography
151
- variant="body2"
149
+ variant='body2'
152
150
  sx={{
153
151
  fontWeight: 500,
154
- whiteSpace: "nowrap",
155
- color: "var(--text)",
152
+ whiteSpace: 'nowrap',
153
+ color: 'var(--text)',
156
154
  fontSize: 13,
157
155
  }}
158
156
  >
159
157
  {label}
160
158
  </Typography>
161
- <CircularProgress
162
- size={14}
163
- thickness={5}
164
- sx={{ color: "#8b5cf6", flexShrink: 0 }}
165
- />
159
+ <CircularProgress size={14} thickness={5} sx={{ color: '#8b5cf6', flexShrink: 0 }} />
166
160
  </Box>
167
161
 
168
162
  {progress !== null && (
@@ -170,17 +164,17 @@ export default function LoadingBar({
170
164
  sx={{
171
165
  height: 4,
172
166
  borderRadius: 1,
173
- backgroundColor: "var(--fc-border-color)",
174
- overflow: "hidden",
167
+ backgroundColor: 'var(--fc-border-color)',
168
+ overflow: 'hidden',
175
169
  }}
176
170
  >
177
171
  <Box
178
172
  sx={{
179
- height: "100%",
173
+ height: '100%',
180
174
  width: `${progress * 100}%`,
181
175
  borderRadius: 1,
182
176
  background: GRADIENT,
183
- transition: "width 0.3s ease",
177
+ transition: 'width 0.3s ease',
184
178
  }}
185
179
  />
186
180
  </Box>