hazo_collab_forms 5.7.0 → 5.9.0

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.
Files changed (105) hide show
  1. package/CHANGE_LOG.md +67 -0
  2. package/README.md +118 -0
  3. package/dist/components/field_audit/auditor.d.ts +30 -0
  4. package/dist/components/field_audit/auditor.d.ts.map +1 -0
  5. package/dist/components/field_audit/auditor.js +91 -0
  6. package/dist/components/field_audit/auditor.js.map +1 -0
  7. package/dist/components/field_audit/context.d.ts +29 -0
  8. package/dist/components/field_audit/context.d.ts.map +1 -0
  9. package/dist/components/field_audit/context.js +123 -0
  10. package/dist/components/field_audit/context.js.map +1 -0
  11. package/dist/components/field_audit/field_audit_icon.d.ts +12 -0
  12. package/dist/components/field_audit/field_audit_icon.d.ts.map +1 -0
  13. package/dist/components/field_audit/field_audit_icon.js +23 -0
  14. package/dist/components/field_audit/field_audit_icon.js.map +1 -0
  15. package/dist/components/field_audit/field_audit_panel.d.ts +9 -0
  16. package/dist/components/field_audit/field_audit_panel.d.ts.map +1 -0
  17. package/dist/components/field_audit/field_audit_panel.js +54 -0
  18. package/dist/components/field_audit/field_audit_panel.js.map +1 -0
  19. package/dist/components/field_audit/index.d.ts +28 -0
  20. package/dist/components/field_audit/index.d.ts.map +1 -0
  21. package/dist/components/field_audit/index.js +24 -0
  22. package/dist/components/field_audit/index.js.map +1 -0
  23. package/dist/components/field_audit/types.d.ts +75 -0
  24. package/dist/components/field_audit/types.d.ts.map +1 -0
  25. package/dist/components/field_audit/types.js +10 -0
  26. package/dist/components/field_audit/types.js.map +1 -0
  27. package/dist/components/field_audit/with_field_audit.d.ts +32 -0
  28. package/dist/components/field_audit/with_field_audit.d.ts.map +1 -0
  29. package/dist/components/field_audit/with_field_audit.js +42 -0
  30. package/dist/components/field_audit/with_field_audit.js.map +1 -0
  31. package/dist/components/hazo_collab_form_checkbox.d.ts.map +1 -1
  32. package/dist/components/hazo_collab_form_checkbox.js +3 -1
  33. package/dist/components/hazo_collab_form_checkbox.js.map +1 -1
  34. package/dist/components/hazo_collab_form_doc.d.ts.map +1 -1
  35. package/dist/components/hazo_collab_form_doc.js +4 -1
  36. package/dist/components/hazo_collab_form_doc.js.map +1 -1
  37. package/dist/components/hazo_collab_form_radio.d.ts.map +1 -1
  38. package/dist/components/hazo_collab_form_radio.js +4 -2
  39. package/dist/components/hazo_collab_form_radio.js.map +1 -1
  40. package/dist/components/hazo_collab_form_view/context.d.ts +7 -0
  41. package/dist/components/hazo_collab_form_view/context.d.ts.map +1 -1
  42. package/dist/components/hazo_collab_form_view/context.js +38 -0
  43. package/dist/components/hazo_collab_form_view/context.js.map +1 -1
  44. package/dist/components/hazo_collab_form_view/index.d.ts +1 -1
  45. package/dist/components/hazo_collab_form_view/index.d.ts.map +1 -1
  46. package/dist/components/hazo_collab_form_view/index.js +13 -1
  47. package/dist/components/hazo_collab_form_view/index.js.map +1 -1
  48. package/dist/components/hazo_collab_form_view/types.d.ts +98 -0
  49. package/dist/components/hazo_collab_form_view/types.d.ts.map +1 -1
  50. package/dist/components/hazo_collab_form_view/views/approval_view.d.ts.map +1 -1
  51. package/dist/components/hazo_collab_form_view/views/approval_view.js +3 -2
  52. package/dist/components/hazo_collab_form_view/views/approval_view.js.map +1 -1
  53. package/dist/components/hazo_collab_form_view/views/print_view.d.ts.map +1 -1
  54. package/dist/components/hazo_collab_form_view/views/print_view.js +3 -2
  55. package/dist/components/hazo_collab_form_view/views/print_view.js.map +1 -1
  56. package/dist/components/hazo_collab_form_view/views/summary_view.d.ts.map +1 -1
  57. package/dist/components/hazo_collab_form_view/views/summary_view.js +3 -2
  58. package/dist/components/hazo_collab_form_view/views/summary_view.js.map +1 -1
  59. package/dist/components/hazo_data_form/group_renderer.d.ts +6 -2
  60. package/dist/components/hazo_data_form/group_renderer.d.ts.map +1 -1
  61. package/dist/components/hazo_data_form/group_renderer.js +3 -3
  62. package/dist/components/hazo_data_form/group_renderer.js.map +1 -1
  63. package/dist/components/hazo_data_form/hazo_data_form.d.ts +2 -1
  64. package/dist/components/hazo_data_form/hazo_data_form.d.ts.map +1 -1
  65. package/dist/components/hazo_data_form/hazo_data_form.js +13 -4
  66. package/dist/components/hazo_data_form/hazo_data_form.js.map +1 -1
  67. package/dist/components/hazo_data_form/section_renderer.d.ts +3 -2
  68. package/dist/components/hazo_data_form/section_renderer.d.ts.map +1 -1
  69. package/dist/components/hazo_data_form/section_renderer.js +2 -2
  70. package/dist/components/hazo_data_form/section_renderer.js.map +1 -1
  71. package/dist/components/hazo_data_form/shared/data_form_field_layout.d.ts +1 -1
  72. package/dist/components/hazo_data_form/shared/data_form_field_layout.d.ts.map +1 -1
  73. package/dist/components/hazo_data_form/shared/data_form_field_layout.js +19 -8
  74. package/dist/components/hazo_data_form/shared/data_form_field_layout.js.map +1 -1
  75. package/dist/components/hazo_data_form/types.d.ts +31 -1
  76. package/dist/components/hazo_data_form/types.d.ts.map +1 -1
  77. package/dist/components/index.d.ts +5 -1
  78. package/dist/components/index.d.ts.map +1 -1
  79. package/dist/components/index.js +4 -0
  80. package/dist/components/index.js.map +1 -1
  81. package/dist/components/shared/base_field_layout.d.ts.map +1 -1
  82. package/dist/components/shared/base_field_layout.js +5 -1
  83. package/dist/components/shared/base_field_layout.js.map +1 -1
  84. package/dist/components/shared/field_action_slot.d.ts +22 -0
  85. package/dist/components/shared/field_action_slot.d.ts.map +1 -0
  86. package/dist/components/shared/field_action_slot.js +20 -0
  87. package/dist/components/shared/field_action_slot.js.map +1 -0
  88. package/dist/components/shared/use_field_action_slot.d.ts +32 -0
  89. package/dist/components/shared/use_field_action_slot.d.ts.map +1 -0
  90. package/dist/components/shared/use_field_action_slot.js +52 -0
  91. package/dist/components/shared/use_field_action_slot.js.map +1 -0
  92. package/dist/components/thread_form/components/key_info_drawer.d.ts +7 -1
  93. package/dist/components/thread_form/components/key_info_drawer.d.ts.map +1 -1
  94. package/dist/components/thread_form/components/key_info_drawer.js +2 -2
  95. package/dist/components/thread_form/components/key_info_drawer.js.map +1 -1
  96. package/dist/components/thread_form/index.d.ts +1 -1
  97. package/dist/components/thread_form/index.d.ts.map +1 -1
  98. package/dist/components/thread_form/index.js.map +1 -1
  99. package/dist/components/thread_form/thread_form.d.ts.map +1 -1
  100. package/dist/components/thread_form/thread_form.js +2 -2
  101. package/dist/components/thread_form/thread_form.js.map +1 -1
  102. package/dist/components/thread_form/types.d.ts +29 -1
  103. package/dist/components/thread_form/types.d.ts.map +1 -1
  104. package/dist/components/thread_form/types.js.map +1 -1
  105. package/package.json +1 -1
package/CHANGE_LOG.md CHANGED
@@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## 5.9.0
11
+
12
+ - **NEW**: Built-in `FieldAudit` per-field change history.
13
+ - New `enable_field_audit?: boolean` convenience flag on
14
+ `HazoCollabFormView`, `HazoDataForm` (collab variant), and `ThreadForm`
15
+ (forwarded to the Key-Info drawer's embedded HazoCollabFormView).
16
+ When `true`, the form mounts a `FieldAuditProvider`, auto-records each
17
+ `form_data` change as a `manual` entry, and overlays an inline clock
18
+ icon (`row_end`) + history panel (`row_below`) into every scalar field
19
+ row. Consumer-supplied `field_action_components` win on collision.
20
+ - **Per-field debounce (default 750ms).** Controlled inputs fire on
21
+ every keystroke; the watcher coalesces a typing burst into ONE entry
22
+ whose `prev_value` is the value at the start of the burst and
23
+ `new_value` is the value when typing stopped. Reverting to the
24
+ baseline value within the window cancels the pending entry.
25
+ Tune via `audit_debounce_ms?: number` on each form view.
26
+ - Imperative API for non-manual sources: `useFieldAudit().record_change(
27
+ { field_id, source: 'clarification' | 'manual', new_value, prev_value, user })`.
28
+ When `record_change` is called within ~500ms of an auto-recorded
29
+ `manual` entry for the same value, the existing entry is promoted to
30
+ the more specific source instead of duplicated.
31
+ - In-session storage only — no persistence on reload. Pair with a
32
+ backend write inside `on_field_change` if you need durability.
33
+ - Intended for the tax-agent surface; omit the flag on the client
34
+ portal to hide audit history.
35
+ - New exports from the package root:
36
+ `FieldAuditProvider`, `FieldAuditIcon`, `FieldAuditPanel`,
37
+ `FormDataAuditor`, `useFieldAudit`, `useFieldAuditOptional`,
38
+ plus the `AuditEntry`, `AuditSource`, `RecordChangeInput`,
39
+ `FieldAuditContextValue` types.
40
+ - **NEW**: `field_action_components?: FieldActionComponents` and
41
+ `enable_field_audit?: boolean` on `ThreadFormProps`. Both forward
42
+ through to the embedded Key-Info `HazoCollabFormView`. Without the
43
+ Key-Info drawer (`key_info_sections` omitted), neither prop has any
44
+ effect.
45
+ - Backward-compatible: when neither flag is set, the rendered output is
46
+ byte-identical to 5.8.0.
47
+
48
+ ## 5.8.0
49
+
50
+ - **NEW**: `field_action_components?: FieldActionComponents` prop on
51
+ `HazoCollabFormView` and `HazoDataForm`. Lets consumers render per-field
52
+ React components (audit icon, RFI button, custom popover, inline audit
53
+ log) next to every scalar field row.
54
+ - **Two slot positions, independently or together:**
55
+ - `row_end` — inline at the end of the row, after the value column,
56
+ file column, and `UnifiedFieldControls`.
57
+ - `row_below` — full-width block immediately below the row, before the
58
+ next field begins. Use for inline audit logs, validation
59
+ explanations, expandable detail panels.
60
+ - Each slot receives a `FieldActionContext` with `field`, `value`, `mode`,
61
+ `section_id`, and `group_id`. `mode` is widened to
62
+ `ViewMode | 'edit' | 'view'` so the same component handles both forms.
63
+ - Fires in every view mode of `HazoCollabFormView`
64
+ (`edit | summary | print | approval`) and both modes of `HazoDataForm`
65
+ (`edit | view`).
66
+ - Scalar fields only — inputbox, textarea, currency, percentage,
67
+ computed, date, combo, radio, checkbox, masked, static text, summary
68
+ row, and `HazoCollabFormDoc`. Group header rows, data table fields,
69
+ per-cell positions inside tables, and `HazoCollabFormSplitAmount`
70
+ do not fire the slot in this release. The consumer can return `null`
71
+ from either slot to opt out per-field.
72
+ - New helper `<FieldActionSlot />` exported from the package root —
73
+ consumers normally don't import it directly; it's the internal renderer.
74
+ - Backward-compatible: when the prop is omitted the rendered DOM is
75
+ byte-identical to 5.7.0.
76
+
10
77
  ## 5.7.0
11
78
 
12
79
  - **NEW**: `AutofillDropzone` accepts an optional `upload_manager?: ExternalUploadManager` prop. When set, file drops are delegated to the external manager instead of running the inline `upload → extract → conflict` pipeline. `on_autofill_complete` is not called in this mode; the consuming app handles merging via its own `job:completed` subscription. The `OverwriteConfirmDialog` is not rendered in this mode.
package/README.md CHANGED
@@ -790,6 +790,124 @@ const normalized = normalize_field_metadata_input(input);
790
790
  // Result: { visibility: 'hidden', data_ok: 'ok', locked: true }
791
791
  ```
792
792
 
793
+ ### Per-field action slots (`field_action_components`)
794
+
795
+ Render consumer-owned components inline next to every scalar field row. Use
796
+ this for audit-history icons, RFI buttons, validation explanations, or any
797
+ per-field detail the form itself does not own.
798
+
799
+ Available on both `HazoCollabFormView` (all four modes — `edit | summary |
800
+ print | approval`) and `HazoDataForm` (`edit | view`).
801
+
802
+ Two slot positions, used independently or together:
803
+
804
+ | Slot | Position |
805
+ |---|---|
806
+ | `row_end` | Inline at the end of the row, after the field's value column, file column, and `UnifiedFieldControls`. Right place for a small icon or pill. |
807
+ | `row_below` | Full-width block, immediately below the row, before the next field starts. Right place for an inline audit log, validation panel, or expandable detail. |
808
+
809
+ ```typescript
810
+ 'use client';
811
+
812
+ import { HazoCollabFormView, type FieldActionContext } from 'hazo_collab_forms';
813
+ import { FieldAuditIcon } from '@/lib/audit/ui/FieldAuditIcon';
814
+ import { FieldAuditHistory } from '@/lib/audit/ui/FieldAuditHistory';
815
+
816
+ const field_actions = {
817
+ // Clock icon at the end of every row
818
+ row_end: ({ field }: FieldActionContext) => (
819
+ <FieldAuditIcon
820
+ subjectKind="client_folder"
821
+ subjectId={folderId}
822
+ fieldPath={field.id}
823
+ />
824
+ ),
825
+ // Inline history panel below every row (returns null when there's no
826
+ // history for the given field)
827
+ row_below: ({ field }: FieldActionContext) => (
828
+ <FieldAuditHistory
829
+ subjectKind="client_folder"
830
+ subjectId={folderId}
831
+ fieldPath={field.id}
832
+ />
833
+ ),
834
+ };
835
+
836
+ <HazoCollabFormView
837
+ mode="edit"
838
+ sections={sections}
839
+ form_data={form_data}
840
+ field_action_components={field_actions}
841
+ />
842
+ ```
843
+
844
+ The `FieldActionContext` passed to `row_end` contains:
845
+
846
+ | Field | Type | Notes |
847
+ |---|---|---|
848
+ | `field` | `ViewFieldConfig` | The same field config rendered in the row. |
849
+ | `value` | `unknown` | The current `form_data[field.id]`. |
850
+ | `mode` | `ViewMode \| 'edit' \| 'view'` | Active render mode. |
851
+ | `section_id` | `string?` | `section.section_name` when available. |
852
+ | `group_id` | `string?` | Id of the nearest enclosing group, when available. |
853
+
854
+ Return `null` from `row_end` to render nothing for a given field. When the
855
+ prop is omitted entirely the rendered DOM is byte-identical to the previous
856
+ version.
857
+
858
+ **Coverage note:** the slot fires for scalar fields only — inputbox,
859
+ textarea, currency, percentage, computed, date, combo, radio, checkbox,
860
+ masked, static text, summary row, and `HazoCollabFormDoc`. Group header
861
+ rows, data table fields (`HazoCollabFormDataTable` / `table`), per-cell
862
+ positions inside tables, and `HazoCollabFormSplitAmount` are not slotted in
863
+ this release. `HazoFbForm` is out of scope for this prop. `ThreadForm`
864
+ forwards both `field_action_components` and `enable_field_audit` to its
865
+ embedded Key-Info `HazoCollabFormView` since v5.9.0.
866
+
867
+ ### Built-in FieldAudit (`enable_field_audit`)
868
+
869
+ Since v5.9.0, the package ships a ready-made audit history feature you can
870
+ turn on with a single flag.
871
+
872
+ ```typescript
873
+ <HazoCollabFormView
874
+ mode="edit"
875
+ sections={sections}
876
+ form_data={form_data}
877
+ on_form_data_change={set_form_data}
878
+ current_user={current_user}
879
+ enable_field_audit // <-- that's the whole feature
880
+ />
881
+ ```
882
+
883
+ When `true`:
884
+
885
+ - a `FieldAuditProvider` is mounted around the form,
886
+ - a `FormDataAuditor` auto-records each value change as a `source: 'manual'`
887
+ entry, **debounced per field at 750ms by default** (tune with
888
+ `audit_debounce_ms`) so a typing burst becomes one entry, not one per
889
+ keystroke,
890
+ - a small clock icon is overlaid into `row_end` and an inline history
891
+ panel into `row_below` for every scalar field.
892
+
893
+ Any consumer-supplied `field_action_components` wins on collision, so you
894
+ can mix the built-in audit with your own custom slot in the other
895
+ position.
896
+
897
+ For non-manual sources (e.g. a clarification flow that resolves into a
898
+ value update), call `useFieldAudit().record_change({ field_id,
899
+ source: 'clarification', new_value, prev_value, user })` from your
900
+ handler. If the auto-watcher then sees the same value within ~500ms it
901
+ upgrades its existing `manual` entry to `'clarification'` instead of
902
+ duplicating it.
903
+
904
+ The audit lives entirely in-session — there is no persistence on reload.
905
+ If you need durable audit logs, pair the flag with your own write inside
906
+ `on_field_change`.
907
+
908
+ **Visibility:** intended for the tax-agent surface of a workflow. Omit
909
+ the flag on the client portal to hide audit history.
910
+
793
911
  ### Confirm-required Field State (H.7)
794
912
 
795
913
  Use `confirm_state` in `field_metadata` to mark prefilled fields that a user must review and confirm. Three states: `'prefilled'` (AI/system-filled, needs review), `'confirmed'` (user approved), `'edited'` (user changed the value).
@@ -0,0 +1,30 @@
1
+ /**
2
+ * FormDataAuditor — invisible component that diffs `form_data` between
3
+ * renders and records each changed field as a `manual` audit entry.
4
+ *
5
+ * Coalescing strategy: controlled inputs fire on every keystroke, which
6
+ * would produce one audit entry per character. Instead, we debounce per
7
+ * field — when a field changes we (re)start a per-field timer, and once
8
+ * the user has been idle for `debounce_ms` we record ONE entry whose
9
+ * `prev_value` is the last committed value and whose `new_value` is the
10
+ * latest current value. The result is one entry per typing burst.
11
+ *
12
+ * On unmount any pending timers are flushed so we don't lose unfinished
13
+ * edits. Internal keys prefixed with `__` (file attachments,
14
+ * clarification storage, etc.) are ignored.
15
+ *
16
+ * Renders nothing.
17
+ */
18
+ export interface FormDataAuditorProps {
19
+ form_data: Record<string, unknown> | undefined;
20
+ current_user?: string;
21
+ /**
22
+ * Milliseconds of idle time after the last change before an entry is
23
+ * recorded. Keystrokes within the window are coalesced into a single
24
+ * audit entry. Defaults to 750ms — enough for normal typing pauses but
25
+ * short enough that tabbing out feels immediate.
26
+ */
27
+ debounce_ms?: number;
28
+ }
29
+ export declare function FormDataAuditor({ form_data, current_user, debounce_ms, }: FormDataAuditorProps): null;
30
+ //# sourceMappingURL=auditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditor.d.ts","sourceRoot":"","sources":["../../../src/components/field_audit/auditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AASD,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,YAAY,EACZ,WAAiB,GAClB,EAAE,oBAAoB,GAAG,IAAI,CAgF7B"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * FormDataAuditor — invisible component that diffs `form_data` between
3
+ * renders and records each changed field as a `manual` audit entry.
4
+ *
5
+ * Coalescing strategy: controlled inputs fire on every keystroke, which
6
+ * would produce one audit entry per character. Instead, we debounce per
7
+ * field — when a field changes we (re)start a per-field timer, and once
8
+ * the user has been idle for `debounce_ms` we record ONE entry whose
9
+ * `prev_value` is the last committed value and whose `new_value` is the
10
+ * latest current value. The result is one entry per typing burst.
11
+ *
12
+ * On unmount any pending timers are flushed so we don't lose unfinished
13
+ * edits. Internal keys prefixed with `__` (file attachments,
14
+ * clarification storage, etc.) are ignored.
15
+ *
16
+ * Renders nothing.
17
+ */
18
+ 'use client';
19
+ import { useEffect, useRef } from 'react';
20
+ import { useFieldAuditOptional } from './context.js';
21
+ export function FormDataAuditor({ form_data, current_user, debounce_ms = 750, }) {
22
+ const audit = useFieldAuditOptional();
23
+ // Last committed values — what we'll use as `prev_value` when the next
24
+ // debounced entry fires. Starts at the initial form_data so we don't
25
+ // emit a synthetic "everything changed" entry on first mount.
26
+ const committed_ref = useRef({ ...(form_data ?? {}) });
27
+ // Live form_data, kept fresh via render so the debounced timer reads
28
+ // the latest value at fire time (not the value at the time we set the timer).
29
+ const live_ref = useRef(form_data ?? {});
30
+ live_ref.current = form_data ?? {};
31
+ // Per-field debounce state. `current_user` is captured into the closure.
32
+ const pending_ref = useRef(new Map());
33
+ // Diff each render and schedule audit entries.
34
+ useEffect(() => {
35
+ if (!audit)
36
+ return;
37
+ const prev = committed_ref.current;
38
+ const curr = form_data ?? {};
39
+ const pending = pending_ref.current;
40
+ const keys = new Set([...Object.keys(prev), ...Object.keys(curr)]);
41
+ for (const key of keys) {
42
+ if (key.startsWith('__'))
43
+ continue;
44
+ const baseline = pending.get(key)?.baseline ?? prev[key];
45
+ const after = curr[key];
46
+ // If the value is back at the baseline (e.g. user typed then erased),
47
+ // cancel any pending entry and forget the field.
48
+ if (Object.is(baseline, after)) {
49
+ const existing = pending.get(key);
50
+ if (existing) {
51
+ clearTimeout(existing.timer);
52
+ pending.delete(key);
53
+ }
54
+ continue;
55
+ }
56
+ // Restart the per-field debounce.
57
+ const existing = pending.get(key);
58
+ if (existing) {
59
+ clearTimeout(existing.timer);
60
+ }
61
+ const timer = setTimeout(() => {
62
+ const latest = live_ref.current[key];
63
+ const prev_value = pending.get(key)?.baseline ?? prev[key];
64
+ pending.delete(key);
65
+ if (Object.is(prev_value, latest))
66
+ return;
67
+ audit.record_change({
68
+ field_id: key,
69
+ source: 'manual',
70
+ prev_value,
71
+ new_value: latest,
72
+ user: current_user,
73
+ });
74
+ committed_ref.current = { ...committed_ref.current, [key]: latest };
75
+ }, debounce_ms);
76
+ pending.set(key, { baseline, timer });
77
+ }
78
+ }, [form_data, audit, current_user, debounce_ms]);
79
+ // Flush any pending timers on unmount.
80
+ useEffect(() => {
81
+ return () => {
82
+ const pending = pending_ref.current;
83
+ for (const [, entry] of pending) {
84
+ clearTimeout(entry.timer);
85
+ }
86
+ pending.clear();
87
+ };
88
+ }, []);
89
+ return null;
90
+ }
91
+ //# sourceMappingURL=auditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditor.js","sourceRoot":"","sources":["../../../src/components/field_audit/auditor.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * FieldAuditProvider — owns the in-session audit history.
3
+ *
4
+ * State lives in React state, so it dies with the component tree. Persistence
5
+ * is intentionally out of scope for this release; consuming apps that need
6
+ * durable audit logs can keep their own copy by subscribing to record_change
7
+ * via the imperative API.
8
+ */
9
+ import React from 'react';
10
+ import type { ReactNode } from 'react';
11
+ import type { FieldAuditContextValue } from './types.js';
12
+ declare const FieldAuditContext: React.Context<FieldAuditContextValue | undefined>;
13
+ /**
14
+ * Hook for consumers that *must* run inside a provider. Throws otherwise.
15
+ */
16
+ export declare function useFieldAudit(): FieldAuditContextValue;
17
+ /**
18
+ * Hook for components that may render with or without an audit provider.
19
+ * `FieldAuditIcon` and `FieldAuditPanel` use this so they can be dropped
20
+ * into a slot whether or not audit is enabled — when no provider is mounted
21
+ * they silently render nothing.
22
+ */
23
+ export declare function useFieldAuditOptional(): FieldAuditContextValue | undefined;
24
+ export interface FieldAuditProviderProps {
25
+ children: ReactNode;
26
+ }
27
+ export declare function FieldAuditProvider({ children }: FieldAuditProviderProps): import("react/jsx-runtime").JSX.Element;
28
+ export { FieldAuditContext };
29
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/components/field_audit/context.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAON,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,YAAY,CAAC;AAEpB,QAAA,MAAM,iBAAiB,mDAEtB,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,IAAI,sBAAsB,CAMtD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,sBAAsB,GAAG,SAAS,CAE1E;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAMD,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE,uBAAuB,2CAuGvE;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * FieldAuditProvider — owns the in-session audit history.
3
+ *
4
+ * State lives in React state, so it dies with the component tree. Persistence
5
+ * is intentionally out of scope for this release; consuming apps that need
6
+ * durable audit logs can keep their own copy by subscribing to record_change
7
+ * via the imperative API.
8
+ */
9
+ 'use client';
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { createContext, useCallback, useContext, useMemo, useRef, useState, } from 'react';
12
+ const FieldAuditContext = createContext(undefined);
13
+ /**
14
+ * Hook for consumers that *must* run inside a provider. Throws otherwise.
15
+ */
16
+ export function useFieldAudit() {
17
+ const ctx = useContext(FieldAuditContext);
18
+ if (!ctx) {
19
+ throw new Error('useFieldAudit must be used inside <FieldAuditProvider>');
20
+ }
21
+ return ctx;
22
+ }
23
+ /**
24
+ * Hook for components that may render with or without an audit provider.
25
+ * `FieldAuditIcon` and `FieldAuditPanel` use this so they can be dropped
26
+ * into a slot whether or not audit is enabled — when no provider is mounted
27
+ * they silently render nothing.
28
+ */
29
+ export function useFieldAuditOptional() {
30
+ return useContext(FieldAuditContext);
31
+ }
32
+ function make_id(field_id) {
33
+ return `${field_id}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
34
+ }
35
+ export function FieldAuditProvider({ children }) {
36
+ const [entries, set_entries] = useState(() => new Map());
37
+ const [expanded, set_expanded] = useState(() => new Set());
38
+ // Mirror entries in a ref so the auto-watcher (FormDataAuditor) can read
39
+ // the live state without re-binding to setState and creating stale closures.
40
+ const entries_ref = useRef(entries);
41
+ entries_ref.current = entries;
42
+ const record_change = useCallback((input) => {
43
+ set_entries((prev) => {
44
+ const existing = prev.get(input.field_id) ?? [];
45
+ const last = existing[0];
46
+ const window_ms = input.dedup_window_ms ?? 500;
47
+ // Dedup against a recent entry with the same new value.
48
+ if (last &&
49
+ Object.is(last.new_value, input.new_value) &&
50
+ Date.now() - last.at < window_ms) {
51
+ // The auto-watcher fires `manual` on every form_data change; an
52
+ // explicit caller may classify the same change as `clarification`
53
+ // before/around the watcher fires. Always promote the existing
54
+ // entry to the more specific source.
55
+ if (last.source === 'manual' && input.source !== 'manual') {
56
+ const promoted = {
57
+ ...last,
58
+ source: input.source,
59
+ user: input.user ?? last.user,
60
+ };
61
+ const next = new Map(prev);
62
+ next.set(input.field_id, [promoted, ...existing.slice(1)]);
63
+ return next;
64
+ }
65
+ // Otherwise treat as a duplicate and keep the existing entry.
66
+ return prev;
67
+ }
68
+ const entry = {
69
+ id: make_id(input.field_id),
70
+ field_id: input.field_id,
71
+ source: input.source,
72
+ at: Date.now(),
73
+ prev_value: input.prev_value !== undefined ? input.prev_value : last?.new_value,
74
+ new_value: input.new_value,
75
+ user: input.user,
76
+ };
77
+ const next = new Map(prev);
78
+ next.set(input.field_id, [entry, ...existing]);
79
+ return next;
80
+ });
81
+ }, []);
82
+ const toggle_expanded = useCallback((field_id) => {
83
+ set_expanded((prev) => {
84
+ const next = new Set(prev);
85
+ if (next.has(field_id))
86
+ next.delete(field_id);
87
+ else
88
+ next.add(field_id);
89
+ return next;
90
+ });
91
+ }, []);
92
+ const clear_field = useCallback((field_id) => {
93
+ set_entries((prev) => {
94
+ if (!prev.has(field_id))
95
+ return prev;
96
+ const next = new Map(prev);
97
+ next.delete(field_id);
98
+ return next;
99
+ });
100
+ set_expanded((prev) => {
101
+ if (!prev.has(field_id))
102
+ return prev;
103
+ const next = new Set(prev);
104
+ next.delete(field_id);
105
+ return next;
106
+ });
107
+ }, []);
108
+ const clear_all = useCallback(() => {
109
+ set_entries(new Map());
110
+ set_expanded(new Set());
111
+ }, []);
112
+ const value = useMemo(() => ({
113
+ entries,
114
+ expanded,
115
+ record_change,
116
+ toggle_expanded,
117
+ clear_field,
118
+ clear_all,
119
+ }), [entries, expanded, record_change, toggle_expanded, clear_field, clear_all]);
120
+ return (_jsx(FieldAuditContext.Provider, { value: value, children: children }));
121
+ }
122
+ export { FieldAuditContext };
123
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/components/field_audit/context.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * FieldAuditIcon — designed to be dropped into the
3
+ * `field_action_components.row_end` slot. Renders a small clock toggle that
4
+ * opens/closes the audit history panel for the field.
5
+ *
6
+ * Visible whenever the field has at least one recorded entry. Hidden when
7
+ * the audit provider is absent (so it can sit safely in a shared slot).
8
+ */
9
+ import React from 'react';
10
+ import type { FieldActionContext } from '../hazo_collab_form_view/types.js';
11
+ export declare function FieldAuditIcon({ field }: FieldActionContext): React.ReactElement | null;
12
+ //# sourceMappingURL=field_audit_icon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field_audit_icon.d.ts","sourceRoot":"","sources":["../../../src/components/field_audit/field_audit_icon.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAE5E,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAiDvF"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * FieldAuditIcon — designed to be dropped into the
3
+ * `field_action_components.row_end` slot. Renders a small clock toggle that
4
+ * opens/closes the audit history panel for the field.
5
+ *
6
+ * Visible whenever the field has at least one recorded entry. Hidden when
7
+ * the audit provider is absent (so it can sit safely in a shared slot).
8
+ */
9
+ 'use client';
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import { useFieldAuditOptional } from './context.js';
12
+ export function FieldAuditIcon({ field }) {
13
+ const audit = useFieldAuditOptional();
14
+ if (!audit)
15
+ return null;
16
+ const list = audit.entries.get(field.id) ?? [];
17
+ if (list.length === 0)
18
+ return null;
19
+ const is_open = audit.expanded.has(field.id);
20
+ const count = list.length;
21
+ return (_jsxs("button", { type: "button", "data-testid": `field_audit_icon_${field.id}`, "data-state": is_open ? 'open' : 'closed', "aria-expanded": is_open, "aria-label": `Show ${count} audit ${count === 1 ? 'entry' : 'entries'} for ${field.id}`, title: `${count} change${count === 1 ? '' : 's'} recorded`, onClick: () => audit.toggle_expanded(field.id), className: "cls_field_audit_icon inline-flex items-center justify-center gap-1 h-7 px-2 rounded-md text-[11px] font-medium text-amber-700 hover:bg-amber-50 hover:text-amber-900 transition-colors", children: [_jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-3.5 w-3.5", children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("polyline", { points: "12 7 12 12 15 14" })] }), _jsx("span", { className: "font-mono", children: count }), _jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: `h-3 w-3 transition-transform ${is_open ? 'rotate-180' : ''}`, children: _jsx("polyline", { points: "6 9 12 15 18 9" }) })] }));
22
+ }
23
+ //# sourceMappingURL=field_audit_icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field_audit_icon.js","sourceRoot":"","sources":["../../../src/components/field_audit/field_audit_icon.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,CAAC;;AAGb,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGrD,MAAM,UAAU,cAAc,CAAC,EAAE,KAAK,EAAsB;IAC1D,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;IAE1B,OAAO,CACL,kBACE,IAAI,EAAC,QAAQ,iBACA,oBAAoB,KAAK,CAAC,EAAE,EAAE,gBAC/B,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,mBACxB,OAAO,gBACV,QAAQ,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,QAAQ,KAAK,CAAC,EAAE,EAAE,EACtF,KAAK,EAAE,GAAG,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,EAC1D,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,EAC9C,SAAS,EAAC,wLAAwL,aAElM,8BACc,MAAM,EAClB,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,SAAS,EAAC,aAAa,aAEvB,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,GAAG,GAAG,EAChC,mBAAU,MAAM,EAAC,kBAAkB,GAAG,IAClC,EACN,eAAM,SAAS,EAAC,WAAW,YAAE,KAAK,GAAQ,EAC1C,6BACc,MAAM,EAClB,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,SAAS,EAAE,gCAAgC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,YAExE,mBAAU,MAAM,EAAC,gBAAgB,GAAG,GAChC,IACC,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * FieldAuditPanel — designed to be dropped into the
3
+ * `field_action_components.row_below` slot. Renders the per-field history
4
+ * list when the icon is toggled open.
5
+ */
6
+ import React from 'react';
7
+ import type { FieldActionContext } from '../hazo_collab_form_view/types.js';
8
+ export declare function FieldAuditPanel({ field }: FieldActionContext): React.ReactElement | null;
9
+ //# sourceMappingURL=field_audit_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field_audit_panel.d.ts","sourceRoot":"","sources":["../../../src/components/field_audit/field_audit_panel.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAsD5E,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAiCxF"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * FieldAuditPanel — designed to be dropped into the
3
+ * `field_action_components.row_below` slot. Renders the per-field history
4
+ * list when the icon is toggled open.
5
+ */
6
+ 'use client';
7
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
+ import { useFieldAuditOptional } from './context.js';
9
+ function format_value(value) {
10
+ if (value === undefined)
11
+ return '—';
12
+ if (value === null)
13
+ return 'null';
14
+ if (typeof value === 'string')
15
+ return value === '' ? '""' : `"${value}"`;
16
+ if (typeof value === 'number' || typeof value === 'boolean')
17
+ return String(value);
18
+ try {
19
+ return JSON.stringify(value);
20
+ }
21
+ catch {
22
+ return String(value);
23
+ }
24
+ }
25
+ function format_time(at) {
26
+ const date = new Date(at);
27
+ const hh = String(date.getHours()).padStart(2, '0');
28
+ const mm = String(date.getMinutes()).padStart(2, '0');
29
+ const ss = String(date.getSeconds()).padStart(2, '0');
30
+ return `${hh}:${mm}:${ss}`;
31
+ }
32
+ function source_label(source) {
33
+ switch (source) {
34
+ case 'manual':
35
+ return 'edit';
36
+ case 'clarification':
37
+ return 'clarification';
38
+ }
39
+ }
40
+ function EntryRow({ entry }) {
41
+ return (_jsxs("li", { className: "cls_field_audit_entry flex items-baseline gap-2 px-2 py-1 text-[11px] font-mono", children: [_jsx("span", { className: "text-amber-700/70 shrink-0", children: format_time(entry.at) }), _jsx("span", { className: "text-amber-900 shrink-0 uppercase tracking-wide text-[10px]", children: source_label(entry.source) }), entry.user && (_jsx("span", { className: "text-slate-600 shrink-0", children: entry.user })), _jsxs("span", { className: "text-slate-500 truncate", children: [format_value(entry.prev_value), " ", _jsx("span", { "aria-hidden": true, children: "\u2192" }), ' ', _jsx("span", { className: "text-emerald-700", children: format_value(entry.new_value) })] })] }));
42
+ }
43
+ export function FieldAuditPanel({ field }) {
44
+ const audit = useFieldAuditOptional();
45
+ if (!audit)
46
+ return null;
47
+ if (!audit.expanded.has(field.id))
48
+ return null;
49
+ const list = audit.entries.get(field.id) ?? [];
50
+ if (list.length === 0)
51
+ return null;
52
+ return (_jsxs("div", { "data-testid": `field_audit_panel_${field.id}`, className: "cls_field_audit_panel mx-3 mb-2 rounded-md border border-amber-200 bg-amber-50/60", children: [_jsxs("div", { className: "flex items-center justify-between px-2 py-1 border-b border-amber-200/60", children: [_jsxs("span", { className: "text-[10px] uppercase tracking-wide text-amber-800", children: ["Field audit \u00B7 ", list.length, " change", list.length === 1 ? '' : 's'] }), _jsx("button", { type: "button", onClick: () => audit.clear_field(field.id), "aria-label": `Clear audit history for ${field.id}`, className: "text-[10px] text-amber-700/70 hover:text-red-600", children: "Clear" })] }), _jsx("ol", { className: "divide-y divide-amber-100", children: list.map((entry) => (_jsx(EntryRow, { entry: entry }, entry.id))) })] }));
53
+ }
54
+ //# sourceMappingURL=field_audit_panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field_audit_panel.js","sourceRoot":"","sources":["../../../src/components/field_audit/field_audit_panel.tsx"],"names":[],"mappings":"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"}