@striae-org/striae 4.1.0 → 4.2.1

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 (124) hide show
  1. package/.env.example +8 -0
  2. package/LICENSE +1 -1
  3. package/app/components/actions/case-export/core-export.ts +14 -8
  4. package/app/components/actions/case-export/data-processing.ts +1 -0
  5. package/app/components/actions/case-export/download-handlers.ts +7 -0
  6. package/app/components/actions/case-export/metadata-helpers.ts +2 -1
  7. package/app/components/actions/case-import/confirmation-import.ts +12 -2
  8. package/app/components/actions/case-import/orchestrator.ts +78 -32
  9. package/app/components/actions/case-import/storage-operations.ts +97 -8
  10. package/app/components/actions/case-import/zip-processing.ts +159 -86
  11. package/app/components/actions/case-manage.ts +463 -8
  12. package/app/components/actions/confirm-export.ts +9 -2
  13. package/app/components/actions/image-manage.ts +77 -44
  14. package/app/components/audit/user-audit-viewer.tsx +19 -8
  15. package/app/components/audit/user-audit.module.css +21 -0
  16. package/app/components/audit/viewer/audit-entries-list.tsx +12 -2
  17. package/app/components/audit/viewer/audit-filters-panel.tsx +1 -0
  18. package/app/components/audit/viewer/audit-viewer-utils.ts +2 -0
  19. package/app/components/audit/viewer/use-audit-viewer-data.ts +24 -1
  20. package/app/components/audit/viewer/use-audit-viewer-export.ts +1 -1
  21. package/app/components/canvas/box-annotations/box-annotations.module.css +22 -18
  22. package/app/components/canvas/box-annotations/box-annotations.tsx +15 -0
  23. package/app/components/canvas/canvas.module.css +64 -54
  24. package/app/components/canvas/canvas.tsx +14 -16
  25. package/app/components/canvas/confirmation/confirmation.module.css +1 -0
  26. package/app/components/canvas/confirmation/confirmation.tsx +12 -14
  27. package/app/components/colors/colors.module.css +4 -3
  28. package/app/components/navbar/case-modals/archive-case-modal.module.css +110 -0
  29. package/app/components/navbar/case-modals/archive-case-modal.tsx +129 -0
  30. package/app/components/navbar/case-modals/open-case-modal.module.css +81 -0
  31. package/app/components/navbar/case-modals/open-case-modal.tsx +120 -0
  32. package/app/components/navbar/case-modals/rename-case-modal.module.css +81 -0
  33. package/app/components/navbar/case-modals/rename-case-modal.tsx +107 -0
  34. package/app/components/navbar/navbar.module.css +447 -0
  35. package/app/components/navbar/navbar.tsx +402 -0
  36. package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +1 -0
  37. package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +15 -16
  38. package/app/components/sidebar/case-export/case-export.module.css +1 -0
  39. package/app/components/sidebar/case-export/case-export.tsx +8 -46
  40. package/app/components/sidebar/case-import/case-import.module.css +23 -0
  41. package/app/components/sidebar/case-import/case-import.tsx +64 -16
  42. package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +20 -1
  43. package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +15 -0
  44. package/app/components/sidebar/cases/case-sidebar.tsx +68 -588
  45. package/app/components/sidebar/cases/cases-modal.module.css +1 -0
  46. package/app/components/sidebar/cases/cases-modal.tsx +82 -43
  47. package/app/components/sidebar/cases/cases.module.css +82 -21
  48. package/app/components/sidebar/files/files-modal.module.css +1 -0
  49. package/app/components/sidebar/files/files-modal.tsx +49 -52
  50. package/app/components/sidebar/notes/addl-notes-modal.tsx +82 -0
  51. package/app/components/sidebar/notes/{notes-sidebar.tsx → notes-editor-form.tsx} +187 -138
  52. package/app/components/sidebar/notes/notes-editor-modal.module.css +49 -0
  53. package/app/components/sidebar/notes/notes-editor-modal.tsx +64 -0
  54. package/app/components/sidebar/notes/notes.module.css +170 -1
  55. package/app/components/sidebar/sidebar-container.tsx +16 -28
  56. package/app/components/sidebar/sidebar.module.css +5 -69
  57. package/app/components/sidebar/sidebar.tsx +27 -125
  58. package/app/components/sidebar/upload/image-upload-zone.module.css +13 -13
  59. package/app/components/user/inactivity-warning.module.css +1 -0
  60. package/app/components/user/inactivity-warning.tsx +15 -2
  61. package/app/components/user/manage-profile.tsx +23 -10
  62. package/app/{tailwind.css → global.css} +1 -3
  63. package/app/hooks/useOverlayDismiss.ts +54 -4
  64. package/app/root.tsx +1 -1
  65. package/app/routes/auth/login.tsx +785 -774
  66. package/app/routes/striae/striae.module.css +10 -3
  67. package/app/routes/striae/striae.tsx +475 -30
  68. package/app/services/audit/audit.service.ts +173 -27
  69. package/app/services/audit/builders/audit-event-builders-case-file.ts +43 -0
  70. package/app/services/audit/builders/audit-event-builders-workflow.ts +2 -0
  71. package/app/services/audit/builders/index.ts +1 -0
  72. package/app/types/audit.ts +4 -1
  73. package/app/types/case.ts +29 -0
  74. package/app/types/import.ts +3 -0
  75. package/app/utils/data/confirmation-summary/summary-core.ts +279 -0
  76. package/app/utils/data/data-operations.ts +17 -861
  77. package/app/utils/data/index.ts +11 -1
  78. package/app/utils/data/operations/batch-operations.ts +113 -0
  79. package/app/utils/data/operations/case-operations.ts +168 -0
  80. package/app/utils/data/operations/confirmation-summary-operations.ts +301 -0
  81. package/app/utils/data/operations/file-annotation-operations.ts +196 -0
  82. package/app/utils/data/operations/index.ts +7 -0
  83. package/app/utils/data/operations/signing-operations.ts +225 -0
  84. package/app/utils/data/operations/types.ts +42 -0
  85. package/app/utils/data/operations/validation-operations.ts +48 -0
  86. package/app/utils/data/permissions.ts +16 -1
  87. package/app/utils/forensics/audit-export-signature.ts +5 -1
  88. package/app/utils/forensics/confirmation-signature.ts +3 -0
  89. package/app/utils/forensics/export-verification.ts +426 -22
  90. package/functions/api/_shared/firebase-auth.ts +2 -7
  91. package/functions/api/image/[[path]].ts +20 -23
  92. package/functions/api/pdf/[[path]].ts +27 -8
  93. package/package.json +7 -12
  94. package/scripts/deploy-primershear-emails.sh +2 -1
  95. package/worker-configuration.d.ts +3 -3
  96. package/workers/audit-worker/package.json +1 -1
  97. package/workers/audit-worker/worker-configuration.d.ts +7448 -11323
  98. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  99. package/workers/data-worker/package.json +1 -1
  100. package/workers/data-worker/worker-configuration.d.ts +7448 -11323
  101. package/workers/data-worker/wrangler.jsonc.example +1 -1
  102. package/workers/image-worker/package.json +1 -1
  103. package/workers/image-worker/src/image-worker.example.ts +16 -5
  104. package/workers/image-worker/worker-configuration.d.ts +7447 -11322
  105. package/workers/image-worker/wrangler.jsonc.example +1 -1
  106. package/workers/keys-worker/package.json +1 -1
  107. package/workers/keys-worker/worker-configuration.d.ts +7447 -11322
  108. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  109. package/workers/pdf-worker/package.json +1 -1
  110. package/workers/pdf-worker/src/formats/format-striae.ts +9 -14
  111. package/workers/pdf-worker/src/pdf-worker.example.ts +37 -58
  112. package/workers/pdf-worker/src/report-types.ts +3 -3
  113. package/workers/pdf-worker/worker-configuration.d.ts +7448 -11323
  114. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  115. package/workers/user-worker/package.json +1 -1
  116. package/workers/user-worker/src/user-worker.example.ts +17 -0
  117. package/workers/user-worker/worker-configuration.d.ts +7448 -11323
  118. package/workers/user-worker/wrangler.jsonc.example +1 -1
  119. package/wrangler.toml.example +1 -1
  120. package/NOTICE +0 -13
  121. package/app/components/sidebar/notes/notes-modal.tsx +0 -53
  122. package/postcss.config.js +0 -6
  123. package/public/.well-known/keybase.txt +0 -56
  124. package/tailwind.config.ts +0 -22
@@ -1,4 +1,5 @@
1
1
  import { useState, useEffect } from 'react';
2
+ import { useOverlayDismiss } from '~/hooks/useOverlayDismiss';
2
3
  import styles from './inactivity-warning.module.css';
3
4
 
4
5
  interface InactivityWarningProps {
@@ -15,6 +16,15 @@ export const InactivityWarning = ({
15
16
  onSignOut
16
17
  }: InactivityWarningProps) => {
17
18
  const [countdown, setCountdown] = useState(remainingSeconds);
19
+ const {
20
+ requestClose,
21
+ overlayProps,
22
+ getCloseButtonProps,
23
+ } = useOverlayDismiss({
24
+ isOpen,
25
+ onClose: onExtendSession,
26
+ closeOnBackdrop: false,
27
+ });
18
28
 
19
29
  useEffect(() => {
20
30
  setCountdown(remainingSeconds);
@@ -46,8 +56,11 @@ export const InactivityWarning = ({
46
56
  const seconds = countdown % 60;
47
57
 
48
58
  return (
49
- <div className={styles.overlay}>
59
+ <div className={styles.overlay} aria-label="Close inactivity warning" {...overlayProps}>
50
60
  <div className={styles.modal}>
61
+ <button className={styles.closeButton} {...getCloseButtonProps({ ariaLabel: 'Close inactivity warning' })}>
62
+ ×
63
+ </button>
51
64
  <div className={styles.header}>
52
65
  <h3>Session Timeout Warning</h3>
53
66
  </div>
@@ -66,7 +79,7 @@ export const InactivityWarning = ({
66
79
 
67
80
  <div className={styles.actions}>
68
81
  <button
69
- onClick={onExtendSession}
82
+ onClick={requestClose}
70
83
  className={styles.extendButton}
71
84
  >
72
85
  Extend Session
@@ -8,7 +8,8 @@ import { useOverlayDismiss } from '~/hooks/useOverlayDismiss';
8
8
  import { getUserData, updateUserData } from '~/utils/data';
9
9
  import { auditService } from '~/services/audit';
10
10
  import { handleAuthError, ERROR_MESSAGES } from '~/services/firebase/errors';
11
- import { FormField, FormButton, FormMessage } from '../form';
11
+ import { FormField, FormButton } from '../form';
12
+ import { Toast } from '~/components/toast/toast';
12
13
  import { MfaPhoneUpdateSection } from './mfa-phone-update';
13
14
  import styles from './manage-profile.module.css';
14
15
 
@@ -26,8 +27,9 @@ export const ManageProfile = ({ isOpen, onClose }: ManageProfileProps) => {
26
27
  const [email, setEmail] = useState('');
27
28
  const [isLoading, setIsLoading] = useState(false);
28
29
  const [isMfaBusy, setIsMfaBusy] = useState(false);
29
- const [error, setError] = useState('');
30
- const [success, setSuccess] = useState('');
30
+ const [showToast, setShowToast] = useState(false);
31
+ const [toastMessage, setToastMessage] = useState('');
32
+ const [toastType, setToastType] = useState<'success' | 'error'>('success');
31
33
  const [showResetForm, setShowResetForm] = useState(false);
32
34
  const [showDeleteModal, setShowDeleteModal] = useState(false);
33
35
  const [showAuditViewer, setShowAuditViewer] = useState(false);
@@ -80,8 +82,7 @@ export const ManageProfile = ({ isOpen, onClose }: ManageProfileProps) => {
80
82
  const handleUpdateProfile = async (e: React.FormEvent) => {
81
83
  e.preventDefault();
82
84
  setIsLoading(true);
83
- setError('');
84
- setSuccess('');
85
+ setShowToast(false);
85
86
 
86
87
  const oldDisplayName = user?.displayName || '';
87
88
  const oldBadgeId = initialBadgeId;
@@ -129,7 +130,12 @@ export const ManageProfile = ({ isOpen, onClose }: ManageProfileProps) => {
129
130
 
130
131
  setInitialBadgeId(normalizedBadgeId);
131
132
 
132
- setSuccess(ERROR_MESSAGES.PROFILE_UPDATED);
133
+ setToastType('success');
134
+ setToastMessage(ERROR_MESSAGES.PROFILE_UPDATED);
135
+ setShowToast(true);
136
+ setTimeout(() => {
137
+ window.location.reload();
138
+ }, 1500);
133
139
  } catch (err) {
134
140
  const { message } = handleAuthError(err);
135
141
 
@@ -157,7 +163,9 @@ export const ManageProfile = ({ isOpen, onClose }: ManageProfileProps) => {
157
163
  );
158
164
  }
159
165
 
160
- setError(message);
166
+ setToastType('error');
167
+ setToastMessage(message);
168
+ setShowToast(true);
161
169
  } finally {
162
170
  setIsLoading(false);
163
171
  }
@@ -193,6 +201,13 @@ export const ManageProfile = ({ isOpen, onClose }: ManageProfileProps) => {
193
201
  }
194
202
 
195
203
  return (
204
+ <>
205
+ <Toast
206
+ message={toastMessage}
207
+ type={toastType}
208
+ isVisible={showToast}
209
+ onClose={() => setShowToast(false)}
210
+ />
196
211
  <div
197
212
  className={styles.modalOverlay}
198
213
  onMouseDown={handleOverlayMouseDown}
@@ -282,9 +297,6 @@ export const ManageProfile = ({ isOpen, onClose }: ManageProfileProps) => {
282
297
 
283
298
  <MfaPhoneUpdateSection user={user} isOpen={isOpen} onBusyChange={handleMfaBusyChange} />
284
299
 
285
- {error && <FormMessage type="error" message={error} />}
286
- {success && <FormMessage type="success" message={success} />}
287
-
288
300
  <div className={styles.buttonGroup}>
289
301
  <FormButton variant="primary" type="submit" isLoading={isLoading} loadingText="Updating...">
290
302
  Update Profile
@@ -302,5 +314,6 @@ export const ManageProfile = ({ isOpen, onClose }: ManageProfileProps) => {
302
314
  </form>
303
315
  </div>
304
316
  </div>
317
+ </>
305
318
  );
306
319
  };
@@ -1,6 +1,4 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import "modern-normalize";
4
2
 
5
3
  @layer base {
6
4
  :root {
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, type KeyboardEventHandler, type MouseEventHandler } from 'react';
1
+ import { useCallback, useEffect, type CSSProperties, type KeyboardEventHandler, type MouseEventHandler } from 'react';
2
2
 
3
3
  interface UseOverlayDismissOptions {
4
4
  isOpen: boolean;
@@ -8,6 +8,30 @@ interface UseOverlayDismissOptions {
8
8
  closeOnBackdrop?: boolean;
9
9
  }
10
10
 
11
+ interface CloseButtonOptions {
12
+ ariaLabel?: string;
13
+ title?: string;
14
+ }
15
+
16
+ const sharedCloseButtonStyle: CSSProperties = {
17
+ position: 'absolute',
18
+ top: '0.6rem',
19
+ right: '0.6rem',
20
+ width: '1.9rem',
21
+ height: '1.9rem',
22
+ borderRadius: '999px',
23
+ border: '1px solid #d6dce2',
24
+ background: '#f8f9fa',
25
+ color: '#495057',
26
+ fontSize: '1.2rem',
27
+ lineHeight: 1,
28
+ display: 'inline-flex',
29
+ alignItems: 'center',
30
+ justifyContent: 'center',
31
+ cursor: 'pointer',
32
+ zIndex: 1,
33
+ };
34
+
11
35
  export const useOverlayDismiss = ({
12
36
  isOpen,
13
37
  onClose,
@@ -30,7 +54,8 @@ export const useOverlayDismiss = ({
30
54
 
31
55
  const handleEscape = (event: KeyboardEvent) => {
32
56
  if (event.key === 'Escape') {
33
- onClose();
57
+ event.preventDefault();
58
+ requestClose();
34
59
  }
35
60
  };
36
61
 
@@ -39,7 +64,7 @@ export const useOverlayDismiss = ({
39
64
  return () => {
40
65
  document.removeEventListener('keydown', handleEscape);
41
66
  };
42
- }, [isOpen, closeOnEscape, canDismiss, onClose]);
67
+ }, [isOpen, closeOnEscape, canDismiss, requestClose]);
43
68
 
44
69
  const handleOverlayMouseDown = useCallback<MouseEventHandler<HTMLDivElement>>((event) => {
45
70
  if (!closeOnBackdrop || event.target !== event.currentTarget) {
@@ -60,9 +85,34 @@ export const useOverlayDismiss = ({
60
85
  }
61
86
  }, [closeOnBackdrop, requestClose]);
62
87
 
88
+ const isBackdropDismissInteractive = closeOnBackdrop && canDismiss;
89
+
90
+ const overlayProps = {
91
+ role: isBackdropDismissInteractive ? 'button' : 'presentation',
92
+ tabIndex: isBackdropDismissInteractive ? 0 : undefined,
93
+ onMouseDown: isBackdropDismissInteractive ? handleOverlayMouseDown : undefined,
94
+ onKeyDown: isBackdropDismissInteractive ? handleOverlayKeyDown : undefined,
95
+ style: { cursor: 'default' as const },
96
+ };
97
+
98
+ const getCloseButtonProps = useCallback((options?: CloseButtonOptions) => {
99
+ const ariaLabel = options?.ariaLabel || 'Close modal';
100
+
101
+ return {
102
+ type: 'button' as const,
103
+ onClick: requestClose,
104
+ disabled: !canDismiss,
105
+ 'aria-label': ariaLabel,
106
+ title: options?.title || ariaLabel,
107
+ style: sharedCloseButtonStyle,
108
+ };
109
+ }, [requestClose, canDismiss]);
110
+
63
111
  return {
64
112
  requestClose,
65
113
  handleOverlayMouseDown,
66
- handleOverlayKeyDown
114
+ handleOverlayKeyDown,
115
+ overlayProps,
116
+ getCloseButtonProps,
67
117
  };
68
118
  };
package/app/root.tsx CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  import { AuthProvider } from '~/components/auth/auth-provider';
17
17
  import { auth } from '~/services/firebase';
18
18
  import styles from '~/styles/root.module.css';
19
- import './tailwind.css';
19
+ import './global.css';
20
20
 
21
21
  export const links: LinksFunction = () => [
22
22
  { rel: "preconnect", href: "https://fonts.googleapis.com" },