@propriety/court-calendar 1.0.154 → 1.0.155

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 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,8 +1,14 @@
1
1
  import { HistoryEntry } from '../../../helpers/dateHistory';
2
2
  /**
3
- * Compact, newest-first trail of everything that has moved this court date — reschedules
4
- * and bulk case adjournments. Renders nothing when there is no history, so unchanged court
5
- * dates keep their header exactly as it was.
3
+ * The trail of everything that has moved this court date.
4
+ *
5
+ * Reschedules render as one left-to-right path — `Feb 23 → Mar 23 → Aug 5` — rather than
6
+ * one chip per log row, so the order reads the way the arrows point and a date shared by
7
+ * two consecutive moves is only printed once. Bulk case adjournments are a different kind
8
+ * of event (they move cases off this date, not the date itself) and keep their own row.
9
+ *
10
+ * Renders nothing when there is no history, so unchanged court dates keep their header
11
+ * exactly as it was.
6
12
  */
7
13
  export default function DateHistory({ entries, align, }: {
8
14
  entries: HistoryEntry[];
@@ -0,0 +1,22 @@
1
+ import { AdjournmentEntry, Case, CourtDate } from '../../../types';
2
+ export interface MotionToast {
3
+ severity: 'success' | 'error' | 'info';
4
+ text: string;
5
+ }
6
+ /**
7
+ * The Motion to Preclude panel: window status and the two actions.
8
+ *
9
+ * Both buttons render for every SCAR date rather than appearing only when usable. The
10
+ * window is a few weeks out of a court date's months-long life, and when the controls were
11
+ * hidden outside it nobody discovered the feature existed — so they stay put and explain
12
+ * themselves through the status line and their tooltips.
13
+ *
14
+ * Callers gate on `isScarDate`: negotiation and collections dates never carry a motion.
15
+ */
16
+ export default function MotionToPreclude({ courtDate, township, adjournments, cases, onNotify, }: {
17
+ courtDate: CourtDate;
18
+ township: string;
19
+ adjournments?: AdjournmentEntry[];
20
+ cases?: Pick<Case, 'AdjournedDate'>[];
21
+ onNotify: (toast: MotionToast) => void;
22
+ }): import("react/jsx-runtime").JSX.Element;
@@ -31,6 +31,8 @@ export declare function parseHistoryDate(value: Date | string | null | undefined
31
31
  * than a meaningless "User 420".
32
32
  */
33
33
  export declare function resolveUserName(updatedBy: number | string | null | undefined, allUsers: Record<number, User>): string;
34
+ /** Same calendar day, ignoring the time component the edit log carries. */
35
+ export declare function isSameDay(a: Date | null, b: Date | null): boolean;
34
36
  /**
35
37
  * Merge the court date's reschedule trail with its case adjournments into one
36
38
  * time-ordered list, newest first.
@@ -40,6 +42,31 @@ export declare function resolveUserName(updatedBy: number | string | null | unde
40
42
  * be 20 identical chips — CourtDateID 296 is exactly that shape in production.
41
43
  */
42
44
  export declare function buildDateHistory(courtDateHistory: DateEditItem[], adjournments: AdjournmentEntry[], allUsers: Record<number, User>): HistoryEntry[];
45
+ /** One date the court date has sat on, as a stop along the reschedule trail. */
46
+ export interface TrailNode {
47
+ /** Stable React key. */
48
+ key: string;
49
+ date: Date | null;
50
+ /** The reschedule that landed on this date. Null on the stop a run starts from. */
51
+ entry: HistoryEntry | null;
52
+ /** False when this stop does not continue the one before it — the log has a hole. */
53
+ connected: boolean;
54
+ }
55
+ /**
56
+ * Flatten the reschedule entries into the ordered path of dates this court date has
57
+ * actually sat on, oldest first.
58
+ *
59
+ * Consecutive log rows chain: a row's `OldValue` is the previous row's `NewValue`. So
60
+ * three separate "A → B", "B → C", "C → D" chips are really one path, A → B → C → D.
61
+ * Rendering the path instead of the edges drops the repeated endpoints and puts the
62
+ * events in reading order, which the newest-first chip list could not do.
63
+ *
64
+ * When a row's origin does not match the previous stop the trail has a hole — rows are
65
+ * only written for edits made through the app, so a direct DB change leaves no row. That
66
+ * stop is marked `connected: false` so the UI can show a break rather than imply a move
67
+ * that never happened.
68
+ */
69
+ export declare function buildRescheduleTrail(entries: HistoryEntry[]): TrailNode[];
43
70
  /**
44
71
  * The adjournment rows belonging to a single case.
45
72
  *
@@ -1,4 +1,11 @@
1
1
  import { Evidence } from '../types';
2
+ /**
3
+ * MM/DD/YYYY for display, or "-" when there is no date.
4
+ *
5
+ * Routed through `parseLocalDate` so a date-only string keeps its calendar day: `new
6
+ * Date("2026-08-14")` is UTC midnight, which renders as 08/13 in any US timezone.
7
+ */
8
+ export declare function formatDisplayDate(date: Date | string | null | undefined): string;
2
9
  export declare function formatDateForAPI(date: Date): string;
3
10
  export declare function formatDateTimeForAPI(date: Date, hearingTime?: string): string;
4
11
  export declare function to24Hour(time: string): string;
@@ -0,0 +1,60 @@
1
+ import { AdjournmentEntry, Case, CourtDate } from '../types';
2
+ /**
3
+ * Where a court date sits relative to its Motion to Preclude upload window.
4
+ *
5
+ * The window runs from `MotionDate` — the *first* day a motion can be uploaded, which the
6
+ * backend computes as four business days before the hearing — through the hearing itself.
7
+ * It is not a countdown to a deadline: `MotionDate` opens the window, the hearing closes it.
8
+ */
9
+ export type MotionWindowState = 'no-motion-date' | 'skipped' | 'not-yet-open' | 'open' | 'closed';
10
+ export interface MotionWindow {
11
+ state: MotionWindowState;
12
+ /** A draft PDF can be produced for review. Never files anything. */
13
+ canDownload: boolean;
14
+ /** The motion can be uploaded to NYSCEF right now. */
15
+ canUpload: boolean;
16
+ /** First day uploading is possible — the court date's MotionDate. */
17
+ opensOn: Date | null;
18
+ /** Last day uploading is possible — the hearing, pushed out by any adjournment. */
19
+ closesOn: Date | null;
20
+ /** Last *scheduled* hearing day: CourtDate, or the final entry of MultipleDates. */
21
+ hearingOn: Date | null;
22
+ /** Set only when an adjournment moved the hearing past `hearingOn`, else null. */
23
+ adjournedTo: Date | null;
24
+ }
25
+ /**
26
+ * Midnight-local for any shape the API hands back, without mutating the input.
27
+ *
28
+ * Date-only strings must not go through `new Date(s)` — that reads "2026-08-14" as UTC
29
+ * midnight, which is the 13th anywhere west of Greenwich. Every comparison in this module
30
+ * is day-granular, so one bad parse is a whole day of wrong answer.
31
+ */
32
+ export declare function startOfDay(value: Date | string | null | undefined): Date | null;
33
+ /**
34
+ * Resolves the Motion to Preclude upload window for a court date.
35
+ *
36
+ * The close boundary is the *effective* hearing day, which is the latest of:
37
+ * - `CourtDate`
38
+ * - every entry in `MultipleDates` (a multi-day hearing is not over until its last day)
39
+ * - every adjourned-to date belonging to this court date
40
+ *
41
+ * Taking the maximum across adjournments is deliberate. Adjournments are per case, so a
42
+ * date can be part-adjourned; as long as one case still has a hearing ahead of it the
43
+ * motion is live. Which cases actually get a motion is the backend's call —
44
+ * `_get_active_indices_for_motion` filters them server-side — so gating the button on the
45
+ * most permissive boundary avoids the frontend refusing an upload the backend would accept.
46
+ *
47
+ * @param courtDate The selected date. Non-SCAR dates never carry a motion; callers gate
48
+ * on `isScarDate` before rendering the control at all.
49
+ * @param adjournments Rows from `GET /court-dates/{id}` — already scoped to this date, but
50
+ * re-checked here because `AdjournmentEntry.HearingDate` holds the
51
+ * originating CourtDateID (yes, an id in a field named for a date).
52
+ * @param cases The date's cases; `AdjournedDate` is a second, independent record of
53
+ * the same adjournment and covers a detail fetch that has not landed.
54
+ * @param today Injectable for tests. Defaults to now.
55
+ */
56
+ export declare function computeMotionWindow(courtDate: CourtDate | null | undefined, { adjournments, cases, today, }?: {
57
+ adjournments?: AdjournmentEntry[];
58
+ cases?: Pick<Case, 'AdjournedDate'>[];
59
+ today?: Date;
60
+ }): MotionWindow;