@eifi1/ui-kit 0.54.0 → 0.55.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/README.md +90 -76
- package/dist/admin/plan-change.js +2 -2
- package/dist/admin/plan-change.js.map +1 -1
- package/dist/admin/user-roster.d.ts +6 -0
- package/dist/admin/user-roster.js +4 -0
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +26 -0
- package/dist/billing/billing-labels.js +9 -0
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/subscription-standing.d.ts +61 -0
- package/dist/billing/subscription-standing.js +51 -0
- package/dist/billing/subscription-standing.js.map +1 -0
- package/dist/billing/usage-meter.d.ts +52 -0
- package/dist/billing/usage-meter.js +52 -0
- package/dist/billing/usage-meter.js.map +1 -0
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +4 -0
- package/dist/chart.js.map +1 -1
- package/dist/components/alert-banner.js +2 -3
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.js +3 -15
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.js +2 -2
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/bulk-action-bar.js +2 -1
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button.d.ts +35 -1
- package/dist/components/button.js +5 -9
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar-heatmap.js +2 -2
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/camera-button.d.ts +89 -0
- package/dist/components/camera-button.js +122 -0
- package/dist/components/camera-button.js.map +1 -0
- package/dist/components/camera-capture.d.ts +66 -0
- package/dist/components/camera-capture.js +190 -0
- package/dist/components/camera-capture.js.map +1 -0
- package/dist/components/camera-labels.d.ts +20 -0
- package/dist/components/camera-labels.js +15 -0
- package/dist/components/camera-labels.js.map +1 -0
- package/dist/components/chart-edge.d.ts +9 -0
- package/dist/components/chart-edge.js +14 -3
- package/dist/components/chart-edge.js.map +1 -1
- package/dist/components/chart-zoom-core.js +34 -29
- package/dist/components/chart-zoom-core.js.map +1 -1
- package/dist/components/checkbox-group.js +1 -1
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/combobox-core.js +3 -2
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.js +2 -2
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/consent-again-notice-labels.d.ts +25 -0
- package/dist/components/consent-again-notice-labels.js +15 -0
- package/dist/components/consent-again-notice-labels.js.map +1 -0
- package/dist/components/consent-again-notice.d.ts +85 -0
- package/dist/components/consent-again-notice.js +94 -0
- package/dist/components/consent-again-notice.js.map +1 -0
- package/dist/components/country-select.js +2 -7
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/currency-select.js +2 -2
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.js +2 -2
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-column-settings.js +2 -2
- package/dist/components/data-table-column-settings.js.map +1 -1
- package/dist/components/data-table-fit.js.map +1 -1
- package/dist/components/data-table-virtual.js +13 -3
- package/dist/components/data-table-virtual.js.map +1 -1
- package/dist/components/date-field.js +2 -2
- package/dist/components/date-field.js.map +1 -1
- package/dist/components/date-picker.js +2 -2
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/date-range-picker.js +2 -6
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +3 -1
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/dropdown.js +7 -3
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.js +2 -2
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/field-parts.d.ts +108 -6
- package/dist/components/field-parts.js +59 -6
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/field-suggestion-labels.d.ts +2 -0
- package/dist/components/field-suggestion-labels.js +10 -0
- package/dist/components/field-suggestion-labels.js.map +1 -0
- package/dist/components/field.js +1 -1
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-button.d.ts +1 -1
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/floating-panel.d.ts +7 -6
- package/dist/components/floating-panel.js +3 -17
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +3 -2
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/hover-menu.js +3 -2
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/icon-button.d.ts +23 -5
- package/dist/components/icon-button.js +6 -8
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/inline-confirm.js +2 -1
- package/dist/components/inline-confirm.js.map +1 -1
- package/dist/components/inline-entity-combobox.js +2 -2
- package/dist/components/inline-entity-combobox.js.map +1 -1
- package/dist/components/input.js +2 -2
- package/dist/components/input.js.map +1 -1
- package/dist/components/jump-calendar.js +3 -3
- package/dist/components/jump-calendar.js.map +1 -1
- package/dist/components/legal.d.ts +13 -1
- package/dist/components/legal.js +12 -0
- package/dist/components/legal.js.map +1 -1
- package/dist/components/legended-series-chart.d.ts +80 -0
- package/dist/components/legended-series-chart.js +70 -0
- package/dist/components/legended-series-chart.js.map +1 -0
- package/dist/components/lightbox.js +2 -1
- package/dist/components/lightbox.js.map +1 -1
- package/dist/components/mini-calendar.js +13 -5
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +23 -2
- package/dist/components/modal.js +24 -6
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +1 -1
- package/dist/components/month-picker.js +6 -6
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.js +2 -2
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.js +2 -6
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-input.js +2 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/page-header-layout.d.ts +18 -0
- package/dist/components/page-header-layout.js +23 -0
- package/dist/components/page-header-layout.js.map +1 -0
- package/dist/components/page-header-skeleton.d.ts +41 -0
- package/dist/components/page-header-skeleton.js +52 -0
- package/dist/components/page-header-skeleton.js.map +1 -0
- package/dist/components/page-header.js +4 -17
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/popover.js +7 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/release-notes-labels.d.ts +16 -0
- package/dist/components/release-notes-labels.js +18 -0
- package/dist/components/release-notes-labels.js.map +1 -0
- package/dist/components/release-notes.d.ts +65 -0
- package/dist/components/release-notes.js +65 -0
- package/dist/components/release-notes.js.map +1 -0
- package/dist/components/select.js +2 -2
- package/dist/components/select.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +9 -0
- package/dist/components/series-chart-labels.js +3 -1
- package/dist/components/series-chart-labels.js.map +1 -1
- package/dist/components/series-chart.d.ts +5 -0
- package/dist/components/series-chart.js +89 -36
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/toggle-group.js +3 -2
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/treemap-layout.js.map +1 -1
- package/dist/components/treemap.d.ts +17 -15
- package/dist/components/treemap.js +167 -85
- package/dist/components/treemap.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/eslint/role-utilities.d.ts +2 -2
- package/dist/eslint/role-utilities.js.map +1 -1
- package/dist/feedback/feedback-inbox-page.d.ts +260 -0
- package/dist/feedback/feedback-inbox-page.js +207 -0
- package/dist/feedback/feedback-inbox-page.js.map +1 -0
- package/dist/feedback/feedback-vocabulary.js +2 -2
- package/dist/feedback/feedback-vocabulary.js.map +1 -1
- package/dist/feedback.d.ts +1 -0
- package/dist/feedback.js +6 -0
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-debounce.d.ts +32 -5
- package/dist/hooks/use-debounce.js +6 -5
- package/dist/hooks/use-debounce.js.map +1 -1
- package/dist/hooks/use-file-picker.d.ts +3 -1
- package/dist/hooks/use-file-picker.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +19 -3
- package/dist/hooks/use-focus-trap.js +15 -5
- package/dist/hooks/use-focus-trap.js.map +1 -1
- package/dist/hooks/use-has-camera.d.ts +36 -0
- package/dist/hooks/use-has-camera.js +53 -0
- package/dist/hooks/use-has-camera.js.map +1 -0
- package/dist/hooks/use-hotkey.js +1 -10
- package/dist/hooks/use-hotkey.js.map +1 -1
- package/dist/hooks/use-overlay-history.d.ts +6 -1
- package/dist/hooks/use-overlay-history.js +10 -2
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/i18n/defaults.js +11 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +75 -3
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +20 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +92 -4
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +71 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +72 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +74 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +74 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +71 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +22 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +24 -5
- package/dist/index.js +35 -1
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-labels.d.ts +8 -0
- package/dist/landing/landing-labels.js +2 -1
- package/dist/landing/landing-labels.js.map +1 -1
- package/dist/lib/changelog.d.ts +130 -0
- package/dist/lib/changelog.js +137 -0
- package/dist/lib/changelog.js.map +1 -0
- package/dist/lib/clipboard-minus.js +2 -2
- package/dist/lib/clipboard-minus.js.map +1 -1
- package/dist/lib/dates.d.ts +29 -3
- package/dist/lib/dates.js +41 -12
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/editable-content.d.ts +33 -0
- package/dist/lib/editable-content.js +15 -0
- package/dist/lib/editable-content.js.map +1 -0
- package/dist/lib/focus-left.d.ts +47 -1
- package/dist/lib/focus-left.js +11 -2
- package/dist/lib/focus-left.js.map +1 -1
- package/dist/lib/server-wake.d.ts +49 -9
- package/dist/lib/server-wake.js +25 -14
- package/dist/lib/server-wake.js.map +1 -1
- package/dist/lib/session-refresh.d.ts +53 -0
- package/dist/lib/session-refresh.js +27 -1
- package/dist/lib/session-refresh.js.map +1 -1
- package/dist/rhf/fields.js +3 -3
- package/dist/rhf/fields.js.map +1 -1
- package/dist/shell/app-shell.d.ts +25 -1
- package/dist/shell/app-shell.js +3 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/dev-menu.d.ts +3 -0
- package/dist/shell/dev-menu.js +4 -2
- package/dist/shell/dev-menu.js.map +1 -1
- package/dist/shell/held-changes-banner-labels.d.ts +23 -0
- package/dist/shell/held-changes-banner-labels.js +14 -0
- package/dist/shell/held-changes-banner-labels.js.map +1 -0
- package/dist/shell/held-changes-banner.d.ts +75 -0
- package/dist/shell/held-changes-banner.js +106 -0
- package/dist/shell/held-changes-banner.js.map +1 -0
- package/dist/shell/sync-status-indicator.d.ts +27 -12
- package/dist/shell/sync-status-indicator.js +13 -4
- package/dist/shell/sync-status-indicator.js.map +1 -1
- package/dist/shell/top-bar-parts-labels.d.ts +5 -0
- package/dist/shell/top-bar-parts-labels.js +3 -1
- package/dist/shell/top-bar-parts-labels.js.map +1 -1
- package/dist/shell.d.ts +2 -0
- package/dist/shell.js +3 -0
- package/dist/shell.js.map +1 -1
- package/dist/sync/engine.d.ts +11 -2
- package/dist/sync/engine.js +13 -5
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/hooks.d.ts +3 -2
- package/dist/sync/hooks.js +4 -2
- package/dist/sync/hooks.js.map +1 -1
- package/dist/testing/css-variables.generated.d.ts +2 -2
- package/dist/testing/css-variables.generated.js +1 -1
- package/dist/testing/css-variables.generated.js.map +1 -1
- package/dist/theme/chart-palette.d.ts +18 -0
- package/dist/theme/chart-palette.js +17 -1
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/contrast-tokens.d.ts +3 -3
- package/dist/theme/contrast-tokens.js.map +1 -1
- package/dist/theme/contrast.d.ts +4 -4
- package/dist/theme/contrast.js.map +1 -1
- package/dist/tour/tour-overlay.js +2 -1
- package/dist/tour/tour-overlay.js.map +1 -1
- package/package.json +5 -9
- package/tokens.css +19 -54
- package/src/account/account-parts.ts +0 -47
- package/src/account/account-settings-labels.ts +0 -240
- package/src/account/account-settings.tsx +0 -698
- package/src/account/data-export-setting-labels.ts +0 -20
- package/src/account/data-export-setting.tsx +0 -200
- package/src/account/delete-account-setting-labels.ts +0 -33
- package/src/account/delete-account-setting.tsx +0 -342
- package/src/account/email-change-setting-labels.ts +0 -31
- package/src/account/email-change-setting.tsx +0 -443
- package/src/account/passkeys-setting.tsx +0 -445
- package/src/account/sessions-setting-labels.ts +0 -24
- package/src/account/sessions-setting.tsx +0 -283
- package/src/admin/admin-action-confirm-labels.ts +0 -19
- package/src/admin/admin-action-confirm.tsx +0 -509
- package/src/admin/admin-action-log-labels.ts +0 -83
- package/src/admin/admin-action-log.tsx +0 -464
- package/src/admin/admin-area-labels.ts +0 -114
- package/src/admin/admin-forbidden.tsx +0 -75
- package/src/admin/admin-overview.tsx +0 -258
- package/src/admin/admin-parts.ts +0 -68
- package/src/admin/admin-sections.tsx +0 -208
- package/src/admin/admin-tool-links.tsx +0 -90
- package/src/admin/invitations-panel-labels.ts +0 -31
- package/src/admin/invitations-panel.tsx +0 -516
- package/src/admin/mail-test-card-labels.ts +0 -19
- package/src/admin/mail-test-card.tsx +0 -189
- package/src/admin/plan-change-labels.ts +0 -5
- package/src/admin/plan-change.tsx +0 -474
- package/src/admin/reviewer-scope-editor-labels.ts +0 -16
- package/src/admin/reviewer-scope-editor.tsx +0 -224
- package/src/admin/role-select-labels.ts +0 -16
- package/src/admin/role-select.tsx +0 -282
- package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
- package/src/admin/transfer-ownership-dialog.tsx +0 -162
- package/src/admin/user-roster-labels.ts +0 -17
- package/src/admin/user-roster.tsx +0 -559
- package/src/auth/accept-invitation-labels.ts +0 -21
- package/src/auth/accept-invitation.tsx +0 -210
- package/src/auth/auth-errors-labels.ts +0 -23
- package/src/auth/auth-errors.ts +0 -439
- package/src/auth/complete-name-dialog-labels.ts +0 -16
- package/src/auth/complete-name-dialog.tsx +0 -253
- package/src/auth/email-tag.ts +0 -75
- package/src/auth/forgot-password-form-labels.ts +0 -20
- package/src/auth/forgot-password-form.tsx +0 -221
- package/src/auth/form-rules.ts +0 -40
- package/src/auth/new-password-fields.tsx +0 -85
- package/src/auth/not-found-page-labels.ts +0 -12
- package/src/auth/not-found-page.tsx +0 -95
- package/src/auth/register-form-labels.ts +0 -31
- package/src/auth/register-form.tsx +0 -456
- package/src/auth/reset-password-form-labels.ts +0 -27
- package/src/auth/reset-password-form.tsx +0 -341
- package/src/auth/sign-in-form-labels.ts +0 -44
- package/src/auth/sign-in-form.tsx +0 -858
- package/src/auth/status-parts.tsx +0 -132
- package/src/auth/verify-email-labels.ts +0 -26
- package/src/auth/verify-email.tsx +0 -445
- package/src/billing/billing-banner.tsx +0 -258
- package/src/billing/billing-labels.ts +0 -381
- package/src/billing/billing-lock.ts +0 -108
- package/src/billing/billing-overview.ts +0 -157
- package/src/billing/billing-standing.ts +0 -80
- package/src/billing/cancel-request.ts +0 -97
- package/src/billing/cancel-subscription.tsx +0 -98
- package/src/billing/checkout-processing.ts +0 -394
- package/src/billing/checkout-return.ts +0 -90
- package/src/billing/pay-page.ts +0 -330
- package/src/billing/plan-card.tsx +0 -562
- package/src/billing/plan-change-labels.ts +0 -66
- package/src/billing/plan-limit-notice.tsx +0 -148
- package/src/billing/plan-limit-toast.tsx +0 -90
- package/src/billing/plan-limit.ts +0 -52
- package/src/billing/plan-price.ts +0 -98
- package/src/billing/plan-switch.tsx +0 -216
- package/src/billing/provider-page.ts +0 -84
- package/src/billing/subscription-actions.tsx +0 -247
- package/src/billing/subscription-status-chip.tsx +0 -95
- package/src/chart.ts +0 -103
- package/src/components/account-chips-labels.ts +0 -16
- package/src/components/account-chips.tsx +0 -474
- package/src/components/alert-banner.tsx +0 -670
- package/src/components/amount-input.tsx +0 -816
- package/src/components/appearance-labels.ts +0 -63
- package/src/components/authed-image-labels.ts +0 -11
- package/src/components/authed-image.tsx +0 -209
- package/src/components/autocomplete.tsx +0 -645
- package/src/components/breadcrumbs-labels.ts +0 -10
- package/src/components/breadcrumbs.tsx +0 -205
- package/src/components/bulk-action-bar-labels.ts +0 -11
- package/src/components/bulk-action-bar.tsx +0 -443
- package/src/components/button-group.tsx +0 -223
- package/src/components/button-look.ts +0 -256
- package/src/components/button.tsx +0 -339
- package/src/components/calculator.tsx +0 -341
- package/src/components/calendar-heatmap-labels.ts +0 -15
- package/src/components/calendar-heatmap.tsx +0 -600
- package/src/components/card.tsx +0 -377
- package/src/components/chart-edge.ts +0 -106
- package/src/components/chart-figure.tsx +0 -35
- package/src/components/chart-height.ts +0 -30
- package/src/components/chart-zoom-core.tsx +0 -627
- package/src/components/chart-zoom.tsx +0 -22
- package/src/components/chart.tsx +0 -554
- package/src/components/checkbox-group.tsx +0 -247
- package/src/components/checkbox.tsx +0 -346
- package/src/components/chip-input.tsx +0 -370
- package/src/components/chip-labels.ts +0 -13
- package/src/components/chip.tsx +0 -843
- package/src/components/choice-card.tsx +0 -544
- package/src/components/column-mapper-labels.ts +0 -38
- package/src/components/column-mapper.tsx +0 -655
- package/src/components/combobox-core.tsx +0 -1321
- package/src/components/combobox-suggestions.tsx +0 -131
- package/src/components/combobox.tsx +0 -716
- package/src/components/confirm-context.ts +0 -7
- package/src/components/confirm-dialog-labels.ts +0 -13
- package/src/components/confirm-dialog.tsx +0 -341
- package/src/components/copy-button-labels.ts +0 -13
- package/src/components/copy-button.tsx +0 -192
- package/src/components/country-select-labels.ts +0 -11
- package/src/components/country-select.tsx +0 -539
- package/src/components/currency-option-list.tsx +0 -188
- package/src/components/currency-select.tsx +0 -343
- package/src/components/danger-confirm-labels.ts +0 -20
- package/src/components/danger-confirm.tsx +0 -987
- package/src/components/data-table-cells.tsx +0 -118
- package/src/components/data-table-column-settings.tsx +0 -198
- package/src/components/data-table-filter-popover.tsx +0 -289
- package/src/components/data-table-filters.ts +0 -315
- package/src/components/data-table-fit.ts +0 -301
- package/src/components/data-table-groups.tsx +0 -288
- package/src/components/data-table-head.tsx +0 -280
- package/src/components/data-table-labels.ts +0 -198
- package/src/components/data-table-leading-row.tsx +0 -58
- package/src/components/data-table-mobile.tsx +0 -1117
- package/src/components/data-table-pagination.tsx +0 -202
- package/src/components/data-table-resize.ts +0 -188
- package/src/components/data-table-row-actions.tsx +0 -302
- package/src/components/data-table-row.tsx +0 -496
- package/src/components/data-table-selection.ts +0 -71
- package/src/components/data-table-shared.tsx +0 -287
- package/src/components/data-table-sort.ts +0 -163
- package/src/components/data-table-virtual.ts +0 -576
- package/src/components/data-table.tsx +0 -2598
- package/src/components/date-field.tsx +0 -684
- package/src/components/date-format-setting.tsx +0 -64
- package/src/components/date-picker.tsx +0 -404
- package/src/components/date-range-picker.tsx +0 -518
- package/src/components/description-list-labels.ts +0 -9
- package/src/components/description-list.tsx +0 -386
- package/src/components/dialog-frame-labels.ts +0 -7
- package/src/components/dialog-frame.tsx +0 -284
- package/src/components/disclosure.tsx +0 -476
- package/src/components/dropdown.tsx +0 -461
- package/src/components/empty-state.tsx +0 -136
- package/src/components/entity-combobox.tsx +0 -422
- package/src/components/error-boundary-labels.ts +0 -20
- package/src/components/error-boundary.tsx +0 -675
- package/src/components/facing-pair.ts +0 -93
- package/src/components/field-base-labels.ts +0 -20
- package/src/components/field-base.tsx +0 -656
- package/src/components/field-parts.tsx +0 -825
- package/src/components/field-strip.tsx +0 -129
- package/src/components/field.tsx +0 -206
- package/src/components/file-button.tsx +0 -295
- package/src/components/file-dropzone.tsx +0 -559
- package/src/components/floating-panel-labels.ts +0 -10
- package/src/components/floating-panel.tsx +0 -828
- package/src/components/focus-ring.ts +0 -16
- package/src/components/form-actions-labels.ts +0 -11
- package/src/components/form-actions.tsx +0 -578
- package/src/components/full-bleed-dialog.tsx +0 -294
- package/src/components/grouped-picker.tsx +0 -184
- package/src/components/help-popover.tsx +0 -117
- package/src/components/hover-menu.tsx +0 -343
- package/src/components/iban-input-labels.ts +0 -14
- package/src/components/iban-input.tsx +0 -236
- package/src/components/icon-button.tsx +0 -959
- package/src/components/icon-picker-labels.ts +0 -13
- package/src/components/icon-picker.tsx +0 -232
- package/src/components/image-grid-labels.ts +0 -11
- package/src/components/image-grid.tsx +0 -202
- package/src/components/inline-confirm.tsx +0 -116
- package/src/components/inline-edit-field-labels.ts +0 -11
- package/src/components/inline-edit-field.tsx +0 -552
- package/src/components/inline-entity-combobox.tsx +0 -849
- package/src/components/input.tsx +0 -394
- package/src/components/jump-calendar.tsx +0 -265
- package/src/components/language-select.tsx +0 -93
- package/src/components/legal-labels.ts +0 -105
- package/src/components/legal-page.tsx +0 -711
- package/src/components/legal.tsx +0 -668
- package/src/components/lightbox-labels.ts +0 -18
- package/src/components/lightbox.tsx +0 -399
- package/src/components/line-items-labels.ts +0 -14
- package/src/components/line-items.tsx +0 -602
- package/src/components/list-labels.ts +0 -12
- package/src/components/list.tsx +0 -870
- package/src/components/loading-state.tsx +0 -78
- package/src/components/measured-grid-labels.ts +0 -25
- package/src/components/measured-grid.tsx +0 -837
- package/src/components/menu-item.tsx +0 -241
- package/src/components/mini-calendar-labels.ts +0 -17
- package/src/components/mini-calendar.tsx +0 -792
- package/src/components/modal.tsx +0 -534
- package/src/components/money-draft.ts +0 -83
- package/src/components/money-field.tsx +0 -76
- package/src/components/month-grid.tsx +0 -229
- package/src/components/month-picker-labels.ts +0 -22
- package/src/components/month-picker.tsx +0 -1087
- package/src/components/multi-entity-combobox.tsx +0 -369
- package/src/components/multi-select.tsx +0 -376
- package/src/components/nav-pills.tsx +0 -198
- package/src/components/number-field.tsx +0 -327
- package/src/components/number-input.tsx +0 -559
- package/src/components/numeric-field.ts +0 -169
- package/src/components/numpad-sheet.tsx +0 -285
- package/src/components/one-time-code-input.tsx +0 -270
- package/src/components/page-contents-labels.ts +0 -9
- package/src/components/page-contents.tsx +0 -282
- package/src/components/page-header-context.ts +0 -8
- package/src/components/page-header.tsx +0 -298
- package/src/components/password-strength-labels.ts +0 -22
- package/src/components/password-strength.tsx +0 -241
- package/src/components/phone-input-labels.ts +0 -10
- package/src/components/phone-input.tsx +0 -282
- package/src/components/picker-sheet.tsx +0 -330
- package/src/components/pie-chart-labels.ts +0 -40
- package/src/components/pie-chart.tsx +0 -480
- package/src/components/popover-labels.ts +0 -14
- package/src/components/popover.tsx +0 -268
- package/src/components/progress-bar-labels.ts +0 -10
- package/src/components/progress-bar.tsx +0 -479
- package/src/components/qr-code.tsx +0 -76
- package/src/components/reauth-dialog-labels.ts +0 -13
- package/src/components/reauth-dialog.tsx +0 -265
- package/src/components/row-actions-labels.ts +0 -12
- package/src/components/row-actions.tsx +0 -795
- package/src/components/scroll-area.tsx +0 -123
- package/src/components/search-field.tsx +0 -192
- package/src/components/select.tsx +0 -294
- package/src/components/separator.tsx +0 -33
- package/src/components/series-chart-axes.ts +0 -342
- package/src/components/series-chart-budget.ts +0 -150
- package/src/components/series-chart-labels.ts +0 -33
- package/src/components/series-chart-thin.ts +0 -180
- package/src/components/series-chart-ticks.ts +0 -194
- package/src/components/series-chart.tsx +0 -1906
- package/src/components/server-wake-labels.ts +0 -11
- package/src/components/server-wake.tsx +0 -129
- package/src/components/settings-fields.tsx +0 -237
- package/src/components/share-card-labels.ts +0 -30
- package/src/components/share-card.tsx +0 -675
- package/src/components/signature-pad-labels.ts +0 -24
- package/src/components/signature-pad.tsx +0 -747
- package/src/components/signed-amount-labels.ts +0 -10
- package/src/components/signed-amount.tsx +0 -336
- package/src/components/skeleton.tsx +0 -80
- package/src/components/slider.tsx +0 -338
- package/src/components/sparkline-labels.ts +0 -14
- package/src/components/sparkline.tsx +0 -322
- package/src/components/spinner.tsx +0 -81
- package/src/components/stat-tile-labels.ts +0 -15
- package/src/components/stat-tile.tsx +0 -754
- package/src/components/status-dot.tsx +0 -170
- package/src/components/swatch-picker-labels.ts +0 -10
- package/src/components/swatch-picker.tsx +0 -283
- package/src/components/swipeable-row.tsx +0 -386
- package/src/components/switch.tsx +0 -273
- package/src/components/table-skeleton.tsx +0 -136
- package/src/components/table.tsx +0 -647
- package/src/components/tabs-labels.ts +0 -13
- package/src/components/tabs.tsx +0 -693
- package/src/components/text-link.tsx +0 -359
- package/src/components/text.tsx +0 -150
- package/src/components/textarea.tsx +0 -330
- package/src/components/tile-radio.tsx +0 -332
- package/src/components/time-input.tsx +0 -121
- package/src/components/toast-labels.ts +0 -12
- package/src/components/toast.tsx +0 -519
- package/src/components/toggle-group.tsx +0 -752
- package/src/components/toggle-legend.tsx +0 -394
- package/src/components/tooltip.tsx +0 -1149
- package/src/components/translation-review-editor.tsx +0 -290
- package/src/components/translation-review-labels.ts +0 -181
- package/src/components/translation-review-parts.tsx +0 -272
- package/src/components/translation-review.tsx +0 -1240
- package/src/components/tree-view.tsx +0 -589
- package/src/components/treemap-labels.ts +0 -47
- package/src/components/treemap-layout.ts +0 -210
- package/src/components/treemap.tsx +0 -1184
- package/src/components/trigger-aria.ts +0 -44
- package/src/components/use-anchor-dir.ts +0 -20
- package/src/components/use-mobile-reveal.ts +0 -62
- package/src/components/use-table-state.ts +0 -753
- package/src/components/user-avatar.tsx +0 -116
- package/src/components/wizard-stepper.tsx +0 -42
- package/src/components/write-lock-labels.ts +0 -10
- package/src/components/write-lock.tsx +0 -253
- package/src/currencies.ts +0 -90
- package/src/data-table.ts +0 -42
- package/src/demo/demo-banner.tsx +0 -153
- package/src/demo/demo-countdown.ts +0 -136
- package/src/demo/demo-ended.tsx +0 -90
- package/src/demo/demo-labels.ts +0 -78
- package/src/demo/demo-session.ts +0 -32
- package/src/demo/demo-start.tsx +0 -156
- package/src/eslint/index.ts +0 -161
- package/src/eslint/role-utilities.generated.ts +0 -146
- package/src/eslint/role-utilities.ts +0 -233
- package/src/feedback/feedback-attachment-download.tsx +0 -77
- package/src/feedback/feedback-attachment-labels.ts +0 -37
- package/src/feedback/feedback-attachment.tsx +0 -1239
- package/src/feedback/feedback-capture.ts +0 -287
- package/src/feedback/feedback-comment-thread.tsx +0 -353
- package/src/feedback/feedback-context-labels.ts +0 -11
- package/src/feedback/feedback-context.tsx +0 -245
- package/src/feedback/feedback-crash.ts +0 -547
- package/src/feedback/feedback-diagnostics.tsx +0 -41
- package/src/feedback/feedback-dialog-labels.ts +0 -29
- package/src/feedback/feedback-dialog.tsx +0 -452
- package/src/feedback/feedback-entry-file.tsx +0 -150
- package/src/feedback/feedback-inbox.tsx +0 -552
- package/src/feedback/feedback-labels.ts +0 -144
- package/src/feedback/feedback-menu-labels.ts +0 -12
- package/src/feedback/feedback-menu.tsx +0 -177
- package/src/feedback/feedback-mutation.ts +0 -20
- package/src/feedback/feedback-page-link.ts +0 -90
- package/src/feedback/feedback-public-labels.ts +0 -81
- package/src/feedback/feedback-public-list.tsx +0 -524
- package/src/feedback/feedback-public-switch.tsx +0 -102
- package/src/feedback/feedback-public-types.ts +0 -101
- package/src/feedback/feedback-record.ts +0 -428
- package/src/feedback/feedback-replies-context.ts +0 -9
- package/src/feedback/feedback-replies.tsx +0 -343
- package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
- package/src/feedback/feedback-row-detail-labels.ts +0 -31
- package/src/feedback/feedback-row-detail.tsx +0 -871
- package/src/feedback/feedback-status-undo.ts +0 -109
- package/src/feedback/feedback-submit-dialog.tsx +0 -131
- package/src/feedback/feedback-submit.tsx +0 -270
- package/src/feedback/feedback-swipe.tsx +0 -192
- package/src/feedback/feedback-table-labels.ts +0 -27
- package/src/feedback/feedback-table.tsx +0 -912
- package/src/feedback/feedback-thread-labels.ts +0 -22
- package/src/feedback/feedback-thread-parts-labels.ts +0 -41
- package/src/feedback/feedback-thread.tsx +0 -905
- package/src/feedback/feedback-vocabulary.ts +0 -263
- package/src/feedback/feedback-vote-button.tsx +0 -124
- package/src/feedback.ts +0 -116
- package/src/hooks/use-anchored-panel.ts +0 -396
- package/src/hooks/use-anchored-rect.ts +0 -77
- package/src/hooks/use-announce.ts +0 -85
- package/src/hooks/use-authed-src.ts +0 -167
- package/src/hooks/use-body-scroll-lock.ts +0 -119
- package/src/hooks/use-breakpoint.ts +0 -133
- package/src/hooks/use-close-transition.ts +0 -107
- package/src/hooks/use-copy-to-clipboard.ts +0 -122
- package/src/hooks/use-debounce.ts +0 -114
- package/src/hooks/use-dismiss.ts +0 -371
- package/src/hooks/use-document-title.ts +0 -33
- package/src/hooks/use-file-drop.ts +0 -121
- package/src/hooks/use-file-picker-labels.ts +0 -28
- package/src/hooks/use-file-picker.tsx +0 -359
- package/src/hooks/use-focus-trap.ts +0 -199
- package/src/hooks/use-hotkey.ts +0 -212
- package/src/hooks/use-large-text.ts +0 -41
- package/src/hooks/use-long-press.ts +0 -140
- package/src/hooks/use-media-query.ts +0 -39
- package/src/hooks/use-mounted.ts +0 -39
- package/src/hooks/use-noindex.ts +0 -37
- package/src/hooks/use-overlay-history.ts +0 -714
- package/src/hooks/use-row-swipe.ts +0 -207
- package/src/hooks/use-search-param-state.ts +0 -434
- package/src/hooks/use-windowed-rows.ts +0 -225
- package/src/i18n/defaults.ts +0 -267
- package/src/i18n/german.ts +0 -2059
- package/src/i18n/kit-labels.tsx +0 -1003
- package/src/i18n/languages.ts +0 -349
- package/src/i18n/locales/de-CH.ts +0 -24
- package/src/i18n/locales/en.ts +0 -267
- package/src/i18n/locales/es.ts +0 -1999
- package/src/i18n/locales/fr.ts +0 -2037
- package/src/i18n/locales/hu.ts +0 -2004
- package/src/i18n/locales/it.ts +0 -2014
- package/src/i18n/locales/zh.ts +0 -1912
- package/src/i18n/plurals.ts +0 -86
- package/src/i18n/review.ts +0 -475
- package/src/i18n/shared-labels.ts +0 -138
- package/src/i18n/swiss.ts +0 -29
- package/src/index.ts +0 -1191
- package/src/landing/access.ts +0 -93
- package/src/landing/landing-actions.tsx +0 -97
- package/src/landing/landing-labels.ts +0 -59
- package/src/landing/landing-sections.tsx +0 -341
- package/src/landing/page-seo.ts +0 -261
- package/src/landing/public-header.tsx +0 -121
- package/src/landing/public-page.tsx +0 -106
- package/src/landing/routing.tsx +0 -225
- package/src/lib/broadcast.ts +0 -52
- package/src/lib/calc.ts +0 -323
- package/src/lib/chip-edge-snap.ts +0 -212
- package/src/lib/clipboard-minus.ts +0 -107
- package/src/lib/clipping.ts +0 -34
- package/src/lib/cn.ts +0 -28
- package/src/lib/column-mapping.ts +0 -234
- package/src/lib/countries.ts +0 -91
- package/src/lib/data-attributes.ts +0 -14
- package/src/lib/date-format.ts +0 -227
- package/src/lib/dates.ts +0 -288
- package/src/lib/day-format.ts +0 -96
- package/src/lib/decimal-marks.ts +0 -108
- package/src/lib/describe-failure.ts +0 -18
- package/src/lib/device-settings.ts +0 -147
- package/src/lib/direction.ts +0 -74
- package/src/lib/document-navigation.ts +0 -17
- package/src/lib/document-title.ts +0 -41
- package/src/lib/download.ts +0 -172
- package/src/lib/float-clearance.ts +0 -70
- package/src/lib/floating-stack.ts +0 -70
- package/src/lib/focus-left.ts +0 -156
- package/src/lib/format.ts +0 -585
- package/src/lib/http-status.ts +0 -38
- package/src/lib/iban.ts +0 -251
- package/src/lib/ime.ts +0 -24
- package/src/lib/intl-cache.ts +0 -27
- package/src/lib/iso-day.ts +0 -13
- package/src/lib/lazy-with-fallback.ts +0 -82
- package/src/lib/logger.ts +0 -69
- package/src/lib/minus.ts +0 -50
- package/src/lib/path-pattern.ts +0 -55
- package/src/lib/person-name.ts +0 -155
- package/src/lib/phone.ts +0 -208
- package/src/lib/plain-text.ts +0 -22
- package/src/lib/presence.ts +0 -18
- package/src/lib/qr-encode.ts +0 -444
- package/src/lib/quiet-focus.ts +0 -32
- package/src/lib/reduced-motion.ts +0 -22
- package/src/lib/refs.ts +0 -22
- package/src/lib/round.ts +0 -43
- package/src/lib/safe-storage.ts +0 -119
- package/src/lib/server-wake.ts +0 -315
- package/src/lib/session-refresh.ts +0 -472
- package/src/lib/share-format.ts +0 -18
- package/src/lib/status-tone.ts +0 -43
- package/src/lib/strip-fade.ts +0 -287
- package/src/lib/table-text.ts +0 -554
- package/src/lib/text-case.ts +0 -19
- package/src/lib/text-match.ts +0 -169
- package/src/lib/thenable.ts +0 -19
- package/src/lib/translation-review.ts +0 -769
- package/src/notifications/device-zone-report.ts +0 -67
- package/src/notifications/feedback-notification-types-labels.ts +0 -33
- package/src/notifications/feedback-notification-types.ts +0 -71
- package/src/notifications/notification-bell.tsx +0 -119
- package/src/notifications/notification-inbox.tsx +0 -309
- package/src/notifications/notification-parts.tsx +0 -81
- package/src/notifications/notification-types-setting.tsx +0 -406
- package/src/notifications/notification-types.ts +0 -89
- package/src/notifications/notifications-labels.ts +0 -287
- package/src/notifications/push-device-setting.tsx +0 -230
- package/src/notifications/push-devices-setting.tsx +0 -374
- package/src/notifications/push-worker.ts +0 -168
- package/src/notifications/quiet-hours-setting.tsx +0 -238
- package/src/notifications/unsubscribe-panel.tsx +0 -200
- package/src/notifications/use-push-device.ts +0 -448
- package/src/offline/local-first.ts +0 -100
- package/src/offline/pwa-update-prompt-labels.ts +0 -9
- package/src/offline/pwa-update-prompt.tsx +0 -78
- package/src/offline/query-persistence.ts +0 -391
- package/src/offline/service-worker-updates.ts +0 -81
- package/src/offline/shared-computer-labels.ts +0 -8
- package/src/offline/shared-computer.tsx +0 -87
- package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
- package/src/offline/sign-out-pending-dialog.tsx +0 -135
- package/src/offline.ts +0 -37
- package/src/push-worker.ts +0 -25
- package/src/rhf/fields.tsx +0 -2187
- package/src/rhf/form-core.tsx +0 -324
- package/src/rhf/form.tsx +0 -12
- package/src/rhf/line-items.tsx +0 -135
- package/src/rhf/use-rhf-wizard-step.ts +0 -104
- package/src/rhf.ts +0 -21
- package/src/search/command-palette-labels.ts +0 -16
- package/src/search/command-palette.tsx +0 -624
- package/src/search/global-search-labels.ts +0 -13
- package/src/search/global-search.tsx +0 -660
- package/src/search/search-index.ts +0 -283
- package/src/search/use-command-key.ts +0 -23
- package/src/search.ts +0 -14
- package/src/settings/device-reset-setting-labels.ts +0 -20
- package/src/settings/device-reset-setting.tsx +0 -116
- package/src/settings/settings-card-classes.ts +0 -11
- package/src/settings/settings-catalogue.ts +0 -150
- package/src/settings/settings-context.ts +0 -33
- package/src/settings/settings-heading.tsx +0 -67
- package/src/settings/settings-labels.ts +0 -87
- package/src/settings/settings-layout.tsx +0 -471
- package/src/settings/settings-section.tsx +0 -108
- package/src/settings/use-account-appearance.ts +0 -148
- package/src/settings/use-account-language.ts +0 -146
- package/src/settings/use-settings-focus.ts +0 -128
- package/src/settings/use-settings-route.ts +0 -258
- package/src/shell/account-menu-language.tsx +0 -130
- package/src/shell/account-menu-text-size.tsx +0 -80
- package/src/shell/admin-menu.tsx +0 -183
- package/src/shell/app-shell-labels.ts +0 -11
- package/src/shell/app-shell.tsx +0 -1560
- package/src/shell/auth-layout.tsx +0 -186
- package/src/shell/company-switcher-labels.ts +0 -13
- package/src/shell/company-switcher.tsx +0 -136
- package/src/shell/dev-menu.tsx +0 -257
- package/src/shell/fill-height.tsx +0 -45
- package/src/shell/language-code.ts +0 -5
- package/src/shell/option-switcher-menu.tsx +0 -84
- package/src/shell/sync-status-indicator.tsx +0 -303
- package/src/shell/top-bar-actions.tsx +0 -87
- package/src/shell/top-bar-brand.tsx +0 -78
- package/src/shell/top-bar-parts-labels.ts +0 -148
- package/src/shell/top-bar.tsx +0 -40
- package/src/shell/topbar-action-menu.tsx +0 -567
- package/src/shell/topbar-controls.tsx +0 -269
- package/src/shell/tour-menu.tsx +0 -187
- package/src/shell.ts +0 -108
- package/src/sync/db.ts +0 -129
- package/src/sync/engine.ts +0 -1011
- package/src/sync/hooks.ts +0 -98
- package/src/sync/testing.ts +0 -73
- package/src/sync/wire.ts +0 -61
- package/src/sync.ts +0 -37
- package/src/table-text.ts +0 -8
- package/src/testing/css-variables.generated.ts +0 -112
- package/src/testing/css-variables.ts +0 -189
- package/src/testing/index.ts +0 -29
- package/src/testing/react.ts +0 -58
- package/src/testing/tour.ts +0 -41
- package/src/testing/viewport.ts +0 -357
- package/src/theme/chart-palette.ts +0 -232
- package/src/theme/color.ts +0 -272
- package/src/theme/contrast-tokens.ts +0 -268
- package/src/theme/contrast.ts +0 -250
- package/src/theme/palette-derive.ts +0 -825
- package/src/theme/palette-presets.ts +0 -544
- package/src/theme/palette-store.ts +0 -108
- package/src/theme/text-size.ts +0 -289
- package/src/theme/theme-store.ts +0 -157
- package/src/tour/tour-catalogue-labels.ts +0 -75
- package/src/tour/tour-catalogue.tsx +0 -335
- package/src/tour/tour-dock.ts +0 -134
- package/src/tour/tour-engine.ts +0 -37
- package/src/tour/tour-gates.ts +0 -218
- package/src/tour/tour-guide-button.tsx +0 -64
- package/src/tour/tour-guide-labels.ts +0 -15
- package/src/tour/tour-info.ts +0 -144
- package/src/tour/tour-labels.ts +0 -23
- package/src/tour/tour-overlay.tsx +0 -1170
- package/src/tour/tour.tsx +0 -750
- package/src/tour/use-tour-progress.ts +0 -671
- package/src/tour/use-tour-url-sync.ts +0 -266
- package/src/tour.ts +0 -21
- package/src/vite-env.d.ts +0 -23
- package/src/wizard/stepper-nav.tsx +0 -360
- package/src/wizard/types.ts +0 -275
- package/src/wizard/use-wizard.ts +0 -560
- package/src/wizard/validation.ts +0 -39
- package/src/wizard/wizard-context.tsx +0 -74
- package/src/wizard/wizard-labels.ts +0 -29
- package/src/wizard/wizard-step.tsx +0 -76
- package/src/wizard/wizard-summary.tsx +0 -84
- package/src/wizard.ts +0 -15
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type DataTableColumn, type DataTableProps } from "../components/data-table.js";
|
|
3
|
+
import type { CommitScope } from "../components/write-lock.js";
|
|
4
|
+
import type { AuthedFetcher } from "../hooks/use-authed-src.js";
|
|
5
|
+
import type { LabelOverride } from "../i18n/kit-labels.js";
|
|
6
|
+
import type { PathPattern } from "../landing/routing.js";
|
|
7
|
+
import type { FeedbackRecord } from "./feedback-record.js";
|
|
8
|
+
import { type FeedbackDetailPatch, type FeedbackRowDetailProps, type FeedbackUpload } from "./feedback-row-detail.js";
|
|
9
|
+
import { type FeedbackStatusChange } from "./feedback-status-undo.js";
|
|
10
|
+
import { type FeedbackSwipeBinding } from "./feedback-swipe.js";
|
|
11
|
+
import { type FeedbackPageLabels, type FeedbackRenderDate } from "./feedback-table.js";
|
|
12
|
+
import type { FeedbackStatus } from "./feedback-vocabulary.js";
|
|
13
|
+
/**
|
|
14
|
+
* The feedback inbox, ASSEMBLED ONCE (0.55; kastlan reuse audit 2026-11-02, proposal 2;
|
|
15
|
+
* docs/feedback-harmonization.md §13).
|
|
16
|
+
*
|
|
17
|
+
* Since 0.27 every part of `/feedback` and `/my-feedback` has been the kit's — the columns,
|
|
18
|
+
* the phone card and its day headers, the detail, the Undo, the swipes, the awaiting toggle
|
|
19
|
+
* (feedback-table.tsx, feedback-row-detail.tsx, feedback-swipe.tsx). What was not the kit's
|
|
20
|
+
* was the WIRING between them, and each app wrote the same ~150 lines of it:
|
|
21
|
+
* kastlan `frontend/src/features/feedback/pages/feedback-page.tsx:40-205`, keksdose
|
|
22
|
+
* `frontend/src/features/feedback/feedback-page.tsx:51-303`, Kurvenschmiede
|
|
23
|
+
* `frontend/src/features/feedback/feedback-page.tsx:88-245`. The open row in `?row=` with
|
|
24
|
+
* its numeric parse and its `rowHref`, `useFeedbackStatusUndo` over the page's PATCH, the
|
|
25
|
+
* `DataTable` with a dozen props, `FeedbackRowDetail` with a dozen more, the phone's
|
|
26
|
+
* grouping, card and swipe plan, the awaiting filter and its toggle. So every rename the
|
|
27
|
+
* parts took (0.52's `mutateAsync`, 0.53's removals) was made three times, and three
|
|
28
|
+
* times reviewed.
|
|
29
|
+
*
|
|
30
|
+
* Here that wiring is the kit's: {@link useFeedbackInbox} holds it and returns what the
|
|
31
|
+
* table, the detail and the phone cards need; {@link FeedbackInbox} draws the page from
|
|
32
|
+
* it. What stays the app's is what differs between the three, each a slot: where the rows
|
|
33
|
+
* come from and the three mutations (its API client, TanStack Query in all three — the kit
|
|
34
|
+
* sends no request), who may triage, the votes, the public switch, the comments (the app's
|
|
35
|
+
* `FeedbackCommentThread` over its own queries), its own columns, its swipe binding, its
|
|
36
|
+
* date writer, the page's header and frame, and whatever else `DataTable` takes.
|
|
37
|
+
*
|
|
38
|
+
* Additive: every part it is built from stays exported and unchanged, for a page that
|
|
39
|
+
* wants its own layout — call the hook and spread `inbox.table` into a `DataTable`.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* One of the page's mutations, as the inbox calls it: **TanStack Query's `useMutation`
|
|
43
|
+
* result, passed as it is** — `update={update}`. The inbox reads `mutateAsync` (whose
|
|
44
|
+
* promise settles for every call, refactor audit 2026-10-30 FLW-3) and `isPending`, and
|
|
45
|
+
* nothing else. Any object with those two fits, so a page on another data layer hands
|
|
46
|
+
* `{ mutateAsync: api.update, isPending }`.
|
|
47
|
+
*
|
|
48
|
+
* Taking the mutation rather than a callback per control is the point of the round: how
|
|
49
|
+
* the parts call it (0.52 moved them from `mutate` to `mutateAsync`) is then the kit's to
|
|
50
|
+
* change, once. Failure is the mutation's own: its `onError` toasts the server's sentence
|
|
51
|
+
* (§4.4), and the inbox catches the rejection.
|
|
52
|
+
*/
|
|
53
|
+
export interface FeedbackInboxMutation<V> {
|
|
54
|
+
mutateAsync: (variables: V) => Promise<unknown>;
|
|
55
|
+
/** In flight: the editors' and the composer's buttons wait for it. */
|
|
56
|
+
isPending?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/** What the reply mutation is sent: `POST /feedback/{id}/replies` (§9.7, §12) — the
|
|
59
|
+
* body, the status picked (absent: "No change") and the uploaded file's URL (absent:
|
|
60
|
+
* none). Keys that carry nothing are left out, so a `mutationFn` may spread them onto
|
|
61
|
+
* the wire. kastlan's `useFeedbackReply` takes exactly this. */
|
|
62
|
+
export interface FeedbackReplyPost {
|
|
63
|
+
id: number;
|
|
64
|
+
body: string;
|
|
65
|
+
status?: FeedbackStatus;
|
|
66
|
+
attachmentUrl?: string;
|
|
67
|
+
}
|
|
68
|
+
/** What the rework mutation is sent: `POST /feedback/{id}/rework` (§10.3) — the author's
|
|
69
|
+
* trimmed note and the uploaded file's URL (absent: none). kastlan's `useFeedbackRework`
|
|
70
|
+
* takes exactly this. */
|
|
71
|
+
export interface FeedbackReworkPost {
|
|
72
|
+
id: number;
|
|
73
|
+
note: string;
|
|
74
|
+
attachmentUrl?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface UseFeedbackInboxOptions<T extends FeedbackRecord> {
|
|
77
|
+
/** The list as the app's query answered it (`GET /feedback`, `GET /feedback/my`) —
|
|
78
|
+
* `list.data ?? []`. Shown newest first and, behind the awaiting toggle, narrowed. */
|
|
79
|
+
rows: readonly T[];
|
|
80
|
+
/** `/my-feedback`: the viewer's own reports — no triage there, for an admin too (§4.3). */
|
|
81
|
+
mine?: boolean;
|
|
82
|
+
/** The viewer may triage: the admin. Only on the inbox — `mine` wins — so `isAdmin`
|
|
83
|
+
* is enough. The status cell, the pills, the reply composer, the swipes, the submitter
|
|
84
|
+
* on the card and the awaiting toggle. */
|
|
85
|
+
canEdit: boolean;
|
|
86
|
+
/** The signed-in user's id: who the AUTHOR is (the description editor, Rework, the
|
|
87
|
+
* public switch's window). */
|
|
88
|
+
viewerId?: number | null;
|
|
89
|
+
/**
|
|
90
|
+
* `PATCH /feedback/{id}` (§3.4): every edit of the report itself — a status change from
|
|
91
|
+
* the cell, the pills or a swipe (with the Undo, `useFeedbackStatusUndo`), the
|
|
92
|
+
* description, the legacy rework append and, with {@link publicSwitch}, the flip. ONE
|
|
93
|
+
* mutation for all of them, the page's, so its observer outlives a row a status filter
|
|
94
|
+
* removes (§4.5).
|
|
95
|
+
*/
|
|
96
|
+
update: FeedbackInboxMutation<FeedbackDetailPatch>;
|
|
97
|
+
/** `POST /feedback/{id}/replies` — the admin's answer (§9.7) with its one file (§12).
|
|
98
|
+
* Without it no composer is drawn. */
|
|
99
|
+
reply?: FeedbackInboxMutation<FeedbackReplyPost>;
|
|
100
|
+
/** `POST /feedback/{id}/rework` — the author's rework entry (§10.3). Without it a rework
|
|
101
|
+
* goes through {@link update} as the legacy body append (§3.4). */
|
|
102
|
+
rework?: FeedbackInboxMutation<FeedbackReworkPost>;
|
|
103
|
+
/**
|
|
104
|
+
* The **Visible to others** switch in the detail (§9.3), its flip sent through
|
|
105
|
+
* {@link update} as `{ id, public }` — as keksdose and Kurvenschmiede send it. Off by
|
|
106
|
+
* default: an app without public parts (kastlan) has no switch.
|
|
107
|
+
*/
|
|
108
|
+
publicSwitch?: boolean;
|
|
109
|
+
/** The **Votes** column and the card's count (§9.6). Off by default (kastlan); keksdose
|
|
110
|
+
* always, Kurvenschmiede for a reader of the public parts. */
|
|
111
|
+
votes?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* The report's comments, in a section of their own under the replies (§9.8): the app's
|
|
114
|
+
* `FeedbackCommentThread` over its own queries — a component of its own, since its
|
|
115
|
+
* queries are hooks. Return `null` for a row without a thread (a CRASH, a reader who
|
|
116
|
+
* does not see the public parts; `false` is read as `null`). Called only for the open
|
|
117
|
+
* row.
|
|
118
|
+
*
|
|
119
|
+
* ```tsx
|
|
120
|
+
* comments={(row) => <RowComments id={row.id} isAdmin={isAdmin} canComment={!canEdit} />}
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
comments?: (row: T) => ReactNode;
|
|
124
|
+
/** The detail as drawn, to wrap or extend — kastlan's tour anchor:
|
|
125
|
+
* `(row, detail) => <div data-tour="feedback-row-detail">{detail}</div>`. */
|
|
126
|
+
renderDetail?: (row: T, detail: ReactNode) => ReactNode;
|
|
127
|
+
/**
|
|
128
|
+
* The app's own columns over the kit's (§4.3's nine, and Votes): add, drop, reorder —
|
|
129
|
+
* the keys are stable (`columns.filter((c) => c.key !== "email")`). A column brings its
|
|
130
|
+
* filter with it. Keep it stable (module scope or `useCallback`): it is a memo's
|
|
131
|
+
* dependency.
|
|
132
|
+
*/
|
|
133
|
+
columns?: (columns: readonly DataTableColumn<T>[]) => readonly DataTableColumn<T>[];
|
|
134
|
+
/** Which swipe does what on the phone (§4.5) — keksdose's Settings → Interaction binding,
|
|
135
|
+
* `{ end: prefs.right, start: prefs.left }`. Default `DEFAULT_FEEDBACK_SWIPE`. */
|
|
136
|
+
swipe?: FeedbackSwipeBinding;
|
|
137
|
+
/** The status writes the TABLE offers — the status cell and the desktop's actions
|
|
138
|
+
* column — commit: under a locked `WriteLockProvider` they stay and say why
|
|
139
|
+
* (`FeedbackStatusTransitions`' `commit`). Off by default. The detail's own saves:
|
|
140
|
+
* `detail={{ commit }}`. */
|
|
141
|
+
commit?: CommitScope;
|
|
142
|
+
/** `POST /feedback/attachments` (§3.3): the rework's file and the reply's. */
|
|
143
|
+
onUpload?: FeedbackUpload;
|
|
144
|
+
/** The app's authenticated GET for the pictures and the downloads (§3.5). */
|
|
145
|
+
fetcher?: AuthedFetcher | null;
|
|
146
|
+
/** "Capture screenshot" beside the rework's and the reply's file field. Default: the
|
|
147
|
+
* kit's `captureAppScreenshot()`; `null` for no button. */
|
|
148
|
+
onCaptureScreenshot?: (() => Promise<File | null>) | null;
|
|
149
|
+
/**
|
|
150
|
+
* The app's date writer (keksdose's `useDateCell()`, the date-format setting): the date
|
|
151
|
+
* and resolved columns, the card, the phone's day headers AND the detail's "Resolved:",
|
|
152
|
+
* so all five say the same. Left out, the kit's date with the weekday in the table and
|
|
153
|
+
* on the phone, and in the detail its sentence date — as the parts draw them alone.
|
|
154
|
+
*/
|
|
155
|
+
renderDate?: FeedbackRenderDate;
|
|
156
|
+
/** The app's one-shot routes, on top of the kit's (0.52, FLW-7) — for the URL column and
|
|
157
|
+
* the detail's URL section alike. Keep it stable (module scope). */
|
|
158
|
+
pageLinkExclude?: readonly PathPattern[];
|
|
159
|
+
/** The list is still loading (`list.isPending`): the table says "Loading…" where its
|
|
160
|
+
* rows will be, not "None". */
|
|
161
|
+
loading?: boolean;
|
|
162
|
+
/** Over the detail's props the inbox sets — `commit`, `showSubject`,
|
|
163
|
+
* `reworkAttachment`, `replyAttachments` (on here), `labels`, `className`. Merged last,
|
|
164
|
+
* so it wins. */
|
|
165
|
+
detail?: Partial<Omit<FeedbackRowDetailProps, "row">>;
|
|
166
|
+
/** Over the `feedbackPage` namespace: the columns, the card, the empty state, the toggle
|
|
167
|
+
* and the title. Keep it stable: it is a memo's dependency. */
|
|
168
|
+
labels?: LabelOverride<FeedbackPageLabels>;
|
|
169
|
+
}
|
|
170
|
+
/** What {@link useFeedbackInbox} returns: the state, and the props of the parts. */
|
|
171
|
+
export interface FeedbackInboxState<T extends FeedbackRecord> {
|
|
172
|
+
/** `canEdit && !mine`: the admin on the inbox. */
|
|
173
|
+
editable: boolean;
|
|
174
|
+
/** The page's title, `feedbackPage.title` — or `.myTitle` on `/my-feedback`. */
|
|
175
|
+
title: string;
|
|
176
|
+
/** The open row's id, from `?row=`; `null` when none is open or the param named no
|
|
177
|
+
* id (`?row=abc`, `?row=1.5`, `?row=0`). */
|
|
178
|
+
openId: number | null;
|
|
179
|
+
/** Open a row by its id, or close it with `null`: rewrites `?row=`, replacing the
|
|
180
|
+
* history entry rather than stacking one per report looked at. */
|
|
181
|
+
setOpenId: (id: number | null) => void;
|
|
182
|
+
/** A row's own address: this view — the table's page, sort and filters — with the row
|
|
183
|
+
* open. The row's link (a new tab, a copied address); a plain click opens it in place. */
|
|
184
|
+
rowHref: (row: T) => string;
|
|
185
|
+
/** The undoable status change (§4.5): the status cell's, the pills', the swipes'. */
|
|
186
|
+
changeStatus: FeedbackStatusChange;
|
|
187
|
+
/** The phone's "Only what is waiting for you" — page state, not the URL (§4.3). */
|
|
188
|
+
awaiting: boolean;
|
|
189
|
+
setAwaiting: (next: boolean) => void;
|
|
190
|
+
/** The one date writer the columns, the card and the day headers use. */
|
|
191
|
+
renderDate: FeedbackRenderDate;
|
|
192
|
+
/** `FeedbackRowDetail`'s props for a row, slots included. */
|
|
193
|
+
detailProps: (row: T) => FeedbackRowDetailProps;
|
|
194
|
+
/** `DataTable`'s props, ready to spread: the rows (newest first, narrowed by
|
|
195
|
+
* `awaiting`), the columns, the open row, the detail, the phone's card, day headers and
|
|
196
|
+
* swipes, the desktop's actions column, the empty state, `urlSync`,
|
|
197
|
+
* `storageKey="feedback"`, `fillHeight`. */
|
|
198
|
+
table: DataTableProps<T>;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The inbox's wiring, once (§13): from the app's rows and mutations to what the table,
|
|
202
|
+
* the detail and the phone cards take.
|
|
203
|
+
*
|
|
204
|
+
* ```tsx
|
|
205
|
+
* const list = useQuery({ queryKey: keys.feedback.all, queryFn: api.listFeedback });
|
|
206
|
+
* const update = useMutation({ mutationFn: ({ id, ...patch }: FeedbackDetailPatch) => api.update(id, patch), … });
|
|
207
|
+
* const inbox = useFeedbackInbox({ rows: list.data ?? [], canEdit: isAdmin, viewerId: user?.id, update });
|
|
208
|
+
* <DataTable {...inbox.table} edgeFade />
|
|
209
|
+
* ```
|
|
210
|
+
*
|
|
211
|
+
* {@link FeedbackInbox} is this hook drawn — use the hook alone for a layout of your own.
|
|
212
|
+
* Needs a react-router Router (`?row=`, `urlSync`).
|
|
213
|
+
*/
|
|
214
|
+
export declare function useFeedbackInbox<T extends FeedbackRecord>({ rows, mine, canEdit, viewerId, update, reply, rework, publicSwitch, votes, comments, renderDetail, columns: ownColumns, swipe, commit, onUpload, fetcher, onCaptureScreenshot, renderDate: renderDateProp, pageLinkExclude, loading, detail, labels, }: UseFeedbackInboxOptions<T>): FeedbackInboxState<T>;
|
|
215
|
+
export interface FeedbackInboxProps<T extends FeedbackRecord> extends UseFeedbackInboxOptions<T> {
|
|
216
|
+
/**
|
|
217
|
+
* Above the table. Default: `<PageHeader title={title} className="mb-4 md:shrink-0" />`.
|
|
218
|
+
* A function gets the page's title (`feedbackPage.title` / `.myTitle`) — the app's own
|
|
219
|
+
* header: `(title) => <PageHeader size="compact" title={title} data-tour="feedback-header" />`.
|
|
220
|
+
* `null` for none.
|
|
221
|
+
*/
|
|
222
|
+
header?: ReactNode | ((title: string) => ReactNode);
|
|
223
|
+
/** Over the inbox's `DataTable` props — `edgeFade`, `locale`, `mobileExpandAsDialog`,
|
|
224
|
+
* `className`, another `storageKey`. Merged last, so it wins. */
|
|
225
|
+
table?: Partial<DataTableProps<T>>;
|
|
226
|
+
/** Further `FloatingAction`s on the phone: after the awaiting toggle on the inbox, in a
|
|
227
|
+
* group of their own on `/my-feedback`. */
|
|
228
|
+
phoneActions?: ReactNode;
|
|
229
|
+
/** On the page's frame — the fill-height column (`FILL_HEIGHT_CLASS`); the app's page
|
|
230
|
+
* padding goes here. */
|
|
231
|
+
className?: string;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The feedback inbox (§13): `/feedback` and `/my-feedback` drawn whole from
|
|
235
|
+
* {@link useFeedbackInbox} — the header, the table with its detail, phone cards and swipes,
|
|
236
|
+
* and the phone's awaiting toggle, in the fill-height frame (page layout §1.3) — so a page
|
|
237
|
+
* is its queries and this:
|
|
238
|
+
*
|
|
239
|
+
* ```tsx
|
|
240
|
+
* export function FeedbackPage({ mine = false }: { mine?: boolean }) {
|
|
241
|
+
* const { user, isAdmin } = useAuth();
|
|
242
|
+
* const list = useQuery({ queryKey: mine ? keys.mine : keys.all, queryFn: mine ? api.mine : api.all });
|
|
243
|
+
* const update = useFeedbackUpdate();
|
|
244
|
+
* const reply = useFeedbackReply();
|
|
245
|
+
* const rework = useFeedbackRework();
|
|
246
|
+
* if (!mine && !isAdmin) return <Navigate to="/my-feedback" replace />;
|
|
247
|
+
* return (
|
|
248
|
+
* <FeedbackInbox
|
|
249
|
+
* rows={list.data ?? []} loading={list.isPending} mine={mine} canEdit={isAdmin} viewerId={user?.id}
|
|
250
|
+
* update={update} reply={reply} rework={rework}
|
|
251
|
+
* onUpload={api.upload} fetcher={api.file}
|
|
252
|
+
* />
|
|
253
|
+
* );
|
|
254
|
+
* }
|
|
255
|
+
* ```
|
|
256
|
+
*
|
|
257
|
+
* Every slot is {@link UseFeedbackInboxOptions}' or one of the four below. The redirect of
|
|
258
|
+
* a non-admin, the routes and the queries stay the app's.
|
|
259
|
+
*/
|
|
260
|
+
export declare function FeedbackInbox<T extends FeedbackRecord>({ header, table, phoneActions, className, ...options }: FeedbackInboxProps<T>): import("react").JSX.Element;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { useHref, useLocation } from "react-router";
|
|
5
|
+
import { cn } from "../lib/cn.js";
|
|
6
|
+
import { DataTable } from "../components/data-table.js";
|
|
7
|
+
import { FloatingActionGroup } from "../components/floating-panel.js";
|
|
8
|
+
import { LoadingState } from "../components/loading-state.js";
|
|
9
|
+
import { PageHeader } from "../components/page-header.js";
|
|
10
|
+
import { useSearchParamState } from "../hooks/use-search-param-state.js";
|
|
11
|
+
import { FILL_HEIGHT_CLASS } from "../shell/fill-height.js";
|
|
12
|
+
import { captureAppScreenshot } from "./feedback-capture.js";
|
|
13
|
+
import { useFeedbackStatusLabels } from "./feedback-labels.js";
|
|
14
|
+
import { runMutation } from "./feedback-mutation.js";
|
|
15
|
+
import {
|
|
16
|
+
FeedbackRowDetail
|
|
17
|
+
} from "./feedback-row-detail.js";
|
|
18
|
+
import { useFeedbackStatusUndo } from "./feedback-status-undo.js";
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_FEEDBACK_SWIPE,
|
|
21
|
+
feedbackSwipeOmitShown,
|
|
22
|
+
feedbackSwipePlan
|
|
23
|
+
} from "./feedback-swipe.js";
|
|
24
|
+
import {
|
|
25
|
+
FeedbackAwaitingToggle,
|
|
26
|
+
FeedbackEmptyState,
|
|
27
|
+
FeedbackMobileCard,
|
|
28
|
+
feedbackMobileGroupBy,
|
|
29
|
+
isFeedbackAwaiting,
|
|
30
|
+
useFeedbackColumns,
|
|
31
|
+
useFeedbackPageLabels,
|
|
32
|
+
useFeedbackRenderDate
|
|
33
|
+
} from "./feedback-table.js";
|
|
34
|
+
function parseRowId(raw) {
|
|
35
|
+
if (!/^[1-9]\d*$/.test(raw)) return void 0;
|
|
36
|
+
const id = Number(raw);
|
|
37
|
+
return Number.isSafeInteger(id) ? id : void 0;
|
|
38
|
+
}
|
|
39
|
+
const ROW_PARAM = { parse: parseRowId, history: "replace" };
|
|
40
|
+
const newestFirst = (a, b) => a.created_at < b.created_at ? 1 : a.created_at > b.created_at ? -1 : 0;
|
|
41
|
+
const rowId = (row) => row.id;
|
|
42
|
+
const rowTitle = (row) => row.title;
|
|
43
|
+
const captureScreenshot = () => captureAppScreenshot();
|
|
44
|
+
const ignore = () => {
|
|
45
|
+
};
|
|
46
|
+
const threadOf = (node) => node === false || node === "" ? void 0 : node;
|
|
47
|
+
function useFeedbackInbox({
|
|
48
|
+
rows,
|
|
49
|
+
mine = false,
|
|
50
|
+
canEdit,
|
|
51
|
+
viewerId,
|
|
52
|
+
update,
|
|
53
|
+
reply,
|
|
54
|
+
rework,
|
|
55
|
+
publicSwitch = false,
|
|
56
|
+
votes = false,
|
|
57
|
+
comments,
|
|
58
|
+
renderDetail,
|
|
59
|
+
columns: ownColumns,
|
|
60
|
+
swipe = DEFAULT_FEEDBACK_SWIPE,
|
|
61
|
+
commit,
|
|
62
|
+
onUpload,
|
|
63
|
+
fetcher,
|
|
64
|
+
onCaptureScreenshot = captureScreenshot,
|
|
65
|
+
renderDate: renderDateProp,
|
|
66
|
+
pageLinkExclude,
|
|
67
|
+
loading = false,
|
|
68
|
+
detail,
|
|
69
|
+
labels
|
|
70
|
+
}) {
|
|
71
|
+
const editable = canEdit && !mine;
|
|
72
|
+
const pageLabels = useFeedbackPageLabels(labels);
|
|
73
|
+
const statusLabels = useFeedbackStatusLabels();
|
|
74
|
+
const renderDate = useFeedbackRenderDate(renderDateProp);
|
|
75
|
+
const updateAsync = update.mutateAsync;
|
|
76
|
+
const changeStatus = useFeedbackStatusUndo(updateAsync);
|
|
77
|
+
const [openId, setOpenId] = useSearchParamState("row", null, ROW_PARAM);
|
|
78
|
+
const { pathname, search } = useLocation();
|
|
79
|
+
const here = useHref({ pathname });
|
|
80
|
+
const rowHref = useCallback(
|
|
81
|
+
(row) => {
|
|
82
|
+
const params = new URLSearchParams(search);
|
|
83
|
+
params.set("row", String(row.id));
|
|
84
|
+
return `${here}?${params.toString()}`;
|
|
85
|
+
},
|
|
86
|
+
[here, search]
|
|
87
|
+
);
|
|
88
|
+
const [awaiting, setAwaiting] = useState(false);
|
|
89
|
+
const shown = useMemo(
|
|
90
|
+
() => (editable && awaiting ? rows.filter((row) => isFeedbackAwaiting(row.status)) : [...rows]).sort(newestFirst),
|
|
91
|
+
[rows, editable, awaiting]
|
|
92
|
+
);
|
|
93
|
+
const kitColumns = useFeedbackColumns({
|
|
94
|
+
mine,
|
|
95
|
+
canEdit,
|
|
96
|
+
onStatus: changeStatus,
|
|
97
|
+
renderDate,
|
|
98
|
+
commit,
|
|
99
|
+
votes,
|
|
100
|
+
pageLinkExclude,
|
|
101
|
+
labels
|
|
102
|
+
});
|
|
103
|
+
const columns = useMemo(() => ownColumns ? ownColumns(kitColumns) : kitColumns, [ownColumns, kitColumns]);
|
|
104
|
+
const mobileGroupBy = useMemo(() => feedbackMobileGroupBy(renderDate), [renderDate]);
|
|
105
|
+
const pending = Boolean(update.isPending || rework?.isPending);
|
|
106
|
+
const capture = onCaptureScreenshot ?? void 0;
|
|
107
|
+
const detailProps = (row) => ({
|
|
108
|
+
row,
|
|
109
|
+
canEdit: editable,
|
|
110
|
+
viewerId,
|
|
111
|
+
onUpdate: updateAsync,
|
|
112
|
+
pending,
|
|
113
|
+
// The page's change, not one the detail would build: the same Undo as the cell's.
|
|
114
|
+
statusChange: changeStatus,
|
|
115
|
+
onUpload,
|
|
116
|
+
fetcher,
|
|
117
|
+
onCaptureScreenshot: capture,
|
|
118
|
+
// Only the app's own: left out, the detail's "Resolved:" is the kit's sentence date,
|
|
119
|
+
// no weekday — as kastlan's has always been.
|
|
120
|
+
renderDate: renderDateProp,
|
|
121
|
+
pageLinkExclude,
|
|
122
|
+
onReply: reply ? (body, status, attachmentUrl) => reply.mutateAsync({
|
|
123
|
+
id: row.id,
|
|
124
|
+
body,
|
|
125
|
+
...status ? { status } : {},
|
|
126
|
+
...attachmentUrl ? { attachmentUrl } : {}
|
|
127
|
+
}) : void 0,
|
|
128
|
+
// The reply's one file (§12): all three apps are on server-kit 0.18, and the reply
|
|
129
|
+
// mutation's `attachmentUrl` is in its type from the start (the B1 risk the detail's
|
|
130
|
+
// default guards against — an `onReply` that drops the file — cannot arise here).
|
|
131
|
+
replyAttachments: true,
|
|
132
|
+
replying: reply?.isPending ?? false,
|
|
133
|
+
onRework: rework ? (note, attachmentUrl) => rework.mutateAsync({ id: row.id, note, ...attachmentUrl ? { attachmentUrl } : {} }) : void 0,
|
|
134
|
+
comments: threadOf(comments?.(row)),
|
|
135
|
+
onPublicChange: publicSwitch ? (next) => runMutation(updateAsync, { id: row.id, public: next }, ignore) : void 0,
|
|
136
|
+
...detail
|
|
137
|
+
});
|
|
138
|
+
const table = {
|
|
139
|
+
rows: shown,
|
|
140
|
+
columns,
|
|
141
|
+
rowKey: rowId,
|
|
142
|
+
storageKey: "feedback",
|
|
143
|
+
urlSync: true,
|
|
144
|
+
fillHeight: true,
|
|
145
|
+
rowHref,
|
|
146
|
+
// The link covers the row's first cell; a click anywhere on the row opens it — or,
|
|
147
|
+
// on the open one, closes it.
|
|
148
|
+
onRowClick: (row) => setOpenId((id) => id === row.id ? null : row.id),
|
|
149
|
+
isExpanded: (row) => row.id === openId,
|
|
150
|
+
expandedRow: (row) => {
|
|
151
|
+
const node = /* @__PURE__ */ jsx(FeedbackRowDetail, { ...detailProps(row) });
|
|
152
|
+
return renderDetail ? renderDetail(row, node) : node;
|
|
153
|
+
},
|
|
154
|
+
// §7.13: one word, also when a filter or the toggle leaves nothing; and while the
|
|
155
|
+
// first answer is out, what is happening instead.
|
|
156
|
+
empty: loading ? /* @__PURE__ */ jsx(LoadingState, { size: "sm", inline: true }) : /* @__PURE__ */ jsx(FeedbackEmptyState, { labels }),
|
|
157
|
+
mobileGroupBy,
|
|
158
|
+
mobileCard: (row) => /* @__PURE__ */ jsx(FeedbackMobileCard, { row, showSubmitter: editable, renderDate, votes, labels }),
|
|
159
|
+
// The admin inbox only — /my-feedback is read-only by design (keksdose #331) — and
|
|
160
|
+
// every swipe through the same Undo as a tap (§4.5, §7.10).
|
|
161
|
+
mobileSwipeActions: editable ? (row) => feedbackSwipePlan(swipe, row, { change: changeStatus, labels: statusLabels }) : void 0,
|
|
162
|
+
// The swipes as the desktop's last column (0.46, keksdose dev#598), less what the
|
|
163
|
+
// status cell already offers.
|
|
164
|
+
desktopSwipeActions: { omit: feedbackSwipeOmitShown, name: rowTitle, ...commit === void 0 ? {} : { commit } }
|
|
165
|
+
};
|
|
166
|
+
return {
|
|
167
|
+
editable,
|
|
168
|
+
title: mine ? pageLabels.myTitle : pageLabels.title,
|
|
169
|
+
openId,
|
|
170
|
+
setOpenId,
|
|
171
|
+
rowHref,
|
|
172
|
+
changeStatus,
|
|
173
|
+
awaiting,
|
|
174
|
+
setAwaiting,
|
|
175
|
+
renderDate,
|
|
176
|
+
detailProps,
|
|
177
|
+
table
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function FeedbackInbox({
|
|
181
|
+
header,
|
|
182
|
+
table,
|
|
183
|
+
phoneActions,
|
|
184
|
+
className,
|
|
185
|
+
...options
|
|
186
|
+
}) {
|
|
187
|
+
const inbox = useFeedbackInbox(options);
|
|
188
|
+
const pageLabels = useFeedbackPageLabels(options.labels);
|
|
189
|
+
const head = header === void 0 ? /* @__PURE__ */ jsx(PageHeader, { title: inbox.title, className: "mb-4 md:shrink-0" }) : typeof header === "function" ? header(inbox.title) : header;
|
|
190
|
+
return (
|
|
191
|
+
// A plain element on the way to the table carries the fill (page layout §1.3).
|
|
192
|
+
/* @__PURE__ */ jsxs("div", { className: cn(FILL_HEIGHT_CLASS, className), "aria-busy": options.loading || void 0, "data-feedback-inbox": "", children: [
|
|
193
|
+
head,
|
|
194
|
+
/* @__PURE__ */ jsx(DataTable, { ...inbox.table, ...table }),
|
|
195
|
+
inbox.editable ? (
|
|
196
|
+
// The phone's floating action (keksdose feedback #58): only on the inbox — on
|
|
197
|
+
// /my-feedback the author is not waiting on their own verdict.
|
|
198
|
+
/* @__PURE__ */ jsx(FeedbackAwaitingToggle, { pressed: inbox.awaiting, onPressedChange: inbox.setAwaiting, labels: options.labels, children: phoneActions })
|
|
199
|
+
) : phoneActions && /* @__PURE__ */ jsx(FloatingActionGroup, { "aria-label": pageLabels.phoneActions, className: "md:hidden", children: phoneActions })
|
|
200
|
+
] })
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
export {
|
|
204
|
+
FeedbackInbox,
|
|
205
|
+
useFeedbackInbox
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=feedback-inbox-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/feedback/feedback-inbox-page.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { useHref, useLocation } from \"react-router\";\nimport { cn } from \"../lib/cn\";\nimport { DataTable, type DataTableColumn, type DataTableProps } from \"../components/data-table\";\nimport { FloatingActionGroup } from \"../components/floating-panel\";\nimport { LoadingState } from \"../components/loading-state\";\nimport { PageHeader } from \"../components/page-header\";\nimport type { CommitScope } from \"../components/write-lock\";\nimport type { AuthedFetcher } from \"../hooks/use-authed-src\";\nimport { useSearchParamState } from \"../hooks/use-search-param-state\";\nimport type { LabelOverride } from \"../i18n/kit-labels\";\nimport type { PathPattern } from \"../landing/routing\";\nimport { FILL_HEIGHT_CLASS } from \"../shell/fill-height\";\nimport { captureAppScreenshot } from \"./feedback-capture\";\nimport { useFeedbackStatusLabels } from \"./feedback-labels\";\nimport { runMutation } from \"./feedback-mutation\";\nimport type { FeedbackRecord } from \"./feedback-record\";\nimport {\n FeedbackRowDetail,\n type FeedbackDetailPatch,\n type FeedbackRowDetailProps,\n type FeedbackUpload,\n} from \"./feedback-row-detail\";\nimport { useFeedbackStatusUndo, type FeedbackStatusChange } from \"./feedback-status-undo\";\nimport {\n DEFAULT_FEEDBACK_SWIPE,\n feedbackSwipeOmitShown,\n feedbackSwipePlan,\n type FeedbackSwipeBinding,\n} from \"./feedback-swipe\";\nimport {\n FeedbackAwaitingToggle,\n FeedbackEmptyState,\n FeedbackMobileCard,\n feedbackMobileGroupBy,\n isFeedbackAwaiting,\n useFeedbackColumns,\n useFeedbackPageLabels,\n useFeedbackRenderDate,\n type FeedbackPageLabels,\n type FeedbackRenderDate,\n} from \"./feedback-table\";\nimport type { FeedbackStatus } from \"./feedback-vocabulary\";\n\n/**\n * The feedback inbox, ASSEMBLED ONCE (0.55; kastlan reuse audit 2026-11-02, proposal 2;\n * docs/feedback-harmonization.md §13).\n *\n * Since 0.27 every part of `/feedback` and `/my-feedback` has been the kit's — the columns,\n * the phone card and its day headers, the detail, the Undo, the swipes, the awaiting toggle\n * (feedback-table.tsx, feedback-row-detail.tsx, feedback-swipe.tsx). What was not the kit's\n * was the WIRING between them, and each app wrote the same ~150 lines of it:\n * kastlan `frontend/src/features/feedback/pages/feedback-page.tsx:40-205`, keksdose\n * `frontend/src/features/feedback/feedback-page.tsx:51-303`, Kurvenschmiede\n * `frontend/src/features/feedback/feedback-page.tsx:88-245`. The open row in `?row=` with\n * its numeric parse and its `rowHref`, `useFeedbackStatusUndo` over the page's PATCH, the\n * `DataTable` with a dozen props, `FeedbackRowDetail` with a dozen more, the phone's\n * grouping, card and swipe plan, the awaiting filter and its toggle. So every rename the\n * parts took (0.52's `mutateAsync`, 0.53's removals) was made three times, and three\n * times reviewed.\n *\n * Here that wiring is the kit's: {@link useFeedbackInbox} holds it and returns what the\n * table, the detail and the phone cards need; {@link FeedbackInbox} draws the page from\n * it. What stays the app's is what differs between the three, each a slot: where the rows\n * come from and the three mutations (its API client, TanStack Query in all three — the kit\n * sends no request), who may triage, the votes, the public switch, the comments (the app's\n * `FeedbackCommentThread` over its own queries), its own columns, its swipe binding, its\n * date writer, the page's header and frame, and whatever else `DataTable` takes.\n *\n * Additive: every part it is built from stays exported and unchanged, for a page that\n * wants its own layout — call the hook and spread `inbox.table` into a `DataTable`.\n */\n\n/* ── The mutations ──────────────────────────────────────────────────────────── */\n\n/**\n * One of the page's mutations, as the inbox calls it: **TanStack Query's `useMutation`\n * result, passed as it is** — `update={update}`. The inbox reads `mutateAsync` (whose\n * promise settles for every call, refactor audit 2026-10-30 FLW-3) and `isPending`, and\n * nothing else. Any object with those two fits, so a page on another data layer hands\n * `{ mutateAsync: api.update, isPending }`.\n *\n * Taking the mutation rather than a callback per control is the point of the round: how\n * the parts call it (0.52 moved them from `mutate` to `mutateAsync`) is then the kit's to\n * change, once. Failure is the mutation's own: its `onError` toasts the server's sentence\n * (§4.4), and the inbox catches the rejection.\n */\nexport interface FeedbackInboxMutation<V> {\n mutateAsync: (variables: V) => Promise<unknown>;\n /** In flight: the editors' and the composer's buttons wait for it. */\n isPending?: boolean;\n}\n\n/** What the reply mutation is sent: `POST /feedback/{id}/replies` (§9.7, §12) — the\n * body, the status picked (absent: \"No change\") and the uploaded file's URL (absent:\n * none). Keys that carry nothing are left out, so a `mutationFn` may spread them onto\n * the wire. kastlan's `useFeedbackReply` takes exactly this. */\nexport interface FeedbackReplyPost {\n id: number;\n body: string;\n status?: FeedbackStatus;\n attachmentUrl?: string;\n}\n\n/** What the rework mutation is sent: `POST /feedback/{id}/rework` (§10.3) — the author's\n * trimmed note and the uploaded file's URL (absent: none). kastlan's `useFeedbackRework`\n * takes exactly this. */\nexport interface FeedbackReworkPost {\n id: number;\n note: string;\n attachmentUrl?: string;\n}\n\n/* ── The hook ───────────────────────────────────────────────────────────────── */\n\nexport interface UseFeedbackInboxOptions<T extends FeedbackRecord> {\n /** The list as the app's query answered it (`GET /feedback`, `GET /feedback/my`) —\n * `list.data ?? []`. Shown newest first and, behind the awaiting toggle, narrowed. */\n rows: readonly T[];\n /** `/my-feedback`: the viewer's own reports — no triage there, for an admin too (§4.3). */\n mine?: boolean;\n /** The viewer may triage: the admin. Only on the inbox — `mine` wins — so `isAdmin`\n * is enough. The status cell, the pills, the reply composer, the swipes, the submitter\n * on the card and the awaiting toggle. */\n canEdit: boolean;\n /** The signed-in user's id: who the AUTHOR is (the description editor, Rework, the\n * public switch's window). */\n viewerId?: number | null;\n /**\n * `PATCH /feedback/{id}` (§3.4): every edit of the report itself — a status change from\n * the cell, the pills or a swipe (with the Undo, `useFeedbackStatusUndo`), the\n * description, the legacy rework append and, with {@link publicSwitch}, the flip. ONE\n * mutation for all of them, the page's, so its observer outlives a row a status filter\n * removes (§4.5).\n */\n update: FeedbackInboxMutation<FeedbackDetailPatch>;\n /** `POST /feedback/{id}/replies` — the admin's answer (§9.7) with its one file (§12).\n * Without it no composer is drawn. */\n reply?: FeedbackInboxMutation<FeedbackReplyPost>;\n /** `POST /feedback/{id}/rework` — the author's rework entry (§10.3). Without it a rework\n * goes through {@link update} as the legacy body append (§3.4). */\n rework?: FeedbackInboxMutation<FeedbackReworkPost>;\n /**\n * The **Visible to others** switch in the detail (§9.3), its flip sent through\n * {@link update} as `{ id, public }` — as keksdose and Kurvenschmiede send it. Off by\n * default: an app without public parts (kastlan) has no switch.\n */\n publicSwitch?: boolean;\n /** The **Votes** column and the card's count (§9.6). Off by default (kastlan); keksdose\n * always, Kurvenschmiede for a reader of the public parts. */\n votes?: boolean;\n /**\n * The report's comments, in a section of their own under the replies (§9.8): the app's\n * `FeedbackCommentThread` over its own queries — a component of its own, since its\n * queries are hooks. Return `null` for a row without a thread (a CRASH, a reader who\n * does not see the public parts; `false` is read as `null`). Called only for the open\n * row.\n *\n * ```tsx\n * comments={(row) => <RowComments id={row.id} isAdmin={isAdmin} canComment={!canEdit} />}\n * ```\n */\n comments?: (row: T) => ReactNode;\n /** The detail as drawn, to wrap or extend — kastlan's tour anchor:\n * `(row, detail) => <div data-tour=\"feedback-row-detail\">{detail}</div>`. */\n renderDetail?: (row: T, detail: ReactNode) => ReactNode;\n /**\n * The app's own columns over the kit's (§4.3's nine, and Votes): add, drop, reorder —\n * the keys are stable (`columns.filter((c) => c.key !== \"email\")`). A column brings its\n * filter with it. Keep it stable (module scope or `useCallback`): it is a memo's\n * dependency.\n */\n columns?: (columns: readonly DataTableColumn<T>[]) => readonly DataTableColumn<T>[];\n /** Which swipe does what on the phone (§4.5) — keksdose's Settings → Interaction binding,\n * `{ end: prefs.right, start: prefs.left }`. Default `DEFAULT_FEEDBACK_SWIPE`. */\n swipe?: FeedbackSwipeBinding;\n /** The status writes the TABLE offers — the status cell and the desktop's actions\n * column — commit: under a locked `WriteLockProvider` they stay and say why\n * (`FeedbackStatusTransitions`' `commit`). Off by default. The detail's own saves:\n * `detail={{ commit }}`. */\n commit?: CommitScope;\n /** `POST /feedback/attachments` (§3.3): the rework's file and the reply's. */\n onUpload?: FeedbackUpload;\n /** The app's authenticated GET for the pictures and the downloads (§3.5). */\n fetcher?: AuthedFetcher | null;\n /** \"Capture screenshot\" beside the rework's and the reply's file field. Default: the\n * kit's `captureAppScreenshot()`; `null` for no button. */\n onCaptureScreenshot?: (() => Promise<File | null>) | null;\n /**\n * The app's date writer (keksdose's `useDateCell()`, the date-format setting): the date\n * and resolved columns, the card, the phone's day headers AND the detail's \"Resolved:\",\n * so all five say the same. Left out, the kit's date with the weekday in the table and\n * on the phone, and in the detail its sentence date — as the parts draw them alone.\n */\n renderDate?: FeedbackRenderDate;\n /** The app's one-shot routes, on top of the kit's (0.52, FLW-7) — for the URL column and\n * the detail's URL section alike. Keep it stable (module scope). */\n pageLinkExclude?: readonly PathPattern[];\n /** The list is still loading (`list.isPending`): the table says \"Loading…\" where its\n * rows will be, not \"None\". */\n loading?: boolean;\n /** Over the detail's props the inbox sets — `commit`, `showSubject`,\n * `reworkAttachment`, `replyAttachments` (on here), `labels`, `className`. Merged last,\n * so it wins. */\n detail?: Partial<Omit<FeedbackRowDetailProps, \"row\">>;\n /** Over the `feedbackPage` namespace: the columns, the card, the empty state, the toggle\n * and the title. Keep it stable: it is a memo's dependency. */\n labels?: LabelOverride<FeedbackPageLabels>;\n}\n\n/** What {@link useFeedbackInbox} returns: the state, and the props of the parts. */\nexport interface FeedbackInboxState<T extends FeedbackRecord> {\n /** `canEdit && !mine`: the admin on the inbox. */\n editable: boolean;\n /** The page's title, `feedbackPage.title` — or `.myTitle` on `/my-feedback`. */\n title: string;\n /** The open row's id, from `?row=`; `null` when none is open or the param named no\n * id (`?row=abc`, `?row=1.5`, `?row=0`). */\n openId: number | null;\n /** Open a row by its id, or close it with `null`: rewrites `?row=`, replacing the\n * history entry rather than stacking one per report looked at. */\n setOpenId: (id: number | null) => void;\n /** A row's own address: this view — the table's page, sort and filters — with the row\n * open. The row's link (a new tab, a copied address); a plain click opens it in place. */\n rowHref: (row: T) => string;\n /** The undoable status change (§4.5): the status cell's, the pills', the swipes'. */\n changeStatus: FeedbackStatusChange;\n /** The phone's \"Only what is waiting for you\" — page state, not the URL (§4.3). */\n awaiting: boolean;\n setAwaiting: (next: boolean) => void;\n /** The one date writer the columns, the card and the day headers use. */\n renderDate: FeedbackRenderDate;\n /** `FeedbackRowDetail`'s props for a row, slots included. */\n detailProps: (row: T) => FeedbackRowDetailProps;\n /** `DataTable`'s props, ready to spread: the rows (newest first, narrowed by\n * `awaiting`), the columns, the open row, the detail, the phone's card, day headers and\n * swipes, the desktop's actions column, the empty state, `urlSync`,\n * `storageKey=\"feedback\"`, `fillHeight`. */\n table: DataTableProps<T>;\n}\n\n/** `?row=`'s value: a report's id is a positive integer, written in digits. Anything\n * else — a hand-edited link, `1e2`, `0x10`, `-3`, `1.5` — opens no row (and the URL\n * keeps it until a row is opened or closed). The three apps parsed with `Number()`, so\n * `?row=1e2` opened #100 in all three. */\nfunction parseRowId(raw: string): number | undefined {\n if (!/^[1-9]\\d*$/.test(raw)) return undefined;\n const id = Number(raw);\n return Number.isSafeInteger(id) ? id : undefined;\n}\n\nconst ROW_PARAM = { parse: parseRowId, history: \"replace\" } as const;\n\n/** Newest first, as `GET /feedback` sends it (§3.3) — the phone's day headers group runs\n * of equal days, so an older row between two of today's would split the day in two.\n * ISO strings from one server sort as their instants; the sort is stable. */\nconst newestFirst = (a: FeedbackRecord, b: FeedbackRecord) =>\n a.created_at < b.created_at ? 1 : a.created_at > b.created_at ? -1 : 0;\n\nconst rowId = (row: FeedbackRecord) => row.id;\nconst rowTitle = (row: FeedbackRecord) => row.title;\nconst captureScreenshot = () => captureAppScreenshot();\nconst ignore = () => {};\n/** A slot's \"nothing\": `cond && <Thread />` answers `false`, which the detail would draw\n * as an empty Comments section (it draws one for anything but `null`). */\nconst threadOf = (node: ReactNode): ReactNode => (node === false || node === \"\" ? undefined : node);\n\n/**\n * The inbox's wiring, once (§13): from the app's rows and mutations to what the table,\n * the detail and the phone cards take.\n *\n * ```tsx\n * const list = useQuery({ queryKey: keys.feedback.all, queryFn: api.listFeedback });\n * const update = useMutation({ mutationFn: ({ id, ...patch }: FeedbackDetailPatch) => api.update(id, patch), … });\n * const inbox = useFeedbackInbox({ rows: list.data ?? [], canEdit: isAdmin, viewerId: user?.id, update });\n * <DataTable {...inbox.table} edgeFade />\n * ```\n *\n * {@link FeedbackInbox} is this hook drawn — use the hook alone for a layout of your own.\n * Needs a react-router Router (`?row=`, `urlSync`).\n */\nexport function useFeedbackInbox<T extends FeedbackRecord>({\n rows,\n mine = false,\n canEdit,\n viewerId,\n update,\n reply,\n rework,\n publicSwitch = false,\n votes = false,\n comments,\n renderDetail,\n columns: ownColumns,\n swipe = DEFAULT_FEEDBACK_SWIPE,\n commit,\n onUpload,\n fetcher,\n onCaptureScreenshot = captureScreenshot,\n renderDate: renderDateProp,\n pageLinkExclude,\n loading = false,\n detail,\n labels,\n}: UseFeedbackInboxOptions<T>): FeedbackInboxState<T> {\n // An admin on /my-feedback reads as any author does (§4.3), as `feedbackColumns` has it.\n const editable = canEdit && !mine;\n const pageLabels = useFeedbackPageLabels(labels);\n const statusLabels = useFeedbackStatusLabels();\n const renderDate = useFeedbackRenderDate(renderDateProp);\n\n // TanStack's `mutateAsync` is stable, so the Undo and the columns' memo hold across\n // renders; the mutation object around it is not.\n const updateAsync = update.mutateAsync;\n const changeStatus = useFeedbackStatusUndo(updateAsync);\n\n // The open row IS the URL (§4.3: `?row=<id>`, every app) — which is what makes a row a\n // real link at all (keksdose feedback #451). Replace, not push: looking through ten\n // reports is not ten steps for Back.\n const [openId, setOpenId] = useSearchParamState<number | null>(\"row\", null, ROW_PARAM);\n const { pathname, search } = useLocation();\n // Through the router, so a basename (and a hash router's `#`) is in the address.\n const here = useHref({ pathname });\n // Built from the CURRENT query, not a bare `?row=`: the table keeps its `f.*`, `sort`\n // and `p` in the URL, and dropping them would open the new tab on an unfiltered list\n // where the row may not be on the first page (keksdose feedback-page.tsx:145).\n const rowHref = useCallback(\n (row: T) => {\n const params = new URLSearchParams(search);\n params.set(\"row\", String(row.id));\n return `${here}?${params.toString()}`;\n },\n [here, search],\n );\n\n // A thumb gesture on one screen, not a view worth linking to (§4.3; keksdose live #330).\n const [awaiting, setAwaiting] = useState(false);\n const shown = useMemo(\n () => (editable && awaiting ? rows.filter((row) => isFeedbackAwaiting(row.status)) : [...rows]).sort(newestFirst),\n [rows, editable, awaiting],\n );\n\n const kitColumns = useFeedbackColumns<T>({\n mine,\n canEdit,\n onStatus: changeStatus,\n renderDate,\n commit,\n votes,\n pageLinkExclude,\n labels,\n });\n const columns = useMemo(() => (ownColumns ? ownColumns(kitColumns) : kitColumns), [ownColumns, kitColumns]);\n const mobileGroupBy = useMemo(() => feedbackMobileGroupBy(renderDate), [renderDate]);\n\n // The rework's sending is the detail's too (keksdose, Kurvenschmiede): its editors and\n // the public switch wait for whichever write is out.\n const pending = Boolean(update.isPending || rework?.isPending);\n const capture = onCaptureScreenshot ?? undefined;\n\n const detailProps = (row: T): FeedbackRowDetailProps => ({\n row,\n canEdit: editable,\n viewerId,\n onUpdate: updateAsync,\n pending,\n // The page's change, not one the detail would build: the same Undo as the cell's.\n statusChange: changeStatus,\n onUpload,\n fetcher,\n onCaptureScreenshot: capture,\n // Only the app's own: left out, the detail's \"Resolved:\" is the kit's sentence date,\n // no weekday — as kastlan's has always been.\n renderDate: renderDateProp,\n pageLinkExclude,\n onReply: reply\n ? (body, status, attachmentUrl) =>\n reply.mutateAsync({\n id: row.id,\n body,\n ...(status ? { status } : {}),\n ...(attachmentUrl ? { attachmentUrl } : {}),\n })\n : undefined,\n // The reply's one file (§12): all three apps are on server-kit 0.18, and the reply\n // mutation's `attachmentUrl` is in its type from the start (the B1 risk the detail's\n // default guards against — an `onReply` that drops the file — cannot arise here).\n replyAttachments: true,\n replying: reply?.isPending ?? false,\n onRework: rework\n ? (note, attachmentUrl) => rework.mutateAsync({ id: row.id, note, ...(attachmentUrl ? { attachmentUrl } : {}) })\n : undefined,\n comments: threadOf(comments?.(row)),\n onPublicChange: publicSwitch ? (next) => runMutation(updateAsync, { id: row.id, public: next }, ignore) : undefined,\n ...detail,\n });\n\n const table: DataTableProps<T> = {\n rows: shown,\n columns,\n rowKey: rowId,\n storageKey: \"feedback\",\n urlSync: true,\n fillHeight: true,\n rowHref,\n // The link covers the row's first cell; a click anywhere on the row opens it — or,\n // on the open one, closes it.\n onRowClick: (row) => setOpenId((id) => (id === row.id ? null : row.id)),\n isExpanded: (row) => row.id === openId,\n expandedRow: (row) => {\n const node = <FeedbackRowDetail {...detailProps(row)} />;\n return renderDetail ? renderDetail(row, node) : node;\n },\n // §7.13: one word, also when a filter or the toggle leaves nothing; and while the\n // first answer is out, what is happening instead.\n empty: loading ? <LoadingState size=\"sm\" inline /> : <FeedbackEmptyState labels={labels} />,\n mobileGroupBy,\n mobileCard: (row) => (\n <FeedbackMobileCard row={row} showSubmitter={editable} renderDate={renderDate} votes={votes} labels={labels} />\n ),\n // The admin inbox only — /my-feedback is read-only by design (keksdose #331) — and\n // every swipe through the same Undo as a tap (§4.5, §7.10).\n mobileSwipeActions: editable\n ? (row) => feedbackSwipePlan(swipe, row, { change: changeStatus, labels: statusLabels })\n : undefined,\n // The swipes as the desktop's last column (0.46, keksdose dev#598), less what the\n // status cell already offers.\n desktopSwipeActions: { omit: feedbackSwipeOmitShown, name: rowTitle, ...(commit === undefined ? {} : { commit }) },\n };\n\n return {\n editable,\n title: mine ? pageLabels.myTitle : pageLabels.title,\n openId,\n setOpenId,\n rowHref,\n changeStatus,\n awaiting,\n setAwaiting,\n renderDate,\n detailProps,\n table,\n };\n}\n\n/* ── The component ──────────────────────────────────────────────────────────── */\n\nexport interface FeedbackInboxProps<T extends FeedbackRecord> extends UseFeedbackInboxOptions<T> {\n /**\n * Above the table. Default: `<PageHeader title={title} className=\"mb-4 md:shrink-0\" />`.\n * A function gets the page's title (`feedbackPage.title` / `.myTitle`) — the app's own\n * header: `(title) => <PageHeader size=\"compact\" title={title} data-tour=\"feedback-header\" />`.\n * `null` for none.\n */\n header?: ReactNode | ((title: string) => ReactNode);\n /** Over the inbox's `DataTable` props — `edgeFade`, `locale`, `mobileExpandAsDialog`,\n * `className`, another `storageKey`. Merged last, so it wins. */\n table?: Partial<DataTableProps<T>>;\n /** Further `FloatingAction`s on the phone: after the awaiting toggle on the inbox, in a\n * group of their own on `/my-feedback`. */\n phoneActions?: ReactNode;\n /** On the page's frame — the fill-height column (`FILL_HEIGHT_CLASS`); the app's page\n * padding goes here. */\n className?: string;\n}\n\n/**\n * The feedback inbox (§13): `/feedback` and `/my-feedback` drawn whole from\n * {@link useFeedbackInbox} — the header, the table with its detail, phone cards and swipes,\n * and the phone's awaiting toggle, in the fill-height frame (page layout §1.3) — so a page\n * is its queries and this:\n *\n * ```tsx\n * export function FeedbackPage({ mine = false }: { mine?: boolean }) {\n * const { user, isAdmin } = useAuth();\n * const list = useQuery({ queryKey: mine ? keys.mine : keys.all, queryFn: mine ? api.mine : api.all });\n * const update = useFeedbackUpdate();\n * const reply = useFeedbackReply();\n * const rework = useFeedbackRework();\n * if (!mine && !isAdmin) return <Navigate to=\"/my-feedback\" replace />;\n * return (\n * <FeedbackInbox\n * rows={list.data ?? []} loading={list.isPending} mine={mine} canEdit={isAdmin} viewerId={user?.id}\n * update={update} reply={reply} rework={rework}\n * onUpload={api.upload} fetcher={api.file}\n * />\n * );\n * }\n * ```\n *\n * Every slot is {@link UseFeedbackInboxOptions}' or one of the four below. The redirect of\n * a non-admin, the routes and the queries stay the app's.\n */\nexport function FeedbackInbox<T extends FeedbackRecord>({\n header,\n table,\n phoneActions,\n className,\n ...options\n}: FeedbackInboxProps<T>) {\n const inbox = useFeedbackInbox<T>(options);\n const pageLabels = useFeedbackPageLabels(options.labels);\n const head =\n header === undefined ? (\n <PageHeader title={inbox.title} className=\"mb-4 md:shrink-0\" />\n ) : typeof header === \"function\" ? (\n header(inbox.title)\n ) : (\n header\n );\n return (\n // A plain element on the way to the table carries the fill (page layout §1.3).\n <div className={cn(FILL_HEIGHT_CLASS, className)} aria-busy={options.loading || undefined} data-feedback-inbox=\"\">\n {head}\n <DataTable {...inbox.table} {...table} />\n {inbox.editable ? (\n // The phone's floating action (keksdose feedback #58): only on the inbox — on\n // /my-feedback the author is not waiting on their own verdict.\n <FeedbackAwaitingToggle pressed={inbox.awaiting} onPressedChange={inbox.setAwaiting} labels={options.labels}>\n {phoneActions}\n </FeedbackAwaitingToggle>\n ) : (\n phoneActions && (\n <FloatingActionGroup aria-label={pageLabels.phoneActions} className=\"md:hidden\">\n {phoneActions}\n </FloatingActionGroup>\n )\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -70,8 +70,8 @@ const FEEDBACK_STATUS_META = {
|
|
|
70
70
|
},
|
|
71
71
|
// The one status with nowhere to move to: the vocabulary has a brand, an info, a
|
|
72
72
|
// warning and a danger, and nothing that means "succeeded". Deliberately NOT
|
|
73
|
-
// borrowed from a
|
|
74
|
-
//
|
|
73
|
+
// borrowed from a colour that means something else — a field's "saved" is a trip
|
|
74
|
+
// to the database rather than an outcome, and tokens.css's `--success` says so in
|
|
75
75
|
// as many words. Green beside WONT_DO's red is the classic CVD-unsafe pair, which
|
|
76
76
|
// is why both carry a glyph and neither leans on colour alone.
|
|
77
77
|
DONE: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/feedback/feedback-vocabulary.ts"],"sourcesContent":["import {\n Ban,\n Bug,\n CheckCircle2,\n ClipboardCheck,\n CloudUpload,\n Eye,\n HelpCircle,\n Inbox,\n Lightbulb,\n MoreHorizontal,\n OctagonAlert,\n PauseCircle,\n RotateCcw,\n Wrench,\n} from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\n\n/**\n * The feedback **vocabulary**: the nine statuses and five categories, the glyph and tone\n * of each, their orders, and the policy for how a report moves from status to status.\n *\n * Data and plain functions, no components. Until 0.34 this lived at the top of\n * feedback-inbox.tsx, so the feedback menu every app mounts in its top bar imported the\n * inbox — its components and the attachment field they render — for one constant (a\n * category's glyph), and only tree-shaking kept the rest out of every app's entry chunk.\n * From here the top bar's imports reach none of it (MOD-1, refactor audit 2026-10).\n * feedback-inbox.tsx re-exports every name here, so nothing an app imports has moved.\n */\n\n/** The nine states a report can be in.\n *\n * Shared value for value across the apps on purpose: it is what lets one habit,\n * and one agent prompt, work on any repo. Five are a chain and three sit off\n * it — see {@link visibleFeedbackStatuses} for what that buys. `READY` joined in 0.31\n * (feedback contract §8.2): OPEN is \"filed, not yet triaged\", READY \"released for\n * implementation\", and nobody works an OPEN row. `REWORK` joined in 0.40 (§10.2\n * decisions 2, 4): \"sent back, released for work again\" — a READY that came back. */\nexport type FeedbackStatus =\n \"OPEN\" | \"READY\" | \"REWORK\" | \"IN_PROGRESS\" | \"IN_EVALUATION\" | \"NEEDS_LIVE_TEST\" | \"POSTPONED\" | \"DONE\" | \"WONT_DO\";\n\n/** What a report is about. `CRASH` is filed by an error boundary and never\n * chosen, which is exactly why it is loud below and absent from every picker. */\nexport type FeedbackCategory = \"CRASH\" | \"BUG\" | \"IDEA\" | \"QUESTION\" | \"OTHER\";\n\n/**\n * A glyph and a tone per status.\n *\n * Colour alone is not a label — two of these sit a hue apart, and a consumer who\n * moves `--brand` can park it beside any of the others — so every status carries\n * its own shape as well. Declaration order is chain order, and\n * {@link FEEDBACK_STATUS_ORDER} is derived from it rather than restated, because\n * a restated list cannot be checked for exhaustiveness: an eighth status would\n * break the build here and leave a literal eight long (as READY did in 0.31).\n */\nexport const FEEDBACK_STATUS_META: Record<FeedbackStatus, { icon: LucideIcon; activeBg: string; activeText: string }> =\n {\n OPEN: {\n icon: Inbox,\n activeBg: \"bg-active\",\n activeText: \"text-primary\",\n },\n // Triaged and released for implementation (feedback contract §8.2, keksdose live\n // #396): OPEN is \"filed, nobody has looked yet\", READY is \"go\". The brand tint at full\n // strength, so it reads as the go-ahead beside OPEN's neutral fill and\n // NEEDS_LIVE_TEST's muted brand.\n READY: {\n icon: ClipboardCheck,\n activeBg: \"bg-brand-soft\",\n activeText: \"text-brand\",\n },\n // Sent back by its author or an admin and released for work again (0.40, feedback\n // contract §10.2 decision 4): the Rework button's `RotateCcw`, in the danger tint so the\n // queue shows reworks apart from new reports. Not IN_PROGRESS's warning tint — that is\n // where a REWORK row moves next — and WONT_DO, the only other red, sits at the far end\n // with `Ban`.\n REWORK: {\n icon: RotateCcw,\n activeBg: \"bg-danger-soft\",\n activeText: \"text-danger\",\n },\n IN_PROGRESS: {\n icon: Wrench,\n activeBg: \"bg-warning-soft\",\n activeText: \"text-warning\",\n },\n // Where solved work is parked, waiting on the person who reported it. Its own\n // colour, because \"somebody has to check this\" is a state you want to find by\n // scanning rather than by reading.\n IN_EVALUATION: {\n icon: Eye,\n activeBg: \"bg-info-soft\",\n activeText: \"text-info\",\n },\n // Resolved but only checkable on a deployed build — the SOFT brand tint, so it\n // reads as \"waiting on something\" rather than as a done or a refusal, and it\n // follows whatever the consumer's brand is instead of pinning an indigo island\n // beside it.\n NEEDS_LIVE_TEST: {\n icon: CloudUpload,\n activeBg: \"bg-brand-soft\",\n activeText: \"text-brand-muted\",\n },\n // Parked on purpose. Muted, not `--danger`: it is not a refusal.\n POSTPONED: {\n icon: PauseCircle,\n activeBg: \"bg-active\",\n activeText: \"text-secondary\",\n },\n // The one status with nowhere to move to: the vocabulary has a brand, an info, a\n // warning and a danger, and nothing that means \"succeeded\". Deliberately NOT\n // borrowed from a family that means something else — `--status-synced` is a\n // FIELD's trip to the database rather than an outcome, and tokens.css says so in\n // as many words. Green beside WONT_DO's red is the classic CVD-unsafe pair, which\n // is why both carry a glyph and neither leans on colour alone.\n DONE: {\n icon: CheckCircle2,\n activeBg: \"bg-success-soft\",\n activeText: \"text-success\",\n },\n WONT_DO: {\n icon: Ban,\n activeBg: \"bg-danger-soft\",\n activeText: \"text-danger\",\n },\n };\n\n/** Chain order, then the three off it. The order a picker offers, and the rank a\n * status column sorts by — so a list sorted by status reads as a queue rather\n * than as an alphabet. */\nexport const FEEDBACK_STATUS_ORDER = Object.keys(FEEDBACK_STATUS_META) as FeedbackStatus[];\n\n/** States that sit OFF the linear chain: reachable from anywhere, leading\n * nowhere by themselves. `WONT_DO` has always worked this way; `POSTPONED`\n * (\"not now\") and `NEEDS_LIVE_TEST` (\"resolved, but only provable on a deployed\n * build\") are the same shape — a row can be parked or handed to the next deploy\n * from any point, and comes back to the chain when somebody picks a real state\n * for it.\n *\n * Kept out of the forward chain deliberately: putting them in it would mean an\n * \"advance\" gesture could park an item, and every row would have to pass\n * through them to reach `DONE`. */\nconst OFF_CHAIN: FeedbackStatus[] = [\"NEEDS_LIVE_TEST\", \"POSTPONED\", \"WONT_DO\"];\n\nconst FORWARD_CHAIN: FeedbackStatus[] = [\"OPEN\", \"READY\", \"IN_PROGRESS\", \"IN_EVALUATION\", \"DONE\"];\n\n/** The next status along the chain, or null at the end (or off it, as `WONT_DO`\n * is). What a swipe-to-advance gesture commits. `REWORK` (0.40) is not in the chain —\n * \"advance\" from READY still goes to IN_PROGRESS — but it leads there too: a reworked\n * row is READY's twin. */\nexport function nextFeedbackStatus(current: FeedbackStatus): FeedbackStatus | null {\n if (current === \"REWORK\") return \"IN_PROGRESS\";\n const at = FORWARD_CHAIN.indexOf(current);\n return at !== -1 && at < FORWARD_CHAIN.length - 1 ? FORWARD_CHAIN[at + 1] : null;\n}\n\n/**\n * The steps that make sense from where a row currently stands.\n *\n * For the compact control in a table cell: it is a glanceable triage affordance,\n * and a row of nine icons in a cell is noise. One step forward, one step back\n * (so an item can be sent back for rework), and any of the three off-chain\n * verdicts — which is what makes those usable as verdicts at all.\n *\n * The two parking states are not dead ends: each offers the step that resumes\n * work and the ones that close it, but deliberately **not each other** —\n * \"postponed\" and \"waiting on a deploy\" are different answers to different\n * questions, and a row moving between them directly is a re-triage, which starts\n * by picking the work back up.\n */\nexport function visibleFeedbackStatuses(current: FeedbackStatus): FeedbackStatus[] {\n const set = new Set<FeedbackStatus>([current]);\n const at = FORWARD_CHAIN.indexOf(current);\n if (current === \"REWORK\") {\n // READY's twin (0.40, §10.2 decision 4): the step that picks the work up, and the\n // verdicts a released row can still get.\n set.add(\"IN_PROGRESS\");\n for (const off of OFF_CHAIN) set.add(off);\n } else if (at !== -1) {\n if (at < FORWARD_CHAIN.length - 1) set.add(FORWARD_CHAIN[at + 1]);\n if (at > 0) set.add(FORWARD_CHAIN[at - 1]);\n for (const off of OFF_CHAIN) set.add(off);\n } else if (current !== \"WONT_DO\") {\n set.add(\"IN_PROGRESS\");\n set.add(\"DONE\");\n set.add(\"WONT_DO\");\n }\n // WONT_DO falls through with nothing added: it stays terminal, reopened by a\n // note from its author rather than by a status pill.\n return FEEDBACK_STATUS_ORDER.filter((status) => set.has(status));\n}\n\n/**\n * Every status, in chain order — what an EXPANDED row offers.\n *\n * {@link visibleFeedbackStatuses} narrows the choice to the steps that make\n * sense from where a row is, which is right for a table cell. Once the row is\n * open in front of you, that same narrowing turns every non-adjacent move into a\n * walk: OPEN to DONE meant three round trips, and WONT_DO could not be left at\n * all except through a rework note.\n *\n * Takes a `current` it does not use, so the two policies read as one pair at the\n * call sites and a future rule (\"terminal rows still cannot jump to X\") has a\n * place to live.\n */\nexport function selectableFeedbackStatuses(_current: FeedbackStatus): FeedbackStatus[] {\n return [...FEEDBACK_STATUS_ORDER];\n}\n\n/**\n * Per-category badge treatment, the counterpart to the status meta above.\n *\n * A category cell that is one grey pill holding the raw enum makes a CRASH —\n * filed automatically, by somebody staring at a broken page right now — read\n * exactly like a QUESTION, and sit unnoticed in the queue. `--danger` plus its own\n * glyph is what makes that impossible; the hand-filed categories stay\n * deliberately quiet so that the loud one means something.\n *\n * Declaration order is triage order, CRASH first.\n */\nexport const FEEDBACK_CATEGORY_META: Record<\n FeedbackCategory,\n { icon: LucideIcon; badgeBg: string; badgeText: string }\n> = {\n CRASH: {\n icon: OctagonAlert,\n badgeBg: \"bg-danger-soft\",\n badgeText: \"text-danger\",\n },\n BUG: {\n icon: Bug,\n badgeBg: \"bg-warning-soft\",\n badgeText: \"text-warning\",\n },\n IDEA: {\n icon: Lightbulb,\n badgeBg: \"bg-surface-2\",\n badgeText: \"text-secondary\",\n },\n QUESTION: {\n icon: HelpCircle,\n badgeBg: \"bg-surface-2\",\n badgeText: \"text-secondary\",\n },\n OTHER: {\n icon: MoreHorizontal,\n badgeBg: \"bg-surface-2\",\n badgeText: \"text-secondary\",\n },\n};\n\n/** Triage order, CRASH first — also a category column's sort key and the option\n * order in its filter. Sorting by the raw enum is alphabetical, which parks\n * CRASH between BUG and IDEA. */\nexport const FEEDBACK_CATEGORY_ORDER = Object.keys(FEEDBACK_CATEGORY_META) as FeedbackCategory[];\n\n/** A category's rank for a column sort. Anything this build has never heard of —\n * an older client against a newer API — sorts LAST rather than to `indexOf`'s\n * −1, which would rank it above CRASH. */\nexport function feedbackCategoryRank(category: FeedbackCategory): number {\n const at = FEEDBACK_CATEGORY_ORDER.indexOf(category);\n return at === -1 ? FEEDBACK_CATEGORY_ORDER.length : at;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/feedback/feedback-vocabulary.ts"],"sourcesContent":["import {\n Ban,\n Bug,\n CheckCircle2,\n ClipboardCheck,\n CloudUpload,\n Eye,\n HelpCircle,\n Inbox,\n Lightbulb,\n MoreHorizontal,\n OctagonAlert,\n PauseCircle,\n RotateCcw,\n Wrench,\n} from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\n\n/**\n * The feedback **vocabulary**: the nine statuses and five categories, the glyph and tone\n * of each, their orders, and the policy for how a report moves from status to status.\n *\n * Data and plain functions, no components. Until 0.34 this lived at the top of\n * feedback-inbox.tsx, so the feedback menu every app mounts in its top bar imported the\n * inbox — its components and the attachment field they render — for one constant (a\n * category's glyph), and only tree-shaking kept the rest out of every app's entry chunk.\n * From here the top bar's imports reach none of it (MOD-1, refactor audit 2026-10).\n * feedback-inbox.tsx re-exports every name here, so nothing an app imports has moved.\n */\n\n/** The nine states a report can be in.\n *\n * Shared value for value across the apps on purpose: it is what lets one habit,\n * and one agent prompt, work on any repo. Five are a chain and three sit off\n * it — see {@link visibleFeedbackStatuses} for what that buys. `READY` joined in 0.31\n * (feedback contract §8.2): OPEN is \"filed, not yet triaged\", READY \"released for\n * implementation\", and nobody works an OPEN row. `REWORK` joined in 0.40 (§10.2\n * decisions 2, 4): \"sent back, released for work again\" — a READY that came back. */\nexport type FeedbackStatus =\n \"OPEN\" | \"READY\" | \"REWORK\" | \"IN_PROGRESS\" | \"IN_EVALUATION\" | \"NEEDS_LIVE_TEST\" | \"POSTPONED\" | \"DONE\" | \"WONT_DO\";\n\n/** What a report is about. `CRASH` is filed by an error boundary and never\n * chosen, which is exactly why it is loud below and absent from every picker. */\nexport type FeedbackCategory = \"CRASH\" | \"BUG\" | \"IDEA\" | \"QUESTION\" | \"OTHER\";\n\n/**\n * A glyph and a tone per status.\n *\n * Colour alone is not a label — two of these sit a hue apart, and a consumer who\n * moves `--brand` can park it beside any of the others — so every status carries\n * its own shape as well. Declaration order is chain order, and\n * {@link FEEDBACK_STATUS_ORDER} is derived from it rather than restated, because\n * a restated list cannot be checked for exhaustiveness: an eighth status would\n * break the build here and leave a literal eight long (as READY did in 0.31).\n */\nexport const FEEDBACK_STATUS_META: Record<FeedbackStatus, { icon: LucideIcon; activeBg: string; activeText: string }> =\n {\n OPEN: {\n icon: Inbox,\n activeBg: \"bg-active\",\n activeText: \"text-primary\",\n },\n // Triaged and released for implementation (feedback contract §8.2, keksdose live\n // #396): OPEN is \"filed, nobody has looked yet\", READY is \"go\". The brand tint at full\n // strength, so it reads as the go-ahead beside OPEN's neutral fill and\n // NEEDS_LIVE_TEST's muted brand.\n READY: {\n icon: ClipboardCheck,\n activeBg: \"bg-brand-soft\",\n activeText: \"text-brand\",\n },\n // Sent back by its author or an admin and released for work again (0.40, feedback\n // contract §10.2 decision 4): the Rework button's `RotateCcw`, in the danger tint so the\n // queue shows reworks apart from new reports. Not IN_PROGRESS's warning tint — that is\n // where a REWORK row moves next — and WONT_DO, the only other red, sits at the far end\n // with `Ban`.\n REWORK: {\n icon: RotateCcw,\n activeBg: \"bg-danger-soft\",\n activeText: \"text-danger\",\n },\n IN_PROGRESS: {\n icon: Wrench,\n activeBg: \"bg-warning-soft\",\n activeText: \"text-warning\",\n },\n // Where solved work is parked, waiting on the person who reported it. Its own\n // colour, because \"somebody has to check this\" is a state you want to find by\n // scanning rather than by reading.\n IN_EVALUATION: {\n icon: Eye,\n activeBg: \"bg-info-soft\",\n activeText: \"text-info\",\n },\n // Resolved but only checkable on a deployed build — the SOFT brand tint, so it\n // reads as \"waiting on something\" rather than as a done or a refusal, and it\n // follows whatever the consumer's brand is instead of pinning an indigo island\n // beside it.\n NEEDS_LIVE_TEST: {\n icon: CloudUpload,\n activeBg: \"bg-brand-soft\",\n activeText: \"text-brand-muted\",\n },\n // Parked on purpose. Muted, not `--danger`: it is not a refusal.\n POSTPONED: {\n icon: PauseCircle,\n activeBg: \"bg-active\",\n activeText: \"text-secondary\",\n },\n // The one status with nowhere to move to: the vocabulary has a brand, an info, a\n // warning and a danger, and nothing that means \"succeeded\". Deliberately NOT\n // borrowed from a colour that means something else — a field's \"saved\" is a trip\n // to the database rather than an outcome, and tokens.css's `--success` says so in\n // as many words. Green beside WONT_DO's red is the classic CVD-unsafe pair, which\n // is why both carry a glyph and neither leans on colour alone.\n DONE: {\n icon: CheckCircle2,\n activeBg: \"bg-success-soft\",\n activeText: \"text-success\",\n },\n WONT_DO: {\n icon: Ban,\n activeBg: \"bg-danger-soft\",\n activeText: \"text-danger\",\n },\n };\n\n/** Chain order, then the three off it. The order a picker offers, and the rank a\n * status column sorts by — so a list sorted by status reads as a queue rather\n * than as an alphabet. */\nexport const FEEDBACK_STATUS_ORDER = Object.keys(FEEDBACK_STATUS_META) as FeedbackStatus[];\n\n/** States that sit OFF the linear chain: reachable from anywhere, leading\n * nowhere by themselves. `WONT_DO` has always worked this way; `POSTPONED`\n * (\"not now\") and `NEEDS_LIVE_TEST` (\"resolved, but only provable on a deployed\n * build\") are the same shape — a row can be parked or handed to the next deploy\n * from any point, and comes back to the chain when somebody picks a real state\n * for it.\n *\n * Kept out of the forward chain deliberately: putting them in it would mean an\n * \"advance\" gesture could park an item, and every row would have to pass\n * through them to reach `DONE`. */\nconst OFF_CHAIN: FeedbackStatus[] = [\"NEEDS_LIVE_TEST\", \"POSTPONED\", \"WONT_DO\"];\n\nconst FORWARD_CHAIN: FeedbackStatus[] = [\"OPEN\", \"READY\", \"IN_PROGRESS\", \"IN_EVALUATION\", \"DONE\"];\n\n/** The next status along the chain, or null at the end (or off it, as `WONT_DO`\n * is). What a swipe-to-advance gesture commits. `REWORK` (0.40) is not in the chain —\n * \"advance\" from READY still goes to IN_PROGRESS — but it leads there too: a reworked\n * row is READY's twin. */\nexport function nextFeedbackStatus(current: FeedbackStatus): FeedbackStatus | null {\n if (current === \"REWORK\") return \"IN_PROGRESS\";\n const at = FORWARD_CHAIN.indexOf(current);\n return at !== -1 && at < FORWARD_CHAIN.length - 1 ? FORWARD_CHAIN[at + 1] : null;\n}\n\n/**\n * The steps that make sense from where a row currently stands.\n *\n * For the compact control in a table cell: it is a glanceable triage affordance,\n * and a row of nine icons in a cell is noise. One step forward, one step back\n * (so an item can be sent back for rework), and any of the three off-chain\n * verdicts — which is what makes those usable as verdicts at all.\n *\n * The two parking states are not dead ends: each offers the step that resumes\n * work and the ones that close it, but deliberately **not each other** —\n * \"postponed\" and \"waiting on a deploy\" are different answers to different\n * questions, and a row moving between them directly is a re-triage, which starts\n * by picking the work back up.\n */\nexport function visibleFeedbackStatuses(current: FeedbackStatus): FeedbackStatus[] {\n const set = new Set<FeedbackStatus>([current]);\n const at = FORWARD_CHAIN.indexOf(current);\n if (current === \"REWORK\") {\n // READY's twin (0.40, §10.2 decision 4): the step that picks the work up, and the\n // verdicts a released row can still get.\n set.add(\"IN_PROGRESS\");\n for (const off of OFF_CHAIN) set.add(off);\n } else if (at !== -1) {\n if (at < FORWARD_CHAIN.length - 1) set.add(FORWARD_CHAIN[at + 1]);\n if (at > 0) set.add(FORWARD_CHAIN[at - 1]);\n for (const off of OFF_CHAIN) set.add(off);\n } else if (current !== \"WONT_DO\") {\n set.add(\"IN_PROGRESS\");\n set.add(\"DONE\");\n set.add(\"WONT_DO\");\n }\n // WONT_DO falls through with nothing added: it stays terminal, reopened by a\n // note from its author rather than by a status pill.\n return FEEDBACK_STATUS_ORDER.filter((status) => set.has(status));\n}\n\n/**\n * Every status, in chain order — what an EXPANDED row offers.\n *\n * {@link visibleFeedbackStatuses} narrows the choice to the steps that make\n * sense from where a row is, which is right for a table cell. Once the row is\n * open in front of you, that same narrowing turns every non-adjacent move into a\n * walk: OPEN to DONE meant three round trips, and WONT_DO could not be left at\n * all except through a rework note.\n *\n * Takes a `current` it does not use, so the two policies read as one pair at the\n * call sites and a future rule (\"terminal rows still cannot jump to X\") has a\n * place to live.\n */\nexport function selectableFeedbackStatuses(_current: FeedbackStatus): FeedbackStatus[] {\n return [...FEEDBACK_STATUS_ORDER];\n}\n\n/**\n * Per-category badge treatment, the counterpart to the status meta above.\n *\n * A category cell that is one grey pill holding the raw enum makes a CRASH —\n * filed automatically, by somebody staring at a broken page right now — read\n * exactly like a QUESTION, and sit unnoticed in the queue. `--danger` plus its own\n * glyph is what makes that impossible; the hand-filed categories stay\n * deliberately quiet so that the loud one means something.\n *\n * Declaration order is triage order, CRASH first.\n */\nexport const FEEDBACK_CATEGORY_META: Record<\n FeedbackCategory,\n { icon: LucideIcon; badgeBg: string; badgeText: string }\n> = {\n CRASH: {\n icon: OctagonAlert,\n badgeBg: \"bg-danger-soft\",\n badgeText: \"text-danger\",\n },\n BUG: {\n icon: Bug,\n badgeBg: \"bg-warning-soft\",\n badgeText: \"text-warning\",\n },\n IDEA: {\n icon: Lightbulb,\n badgeBg: \"bg-surface-2\",\n badgeText: \"text-secondary\",\n },\n QUESTION: {\n icon: HelpCircle,\n badgeBg: \"bg-surface-2\",\n badgeText: \"text-secondary\",\n },\n OTHER: {\n icon: MoreHorizontal,\n badgeBg: \"bg-surface-2\",\n badgeText: \"text-secondary\",\n },\n};\n\n/** Triage order, CRASH first — also a category column's sort key and the option\n * order in its filter. Sorting by the raw enum is alphabetical, which parks\n * CRASH between BUG and IDEA. */\nexport const FEEDBACK_CATEGORY_ORDER = Object.keys(FEEDBACK_CATEGORY_META) as FeedbackCategory[];\n\n/** A category's rank for a column sort. Anything this build has never heard of —\n * an older client against a newer API — sorts LAST rather than to `indexOf`'s\n * −1, which would rank it above CRASH. */\nexport function feedbackCategoryRank(category: FeedbackCategory): number {\n const at = FEEDBACK_CATEGORY_ORDER.indexOf(category);\n return at === -1 ? FEEDBACK_CATEGORY_ORDER.length : at;\n}\n"],"mappings":"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","names":[]}
|
package/dist/feedback.d.ts
CHANGED
|
@@ -20,3 +20,4 @@ export * from "./feedback/feedback-vote-button.js";
|
|
|
20
20
|
export * from "./feedback/feedback-comment-thread.js";
|
|
21
21
|
export * from "./feedback/feedback-replies.js";
|
|
22
22
|
export * from "./feedback/feedback-entry-file.js";
|
|
23
|
+
export { useFeedbackInbox, FeedbackInbox, type FeedbackInboxMutation, type FeedbackReplyPost, type FeedbackReworkPost, type UseFeedbackInboxOptions, type FeedbackInboxState, type FeedbackInboxProps, } from "./feedback/feedback-inbox-page.js";
|