@striae-org/striae 4.2.0 → 4.3.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.
- package/LICENSE +1 -1
- package/app/components/actions/case-manage.ts +50 -17
- package/app/components/audit/viewer/audit-entries-list.tsx +5 -2
- package/app/components/audit/viewer/use-audit-viewer-data.ts +6 -3
- package/app/components/audit/viewer/use-audit-viewer-export.ts +1 -1
- package/app/components/canvas/confirmation/confirmation.tsx +6 -2
- package/app/components/colors/colors.module.css +4 -3
- package/app/components/navbar/case-modals/archive-case-modal.module.css +0 -76
- package/app/components/navbar/case-modals/archive-case-modal.tsx +9 -8
- package/app/components/navbar/case-modals/case-modal-shared.module.css +94 -0
- package/app/components/navbar/case-modals/delete-case-modal.module.css +9 -0
- package/app/components/navbar/case-modals/delete-case-modal.tsx +79 -0
- package/app/components/navbar/case-modals/open-case-modal.module.css +2 -1
- package/app/components/navbar/case-modals/rename-case-modal.module.css +0 -72
- package/app/components/navbar/case-modals/rename-case-modal.tsx +9 -8
- package/app/components/navbar/navbar.tsx +34 -9
- package/app/components/sidebar/cases/case-sidebar.tsx +93 -73
- package/app/components/sidebar/cases/cases-modal.module.css +312 -10
- package/app/components/sidebar/cases/cases-modal.tsx +737 -116
- package/app/components/sidebar/cases/cases.module.css +43 -0
- package/app/components/sidebar/files/delete-files-modal.module.css +26 -0
- package/app/components/sidebar/files/delete-files-modal.tsx +94 -0
- package/app/components/sidebar/files/files-modal.module.css +285 -44
- package/app/components/sidebar/files/files-modal.tsx +482 -177
- package/app/components/sidebar/notes/addl-notes-modal.tsx +82 -0
- package/app/components/sidebar/notes/class-details-fields.tsx +146 -0
- package/app/components/sidebar/notes/class-details-modal.tsx +147 -0
- package/app/components/sidebar/notes/class-details-sections.tsx +561 -0
- package/app/components/sidebar/notes/class-details-shared.ts +239 -0
- package/app/components/sidebar/notes/{notes-sidebar.tsx → notes-editor-form.tsx} +77 -76
- package/app/components/sidebar/notes/notes-editor-modal.tsx +5 -7
- package/app/components/sidebar/notes/notes.module.css +262 -14
- package/app/components/sidebar/notes/use-class-details-state.ts +371 -0
- package/app/components/sidebar/sidebar-container.tsx +2 -0
- package/app/components/sidebar/sidebar.tsx +15 -1
- package/app/{tailwind.css → global.css} +1 -3
- package/app/hooks/useCaseListPreferences.ts +99 -0
- package/app/hooks/useFileListPreferences.ts +106 -0
- package/app/hooks/useOverlayDismiss.ts +6 -4
- package/app/root.tsx +1 -1
- package/app/routes/striae/striae.tsx +7 -0
- package/app/services/audit/audit.service.ts +2 -2
- package/app/services/audit/builders/audit-event-builders-case-file.ts +1 -1
- package/app/types/annotations.ts +48 -1
- package/app/types/audit.ts +1 -0
- package/app/utils/data/case-filters.ts +127 -0
- package/app/utils/data/confirmation-summary/summary-core.ts +295 -0
- package/app/utils/data/data-operations.ts +17 -861
- package/app/utils/data/file-filters.ts +201 -0
- package/app/utils/data/index.ts +11 -1
- package/app/utils/data/operations/batch-operations.ts +113 -0
- package/app/utils/data/operations/case-operations.ts +168 -0
- package/app/utils/data/operations/confirmation-summary-operations.ts +301 -0
- package/app/utils/data/operations/file-annotation-operations.ts +196 -0
- package/app/utils/data/operations/index.ts +7 -0
- package/app/utils/data/operations/signing-operations.ts +225 -0
- package/app/utils/data/operations/types.ts +42 -0
- package/app/utils/data/operations/validation-operations.ts +48 -0
- package/app/utils/forensics/export-verification.ts +40 -111
- package/functions/api/_shared/firebase-auth.ts +2 -7
- package/functions/api/image/[[path]].ts +23 -22
- package/functions/api/pdf/[[path]].ts +27 -8
- package/package.json +7 -13
- package/scripts/deploy-primershear-emails.sh +1 -1
- package/worker-configuration.d.ts +2 -2
- package/workers/audit-worker/package.json +1 -1
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/package.json +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/package.json +1 -1
- package/workers/image-worker/src/image-worker.example.ts +16 -5
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/keys-worker/package.json +1 -1
- package/workers/keys-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/package.json +1 -1
- package/workers/pdf-worker/src/formats/format-striae.ts +84 -124
- package/workers/pdf-worker/src/pdf-worker.example.ts +58 -61
- package/workers/pdf-worker/src/report-layout.ts +227 -0
- package/workers/pdf-worker/src/report-types.ts +23 -3
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/package.json +1 -1
- package/workers/user-worker/src/user-worker.example.ts +17 -0
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
- package/NOTICE +0 -13
- package/app/components/sidebar/notes/notes-modal.tsx +0 -52
- package/postcss.config.js +0 -6
- package/tailwind.config.ts +0 -22
- package/workers/pdf-worker/src/assets/icon-256.png +0 -0
- /package/workers/pdf-worker/src/assets/{generated-assets.ts → generated-assets.example.ts} +0 -0
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
.
|
|
1
|
+
.notesEditorForm {
|
|
2
2
|
padding: 0.3rem;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
.
|
|
5
|
+
.editorLayout .caseNumbers {
|
|
6
6
|
display: grid;
|
|
7
7
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
8
8
|
gap: 1.25rem;
|
|
9
9
|
align-items: start;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
.
|
|
12
|
+
.editorLayout .caseNumbers > .inputGroup + .inputGroup {
|
|
13
13
|
border-left: 1px solid #dee2e6;
|
|
14
14
|
padding-left: 1.25rem;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
.
|
|
17
|
+
.editorLayout .inputGroup {
|
|
18
18
|
margin-bottom: 0;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
margin-bottom: 1.5rem;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
.
|
|
38
|
+
.editorLayout .notesActionBarSticky {
|
|
39
39
|
margin-top: 0.25rem;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
.
|
|
42
|
+
.editorLayout .compactSectionGrid > .compactHalfSection + .compactHalfSection {
|
|
43
43
|
border-left: 1px solid #dee2e6;
|
|
44
44
|
padding-left: 1.25rem;
|
|
45
45
|
}
|
|
@@ -49,22 +49,19 @@
|
|
|
49
49
|
padding-left: 1.25rem;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
.
|
|
52
|
+
.editorLayout .additionalNotesRow {
|
|
53
53
|
border-top: 1px solid #dee2e6;
|
|
54
54
|
padding-top: 1.25rem;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
@media (max-width: 980px) {
|
|
58
|
-
.
|
|
58
|
+
.editorLayout .caseNumbers,
|
|
59
59
|
.compactSectionGrid {
|
|
60
60
|
grid-template-columns: 1fr;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
.
|
|
64
|
-
.
|
|
65
|
-
.compactSectionGrid
|
|
66
|
-
> .compactHalfSection
|
|
67
|
-
+ .compactHalfSection,
|
|
63
|
+
.editorLayout .caseNumbers > .inputGroup + .inputGroup,
|
|
64
|
+
.editorLayout .compactSectionGrid > .compactHalfSection + .compactHalfSection,
|
|
68
65
|
.classCharacteristicsColumns > .characteristicsPlaceholder {
|
|
69
66
|
border-left: none;
|
|
70
67
|
padding-left: 0;
|
|
@@ -152,9 +149,25 @@ textarea:focus {
|
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
.caseNumbers {
|
|
152
|
+
display: grid;
|
|
153
|
+
grid-template-columns: 1fr 1fr;
|
|
154
|
+
gap: 1.5rem;
|
|
155
|
+
margin-bottom: 2rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.fontColorRow {
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
gap: 0.75rem;
|
|
155
162
|
margin-bottom: 2rem;
|
|
156
163
|
}
|
|
157
164
|
|
|
165
|
+
.fontColorRow label {
|
|
166
|
+
font-size: 0.95rem;
|
|
167
|
+
font-weight: 600;
|
|
168
|
+
color: #212529;
|
|
169
|
+
}
|
|
170
|
+
|
|
158
171
|
.caseInput {
|
|
159
172
|
display: flex;
|
|
160
173
|
flex-direction: column;
|
|
@@ -234,6 +247,241 @@ textarea:focus {
|
|
|
234
247
|
line-height: 1.4;
|
|
235
248
|
}
|
|
236
249
|
|
|
250
|
+
/* Class Details Panel — replaces placeholder in right column */
|
|
251
|
+
|
|
252
|
+
.classDetailsPanel {
|
|
253
|
+
display: flex;
|
|
254
|
+
flex-direction: column;
|
|
255
|
+
gap: 0.75rem;
|
|
256
|
+
min-height: 150px;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.classDetailsButton {
|
|
260
|
+
width: 100%;
|
|
261
|
+
padding: 0.65rem 1rem;
|
|
262
|
+
background: transparent;
|
|
263
|
+
color: var(--primary);
|
|
264
|
+
border: 1.5px solid var(--primary);
|
|
265
|
+
border-radius: 6px;
|
|
266
|
+
font-size: 0.88rem;
|
|
267
|
+
font-weight: 500;
|
|
268
|
+
cursor: pointer;
|
|
269
|
+
transition: all 0.2s;
|
|
270
|
+
text-align: center;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.classDetailsButton:hover:not(:disabled) {
|
|
274
|
+
background-color: color-mix(in lab, var(--primary) 10%, transparent);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.classDetailsButton:disabled {
|
|
278
|
+
opacity: 0.5;
|
|
279
|
+
cursor: not-allowed;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Class Details Modal */
|
|
283
|
+
|
|
284
|
+
.classDetailsModal {
|
|
285
|
+
max-width: 680px;
|
|
286
|
+
max-height: 80vh;
|
|
287
|
+
display: flex;
|
|
288
|
+
flex-direction: column;
|
|
289
|
+
gap: 1rem;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.classDetailsContent {
|
|
293
|
+
overflow-y: auto;
|
|
294
|
+
flex: 1;
|
|
295
|
+
min-height: 0;
|
|
296
|
+
display: flex;
|
|
297
|
+
flex-direction: column;
|
|
298
|
+
gap: 1.25rem;
|
|
299
|
+
padding-right: 0.25rem;
|
|
300
|
+
padding-bottom: 0.5rem;
|
|
301
|
+
scrollbar-width: thin;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.classDetailsSection {
|
|
305
|
+
display: flex;
|
|
306
|
+
flex-direction: column;
|
|
307
|
+
gap: 1rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.classDetailsSectionHeader {
|
|
311
|
+
margin: 0;
|
|
312
|
+
font-size: 0.95rem;
|
|
313
|
+
font-weight: 700;
|
|
314
|
+
color: #343a40;
|
|
315
|
+
padding-bottom: 0.4rem;
|
|
316
|
+
border-bottom: 1.5px solid #dee2e6;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.classDetailsFieldGrid {
|
|
320
|
+
display: grid;
|
|
321
|
+
grid-template-columns: 1fr 1fr;
|
|
322
|
+
gap: 0.85rem 1.25rem;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.classDetailsField {
|
|
326
|
+
display: flex;
|
|
327
|
+
flex-direction: column;
|
|
328
|
+
gap: 0.25rem;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.classDetailsFieldFull {
|
|
332
|
+
grid-column: 1 / -1;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.classDetailsLabel {
|
|
336
|
+
font-size: 0.8rem;
|
|
337
|
+
font-weight: 600;
|
|
338
|
+
color: #495057;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.classDetailsInput {
|
|
342
|
+
padding: 0.5rem 0.65rem;
|
|
343
|
+
border: 1.5px solid #ced4da;
|
|
344
|
+
border-radius: 6px;
|
|
345
|
+
font-size: 0.88rem;
|
|
346
|
+
transition: border-color 0.2s;
|
|
347
|
+
width: 100%;
|
|
348
|
+
box-sizing: border-box;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.classDetailsInput:focus {
|
|
352
|
+
border-color: var(--primary);
|
|
353
|
+
outline: none;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.classDetailsInput:disabled {
|
|
357
|
+
background-color: #f8f9fa;
|
|
358
|
+
cursor: not-allowed;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.classDetailsCheckboxGroup {
|
|
362
|
+
display: flex;
|
|
363
|
+
flex-wrap: wrap;
|
|
364
|
+
gap: 0.4rem 1.25rem;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.classDetailsCheckboxLabel {
|
|
368
|
+
display: flex;
|
|
369
|
+
align-items: center;
|
|
370
|
+
gap: 0.4rem;
|
|
371
|
+
font-size: 0.85rem;
|
|
372
|
+
color: #495057;
|
|
373
|
+
cursor: pointer;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.classDetailsCheckboxLabel input[type="checkbox"] {
|
|
377
|
+
cursor: pointer;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.classDetailsCheckboxLabel input[type="checkbox"]:disabled {
|
|
381
|
+
cursor: not-allowed;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.lgWidthsSection {
|
|
385
|
+
display: flex;
|
|
386
|
+
flex-direction: column;
|
|
387
|
+
gap: 0.75rem;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.lgWidthsLayout {
|
|
391
|
+
display: flex;
|
|
392
|
+
gap: 1.25rem;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.lgWidthsColumn {
|
|
396
|
+
flex: 1;
|
|
397
|
+
display: flex;
|
|
398
|
+
flex-direction: column;
|
|
399
|
+
gap: 0.85rem;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.calculatedDiameterWrapper {
|
|
403
|
+
display: flex;
|
|
404
|
+
flex-direction: column;
|
|
405
|
+
gap: 0.4rem;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.calculatedDiameterDisplay {
|
|
409
|
+
display: flex;
|
|
410
|
+
justify-content: space-between;
|
|
411
|
+
align-items: center;
|
|
412
|
+
gap: 1rem;
|
|
413
|
+
padding: 0.85rem 1rem;
|
|
414
|
+
border: 1px solid #dee2e6;
|
|
415
|
+
border-radius: 8px;
|
|
416
|
+
background: #f8f9fa;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.calculatedDiameterValue {
|
|
420
|
+
font-size: 0.95rem;
|
|
421
|
+
font-weight: 700;
|
|
422
|
+
color: #343a40;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.calcExplanationToggle {
|
|
426
|
+
all: unset;
|
|
427
|
+
cursor: pointer;
|
|
428
|
+
font-size: 0.8rem;
|
|
429
|
+
color: var(--color-primary, #4f6ef7);
|
|
430
|
+
text-decoration: underline;
|
|
431
|
+
text-underline-offset: 2px;
|
|
432
|
+
padding: 0.1rem 0;
|
|
433
|
+
align-self: flex-start;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.calcExplanationToggle:hover {
|
|
437
|
+
color: var(--color-primary-hover, #3a56d4);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.calcExplanationPanel {
|
|
441
|
+
padding: 0.85rem 1rem;
|
|
442
|
+
border: 1px solid #dee2e6;
|
|
443
|
+
border-radius: 8px;
|
|
444
|
+
background: #f8f9fa;
|
|
445
|
+
font-size: 0.82rem;
|
|
446
|
+
color: #495057;
|
|
447
|
+
display: flex;
|
|
448
|
+
flex-direction: column;
|
|
449
|
+
gap: 0.55rem;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.calcExplanationFormula {
|
|
453
|
+
margin: 0;
|
|
454
|
+
font-family: monospace;
|
|
455
|
+
font-size: 0.88rem;
|
|
456
|
+
font-weight: 600;
|
|
457
|
+
color: #343a40;
|
|
458
|
+
letter-spacing: 0.01em;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.calcExplanationList {
|
|
462
|
+
margin: 0;
|
|
463
|
+
padding-left: 1.15rem;
|
|
464
|
+
display: flex;
|
|
465
|
+
flex-direction: column;
|
|
466
|
+
gap: 0.2rem;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.calcExplanationList li {
|
|
470
|
+
line-height: 1.5;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.calcExplanationNote {
|
|
474
|
+
margin: 0;
|
|
475
|
+
color: #6c757d;
|
|
476
|
+
font-style: italic;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.calcExplanationExample {
|
|
480
|
+
margin: 0;
|
|
481
|
+
padding-top: 0.35rem;
|
|
482
|
+
border-top: 1px solid #dee2e6;
|
|
483
|
+
}
|
|
484
|
+
|
|
237
485
|
.classCharacteristics input {
|
|
238
486
|
width: 100%;
|
|
239
487
|
padding: 0.75rem;
|
|
@@ -382,7 +630,7 @@ textarea:focus {
|
|
|
382
630
|
width: 100%;
|
|
383
631
|
}
|
|
384
632
|
|
|
385
|
-
.
|
|
633
|
+
.editorLayout .additionalNotesRow {
|
|
386
634
|
grid-column: 1 / -1;
|
|
387
635
|
}
|
|
388
636
|
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
BulletAnnotationData,
|
|
4
|
+
CartridgeCaseAnnotationData,
|
|
5
|
+
ShotshellAnnotationData,
|
|
6
|
+
} from '~/types/annotations';
|
|
7
|
+
import {
|
|
8
|
+
ALL_CALIBERS,
|
|
9
|
+
BULLET_BARREL_TYPE_OPTIONS,
|
|
10
|
+
BULLET_CORE_METAL_OPTIONS,
|
|
11
|
+
BULLET_JACKET_METAL_OPTIONS,
|
|
12
|
+
BULLET_TYPE_OPTIONS,
|
|
13
|
+
CARTRIDGE_APERTURE_SHAPE_OPTIONS,
|
|
14
|
+
CARTRIDGE_FPI_SHAPE_OPTIONS,
|
|
15
|
+
CARTRIDGE_METAL_OPTIONS,
|
|
16
|
+
CARTRIDGE_PRIMER_TYPE_OPTIONS,
|
|
17
|
+
SHOTSHELL_GAUGES,
|
|
18
|
+
calculateBulletDiameter,
|
|
19
|
+
formatCalculatedDiameter,
|
|
20
|
+
isCustomValue,
|
|
21
|
+
} from './class-details-shared';
|
|
22
|
+
|
|
23
|
+
interface UseClassDetailsStateParams {
|
|
24
|
+
bulletData?: BulletAnnotationData;
|
|
25
|
+
cartridgeCaseData?: CartridgeCaseAnnotationData;
|
|
26
|
+
shotshellData?: ShotshellAnnotationData;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface BuildSaveDataParams {
|
|
30
|
+
showBullet: boolean;
|
|
31
|
+
showCartridge: boolean;
|
|
32
|
+
showShotshell: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface BuildSaveDataResult {
|
|
36
|
+
bulletData: BulletAnnotationData | undefined;
|
|
37
|
+
cartridgeCaseData: CartridgeCaseAnnotationData | undefined;
|
|
38
|
+
shotshellData: ShotshellAnnotationData | undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface BulletDetailsState {
|
|
42
|
+
caliber: string;
|
|
43
|
+
caliberIsCustom: boolean;
|
|
44
|
+
mass: string;
|
|
45
|
+
diameter: string;
|
|
46
|
+
lgNumber: string;
|
|
47
|
+
lgDirection: string;
|
|
48
|
+
lWidths: string[];
|
|
49
|
+
gWidths: string[];
|
|
50
|
+
jacketMetal: string;
|
|
51
|
+
jacketMetalIsCustom: boolean;
|
|
52
|
+
coreMetal: string;
|
|
53
|
+
coreMetalIsCustom: boolean;
|
|
54
|
+
bulletType: string;
|
|
55
|
+
bulletTypeIsCustom: boolean;
|
|
56
|
+
barrelType: string;
|
|
57
|
+
barrelTypeIsCustom: boolean;
|
|
58
|
+
lgCount: number;
|
|
59
|
+
calculatedDiameter: number | null;
|
|
60
|
+
setCaliber: (value: string) => void;
|
|
61
|
+
setCaliberIsCustom: (value: boolean) => void;
|
|
62
|
+
setMass: (value: string) => void;
|
|
63
|
+
setDiameter: (value: string) => void;
|
|
64
|
+
setLgNumber: (value: string) => void;
|
|
65
|
+
setLgDirection: (value: string) => void;
|
|
66
|
+
updateLWidth: (index: number, value: string) => void;
|
|
67
|
+
updateGWidth: (index: number, value: string) => void;
|
|
68
|
+
setJacketMetal: (value: string) => void;
|
|
69
|
+
setJacketMetalIsCustom: (value: boolean) => void;
|
|
70
|
+
setCoreMetal: (value: string) => void;
|
|
71
|
+
setCoreMetalIsCustom: (value: boolean) => void;
|
|
72
|
+
setBulletType: (value: string) => void;
|
|
73
|
+
setBulletTypeIsCustom: (value: boolean) => void;
|
|
74
|
+
setBarrelType: (value: string) => void;
|
|
75
|
+
setBarrelTypeIsCustom: (value: boolean) => void;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface CartridgeCaseDetailsState {
|
|
79
|
+
caliber: string;
|
|
80
|
+
caliberIsCustom: boolean;
|
|
81
|
+
brand: string;
|
|
82
|
+
metal: string;
|
|
83
|
+
metalIsCustom: boolean;
|
|
84
|
+
primerType: string;
|
|
85
|
+
primerTypeIsCustom: boolean;
|
|
86
|
+
fpiShape: string;
|
|
87
|
+
fpiShapeIsCustom: boolean;
|
|
88
|
+
apertureShape: string;
|
|
89
|
+
apertureShapeIsCustom: boolean;
|
|
90
|
+
hasFpDrag: boolean;
|
|
91
|
+
hasExtractorMarks: boolean;
|
|
92
|
+
hasEjectorMarks: boolean;
|
|
93
|
+
hasChamberMarks: boolean;
|
|
94
|
+
hasMagazineLipMarks: boolean;
|
|
95
|
+
hasPrimerShear: boolean;
|
|
96
|
+
hasEjectionPortMarks: boolean;
|
|
97
|
+
setCaliber: (value: string) => void;
|
|
98
|
+
setCaliberIsCustom: (value: boolean) => void;
|
|
99
|
+
setBrand: (value: string) => void;
|
|
100
|
+
setMetal: (value: string) => void;
|
|
101
|
+
setMetalIsCustom: (value: boolean) => void;
|
|
102
|
+
setPrimerType: (value: string) => void;
|
|
103
|
+
setPrimerTypeIsCustom: (value: boolean) => void;
|
|
104
|
+
setFpiShape: (value: string) => void;
|
|
105
|
+
setFpiShapeIsCustom: (value: boolean) => void;
|
|
106
|
+
setApertureShape: (value: string) => void;
|
|
107
|
+
setApertureShapeIsCustom: (value: boolean) => void;
|
|
108
|
+
setHasFpDrag: (value: boolean) => void;
|
|
109
|
+
setHasExtractorMarks: (value: boolean) => void;
|
|
110
|
+
setHasEjectorMarks: (value: boolean) => void;
|
|
111
|
+
setHasChamberMarks: (value: boolean) => void;
|
|
112
|
+
setHasMagazineLipMarks: (value: boolean) => void;
|
|
113
|
+
setHasPrimerShear: (value: boolean) => void;
|
|
114
|
+
setHasEjectionPortMarks: (value: boolean) => void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface ShotshellDetailsState {
|
|
118
|
+
gauge: string;
|
|
119
|
+
gaugeIsCustom: boolean;
|
|
120
|
+
shotSize: string;
|
|
121
|
+
metal: string;
|
|
122
|
+
metalIsCustom: boolean;
|
|
123
|
+
brand: string;
|
|
124
|
+
fpiShape: string;
|
|
125
|
+
fpiShapeIsCustom: boolean;
|
|
126
|
+
hasExtractorMarks: boolean;
|
|
127
|
+
hasEjectorMarks: boolean;
|
|
128
|
+
hasChamberMarks: boolean;
|
|
129
|
+
setGauge: (value: string) => void;
|
|
130
|
+
setGaugeIsCustom: (value: boolean) => void;
|
|
131
|
+
setShotSize: (value: string) => void;
|
|
132
|
+
setMetal: (value: string) => void;
|
|
133
|
+
setMetalIsCustom: (value: boolean) => void;
|
|
134
|
+
setBrand: (value: string) => void;
|
|
135
|
+
setFpiShape: (value: string) => void;
|
|
136
|
+
setFpiShapeIsCustom: (value: boolean) => void;
|
|
137
|
+
setHasExtractorMarks: (value: boolean) => void;
|
|
138
|
+
setHasEjectorMarks: (value: boolean) => void;
|
|
139
|
+
setHasChamberMarks: (value: boolean) => void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const useClassDetailsState = ({
|
|
143
|
+
bulletData,
|
|
144
|
+
cartridgeCaseData,
|
|
145
|
+
shotshellData,
|
|
146
|
+
}: UseClassDetailsStateParams) => {
|
|
147
|
+
const [bCaliber, setBCaliber] = useState(() => bulletData?.caliber || '');
|
|
148
|
+
const [bCaliberIsCustom, setBCaliberIsCustom] = useState(() => isCustomValue(bulletData?.caliber, ALL_CALIBERS));
|
|
149
|
+
const [bMass, setBMass] = useState(() => bulletData?.mass || '');
|
|
150
|
+
const [bDiameter, setBDiameter] = useState(() => bulletData?.diameter || '');
|
|
151
|
+
const [bLgNumber, setBLgNumber] = useState(() => bulletData?.lgNumber !== undefined ? String(bulletData.lgNumber) : '');
|
|
152
|
+
const [bLgDirection, setBLgDirection] = useState(() => bulletData?.lgDirection || '');
|
|
153
|
+
const [bLWidths, setBLWidths] = useState<string[]>(() => bulletData?.lWidths || []);
|
|
154
|
+
const [bGWidths, setBGWidths] = useState<string[]>(() => bulletData?.gWidths || []);
|
|
155
|
+
const [bJacketMetal, setBJacketMetal] = useState(() => bulletData?.jacketMetal || '');
|
|
156
|
+
const [bJacketMetalIsCustom, setBJacketMetalIsCustom] = useState(() => isCustomValue(bulletData?.jacketMetal, BULLET_JACKET_METAL_OPTIONS));
|
|
157
|
+
const [bCoreMetal, setBCoreMetal] = useState(() => bulletData?.coreMetal || '');
|
|
158
|
+
const [bCoreMetalIsCustom, setBCoreMetalIsCustom] = useState(() => isCustomValue(bulletData?.coreMetal, BULLET_CORE_METAL_OPTIONS));
|
|
159
|
+
const [bBulletType, setBBulletType] = useState(() => bulletData?.bulletType || '');
|
|
160
|
+
const [bBulletTypeIsCustom, setBBulletTypeIsCustom] = useState(() => isCustomValue(bulletData?.bulletType, BULLET_TYPE_OPTIONS));
|
|
161
|
+
const [bBarrelType, setBBarrelType] = useState(() => bulletData?.barrelType || '');
|
|
162
|
+
const [bBarrelTypeIsCustom, setBBarrelTypeIsCustom] = useState(() => isCustomValue(bulletData?.barrelType, BULLET_BARREL_TYPE_OPTIONS));
|
|
163
|
+
|
|
164
|
+
const [cCaliber, setCCaliber] = useState(() => cartridgeCaseData?.caliber || '');
|
|
165
|
+
const [cCaliberIsCustom, setCCaliberIsCustom] = useState(() => isCustomValue(cartridgeCaseData?.caliber, ALL_CALIBERS));
|
|
166
|
+
const [cBrand, setCBrand] = useState(() => cartridgeCaseData?.brand || '');
|
|
167
|
+
const [cMetal, setCMetal] = useState(() => cartridgeCaseData?.metal || '');
|
|
168
|
+
const [cMetalIsCustom, setCMetalIsCustom] = useState(() => isCustomValue(cartridgeCaseData?.metal, CARTRIDGE_METAL_OPTIONS));
|
|
169
|
+
const [cPrimerType, setCPrimerType] = useState(() => cartridgeCaseData?.primerType || '');
|
|
170
|
+
const [cPrimerTypeIsCustom, setCPrimerTypeIsCustom] = useState(() => isCustomValue(cartridgeCaseData?.primerType, CARTRIDGE_PRIMER_TYPE_OPTIONS));
|
|
171
|
+
const [cFpiShape, setCFpiShape] = useState(() => cartridgeCaseData?.fpiShape || '');
|
|
172
|
+
const [cFpiShapeIsCustom, setCFpiShapeIsCustom] = useState(() => isCustomValue(cartridgeCaseData?.fpiShape, CARTRIDGE_FPI_SHAPE_OPTIONS));
|
|
173
|
+
const [cApertureShape, setCApertureShape] = useState(() => cartridgeCaseData?.apertureShape || '');
|
|
174
|
+
const [cApertureShapeIsCustom, setCApertureShapeIsCustom] = useState(() => isCustomValue(cartridgeCaseData?.apertureShape, CARTRIDGE_APERTURE_SHAPE_OPTIONS));
|
|
175
|
+
const [cHasFpDrag, setCHasFpDrag] = useState(() => cartridgeCaseData?.hasFpDrag ?? false);
|
|
176
|
+
const [cHasExtractorMarks, setCHasExtractorMarks] = useState(() => cartridgeCaseData?.hasExtractorMarks ?? false);
|
|
177
|
+
const [cHasEjectorMarks, setCHasEjectorMarks] = useState(() => cartridgeCaseData?.hasEjectorMarks ?? false);
|
|
178
|
+
const [cHasChamberMarks, setCHasChamberMarks] = useState(() => cartridgeCaseData?.hasChamberMarks ?? false);
|
|
179
|
+
const [cHasMagazineLipMarks, setCHasMagazineLipMarks] = useState(() => cartridgeCaseData?.hasMagazineLipMarks ?? false);
|
|
180
|
+
const [cHasPrimerShear, setCHasPrimerShear] = useState(() => cartridgeCaseData?.hasPrimerShear ?? false);
|
|
181
|
+
const [cHasEjectionPortMarks, setCHasEjectionPortMarks] = useState(() => cartridgeCaseData?.hasEjectionPortMarks ?? false);
|
|
182
|
+
|
|
183
|
+
const [sGauge, setSGauge] = useState(() => shotshellData?.gauge || '');
|
|
184
|
+
const [sGaugeIsCustom, setSGaugeIsCustom] = useState(() => isCustomValue(shotshellData?.gauge, SHOTSHELL_GAUGES));
|
|
185
|
+
const [sShotSize, setSShotSize] = useState(() => shotshellData?.shotSize || '');
|
|
186
|
+
const [sMetal, setSMetal] = useState(() => shotshellData?.metal || '');
|
|
187
|
+
const [sMetalIsCustom, setSMetalIsCustom] = useState(() => isCustomValue(shotshellData?.metal, CARTRIDGE_METAL_OPTIONS));
|
|
188
|
+
const [sBrand, setSBrand] = useState(() => shotshellData?.brand || '');
|
|
189
|
+
const [sFpiShape, setSFpiShape] = useState(() => shotshellData?.fpiShape || '');
|
|
190
|
+
const [sFpiShapeIsCustom, setSFpiShapeIsCustom] = useState(() => isCustomValue(shotshellData?.fpiShape, CARTRIDGE_FPI_SHAPE_OPTIONS));
|
|
191
|
+
const [sHasExtractorMarks, setSHasExtractorMarks] = useState(() => shotshellData?.hasExtractorMarks ?? false);
|
|
192
|
+
const [sHasEjectorMarks, setSHasEjectorMarks] = useState(() => shotshellData?.hasEjectorMarks ?? false);
|
|
193
|
+
const [sHasChamberMarks, setSHasChamberMarks] = useState(() => shotshellData?.hasChamberMarks ?? false);
|
|
194
|
+
|
|
195
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
196
|
+
|
|
197
|
+
const lgCount = Math.max(0, Math.min(30, Number(bLgNumber) || 0));
|
|
198
|
+
const calculatedDiameter = calculateBulletDiameter(lgCount, bLWidths, bGWidths);
|
|
199
|
+
|
|
200
|
+
const updateLWidth = (index: number, value: string) => {
|
|
201
|
+
setBLWidths((previous) => {
|
|
202
|
+
const next = [...previous];
|
|
203
|
+
next[index] = value;
|
|
204
|
+
return next;
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const updateGWidth = (index: number, value: string) => {
|
|
209
|
+
setBGWidths((previous) => {
|
|
210
|
+
const next = [...previous];
|
|
211
|
+
next[index] = value;
|
|
212
|
+
return next;
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const buildSaveData = ({
|
|
217
|
+
showBullet,
|
|
218
|
+
showCartridge,
|
|
219
|
+
showShotshell,
|
|
220
|
+
}: BuildSaveDataParams): BuildSaveDataResult => ({
|
|
221
|
+
bulletData: showBullet ? {
|
|
222
|
+
caliber: bCaliber || undefined,
|
|
223
|
+
mass: bMass || undefined,
|
|
224
|
+
diameter: bDiameter || undefined,
|
|
225
|
+
calcDiameter: calculatedDiameter !== null ? formatCalculatedDiameter(calculatedDiameter) : undefined,
|
|
226
|
+
lgNumber: bLgNumber ? Number(bLgNumber) : undefined,
|
|
227
|
+
lgDirection: bLgDirection || undefined,
|
|
228
|
+
lWidths: bLWidths.some(Boolean) ? bLWidths : undefined,
|
|
229
|
+
gWidths: bGWidths.some(Boolean) ? bGWidths : undefined,
|
|
230
|
+
jacketMetal: bJacketMetal || undefined,
|
|
231
|
+
coreMetal: bCoreMetal || undefined,
|
|
232
|
+
bulletType: bBulletType || undefined,
|
|
233
|
+
barrelType: bBarrelType || undefined,
|
|
234
|
+
} : undefined,
|
|
235
|
+
cartridgeCaseData: showCartridge ? {
|
|
236
|
+
caliber: cCaliber || undefined,
|
|
237
|
+
brand: cBrand || undefined,
|
|
238
|
+
metal: cMetal || undefined,
|
|
239
|
+
primerType: cPrimerType || undefined,
|
|
240
|
+
fpiShape: cFpiShape || undefined,
|
|
241
|
+
apertureShape: cApertureShape || undefined,
|
|
242
|
+
hasFpDrag: cHasFpDrag || undefined,
|
|
243
|
+
hasExtractorMarks: cHasExtractorMarks || undefined,
|
|
244
|
+
hasEjectorMarks: cHasEjectorMarks || undefined,
|
|
245
|
+
hasChamberMarks: cHasChamberMarks || undefined,
|
|
246
|
+
hasMagazineLipMarks: cHasMagazineLipMarks || undefined,
|
|
247
|
+
hasPrimerShear: cHasPrimerShear || undefined,
|
|
248
|
+
hasEjectionPortMarks: cHasEjectionPortMarks || undefined,
|
|
249
|
+
} : undefined,
|
|
250
|
+
shotshellData: showShotshell ? {
|
|
251
|
+
gauge: sGauge || undefined,
|
|
252
|
+
shotSize: sShotSize || undefined,
|
|
253
|
+
metal: sMetal || undefined,
|
|
254
|
+
brand: sBrand || undefined,
|
|
255
|
+
fpiShape: sFpiShape || undefined,
|
|
256
|
+
hasExtractorMarks: sHasExtractorMarks || undefined,
|
|
257
|
+
hasEjectorMarks: sHasEjectorMarks || undefined,
|
|
258
|
+
hasChamberMarks: sHasChamberMarks || undefined,
|
|
259
|
+
} : undefined,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const bullet: BulletDetailsState = {
|
|
263
|
+
caliber: bCaliber,
|
|
264
|
+
caliberIsCustom: bCaliberIsCustom,
|
|
265
|
+
mass: bMass,
|
|
266
|
+
diameter: bDiameter,
|
|
267
|
+
lgNumber: bLgNumber,
|
|
268
|
+
lgDirection: bLgDirection,
|
|
269
|
+
lWidths: bLWidths,
|
|
270
|
+
gWidths: bGWidths,
|
|
271
|
+
jacketMetal: bJacketMetal,
|
|
272
|
+
jacketMetalIsCustom: bJacketMetalIsCustom,
|
|
273
|
+
coreMetal: bCoreMetal,
|
|
274
|
+
coreMetalIsCustom: bCoreMetalIsCustom,
|
|
275
|
+
bulletType: bBulletType,
|
|
276
|
+
bulletTypeIsCustom: bBulletTypeIsCustom,
|
|
277
|
+
barrelType: bBarrelType,
|
|
278
|
+
barrelTypeIsCustom: bBarrelTypeIsCustom,
|
|
279
|
+
lgCount,
|
|
280
|
+
calculatedDiameter,
|
|
281
|
+
setCaliber: setBCaliber,
|
|
282
|
+
setCaliberIsCustom: setBCaliberIsCustom,
|
|
283
|
+
setMass: setBMass,
|
|
284
|
+
setDiameter: setBDiameter,
|
|
285
|
+
setLgNumber: setBLgNumber,
|
|
286
|
+
setLgDirection: setBLgDirection,
|
|
287
|
+
updateLWidth,
|
|
288
|
+
updateGWidth,
|
|
289
|
+
setJacketMetal: setBJacketMetal,
|
|
290
|
+
setJacketMetalIsCustom: setBJacketMetalIsCustom,
|
|
291
|
+
setCoreMetal: setBCoreMetal,
|
|
292
|
+
setCoreMetalIsCustom: setBCoreMetalIsCustom,
|
|
293
|
+
setBulletType: setBBulletType,
|
|
294
|
+
setBulletTypeIsCustom: setBBulletTypeIsCustom,
|
|
295
|
+
setBarrelType: setBBarrelType,
|
|
296
|
+
setBarrelTypeIsCustom: setBBarrelTypeIsCustom,
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const cartridgeCase: CartridgeCaseDetailsState = {
|
|
300
|
+
caliber: cCaliber,
|
|
301
|
+
caliberIsCustom: cCaliberIsCustom,
|
|
302
|
+
brand: cBrand,
|
|
303
|
+
metal: cMetal,
|
|
304
|
+
metalIsCustom: cMetalIsCustom,
|
|
305
|
+
primerType: cPrimerType,
|
|
306
|
+
primerTypeIsCustom: cPrimerTypeIsCustom,
|
|
307
|
+
fpiShape: cFpiShape,
|
|
308
|
+
fpiShapeIsCustom: cFpiShapeIsCustom,
|
|
309
|
+
apertureShape: cApertureShape,
|
|
310
|
+
apertureShapeIsCustom: cApertureShapeIsCustom,
|
|
311
|
+
hasFpDrag: cHasFpDrag,
|
|
312
|
+
hasExtractorMarks: cHasExtractorMarks,
|
|
313
|
+
hasEjectorMarks: cHasEjectorMarks,
|
|
314
|
+
hasChamberMarks: cHasChamberMarks,
|
|
315
|
+
hasMagazineLipMarks: cHasMagazineLipMarks,
|
|
316
|
+
hasPrimerShear: cHasPrimerShear,
|
|
317
|
+
hasEjectionPortMarks: cHasEjectionPortMarks,
|
|
318
|
+
setCaliber: setCCaliber,
|
|
319
|
+
setCaliberIsCustom: setCCaliberIsCustom,
|
|
320
|
+
setBrand: setCBrand,
|
|
321
|
+
setMetal: setCMetal,
|
|
322
|
+
setMetalIsCustom: setCMetalIsCustom,
|
|
323
|
+
setPrimerType: setCPrimerType,
|
|
324
|
+
setPrimerTypeIsCustom: setCPrimerTypeIsCustom,
|
|
325
|
+
setFpiShape: setCFpiShape,
|
|
326
|
+
setFpiShapeIsCustom: setCFpiShapeIsCustom,
|
|
327
|
+
setApertureShape: setCApertureShape,
|
|
328
|
+
setApertureShapeIsCustom: setCApertureShapeIsCustom,
|
|
329
|
+
setHasFpDrag: setCHasFpDrag,
|
|
330
|
+
setHasExtractorMarks: setCHasExtractorMarks,
|
|
331
|
+
setHasEjectorMarks: setCHasEjectorMarks,
|
|
332
|
+
setHasChamberMarks: setCHasChamberMarks,
|
|
333
|
+
setHasMagazineLipMarks: setCHasMagazineLipMarks,
|
|
334
|
+
setHasPrimerShear: setCHasPrimerShear,
|
|
335
|
+
setHasEjectionPortMarks: setCHasEjectionPortMarks,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const shotshell: ShotshellDetailsState = {
|
|
339
|
+
gauge: sGauge,
|
|
340
|
+
gaugeIsCustom: sGaugeIsCustom,
|
|
341
|
+
shotSize: sShotSize,
|
|
342
|
+
metal: sMetal,
|
|
343
|
+
metalIsCustom: sMetalIsCustom,
|
|
344
|
+
brand: sBrand,
|
|
345
|
+
fpiShape: sFpiShape,
|
|
346
|
+
fpiShapeIsCustom: sFpiShapeIsCustom,
|
|
347
|
+
hasExtractorMarks: sHasExtractorMarks,
|
|
348
|
+
hasEjectorMarks: sHasEjectorMarks,
|
|
349
|
+
hasChamberMarks: sHasChamberMarks,
|
|
350
|
+
setGauge: setSGauge,
|
|
351
|
+
setGaugeIsCustom: setSGaugeIsCustom,
|
|
352
|
+
setShotSize: setSShotSize,
|
|
353
|
+
setMetal: setSMetal,
|
|
354
|
+
setMetalIsCustom: setSMetalIsCustom,
|
|
355
|
+
setBrand: setSBrand,
|
|
356
|
+
setFpiShape: setSFpiShape,
|
|
357
|
+
setFpiShapeIsCustom: setSFpiShapeIsCustom,
|
|
358
|
+
setHasExtractorMarks: setSHasExtractorMarks,
|
|
359
|
+
setHasEjectorMarks: setSHasEjectorMarks,
|
|
360
|
+
setHasChamberMarks: setSHasChamberMarks,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
bullet,
|
|
365
|
+
cartridgeCase,
|
|
366
|
+
shotshell,
|
|
367
|
+
isSaving,
|
|
368
|
+
setIsSaving,
|
|
369
|
+
buildSaveData,
|
|
370
|
+
};
|
|
371
|
+
};
|