@eifi1/ui-kit 0.26.0 → 0.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/dist/chart.d.ts +13 -9
- package/dist/components/account-chips.d.ts +1 -1
- package/dist/components/amount-input.d.ts +13 -9
- package/dist/components/button-group.d.ts +13 -9
- package/dist/components/calculator.d.ts +13 -9
- package/dist/components/confirm-dialog.d.ts +13 -9
- package/dist/components/copy-button.d.ts +10 -6
- package/dist/components/danger-confirm.d.ts +13 -9
- package/dist/components/data-table-cells.d.ts +1 -1
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-labels.d.ts +1 -1
- package/dist/components/data-table-pagination.d.ts +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/facing-pair.d.ts +13 -9
- package/dist/components/file-button.d.ts +13 -9
- package/dist/components/file-dropzone.d.ts +13 -9
- package/dist/components/form-actions.d.ts +13 -9
- package/dist/components/iban-input.d.ts +13 -9
- package/dist/components/language-select.d.ts +13 -9
- package/dist/components/legal.d.ts +5 -2
- package/dist/components/legal.js +4 -10
- package/dist/components/legal.js.map +1 -1
- package/dist/components/money-field.d.ts +13 -9
- package/dist/components/number-field.d.ts +13 -9
- package/dist/components/number-input.d.ts +13 -9
- package/dist/components/numpad-sheet.d.ts +13 -9
- package/dist/components/phone-input.d.ts +13 -9
- package/dist/components/series-chart.d.ts +13 -9
- package/dist/components/settings-fields.d.ts +13 -9
- package/dist/components/share-card.d.ts +13 -9
- package/dist/components/text-link.d.ts +13 -9
- package/dist/components/time-input.d.ts +13 -9
- package/dist/components/ui.d.ts +10 -6
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/{data-table-labels-Cdn1QS2k.d.ts → data-table-labels-DUELeXpa.d.ts} +5 -0
- package/dist/data-table.d.ts +1 -1
- package/dist/feedback/feedback-attachment.d.ts +13 -9
- package/dist/feedback/feedback-attachment.js +3 -3
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-capture.d.ts +71 -0
- package/dist/feedback/feedback-capture.js +194 -0
- package/dist/feedback/feedback-capture.js.map +1 -0
- package/dist/feedback/feedback-context.d.ts +71 -0
- package/dist/feedback/feedback-context.js +96 -0
- package/dist/feedback/feedback-context.js.map +1 -0
- package/dist/feedback/feedback-crash.d.ts +276 -0
- package/dist/feedback/feedback-crash.js +225 -0
- package/dist/feedback/feedback-crash.js.map +1 -0
- package/dist/feedback/feedback-dialog.d.ts +13 -9
- package/dist/feedback/feedback-dialog.js +5 -3
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +13 -9
- package/dist/feedback/feedback-inbox.js +89 -49
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-labels.d.ts +71 -0
- package/dist/feedback/feedback-labels.js +48 -0
- package/dist/feedback/feedback-labels.js.map +1 -0
- package/dist/feedback/feedback-menu.d.ts +71 -0
- package/dist/feedback/feedback-menu.js +61 -0
- package/dist/feedback/feedback-menu.js.map +1 -0
- package/dist/feedback/feedback-record.d.ts +71 -0
- package/dist/feedback/feedback-record.js +104 -0
- package/dist/feedback/feedback-record.js.map +1 -0
- package/dist/feedback/feedback-row-detail.d.ts +71 -0
- package/dist/feedback/feedback-row-detail.js +437 -0
- package/dist/feedback/feedback-row-detail.js.map +1 -0
- package/dist/feedback/feedback-status-undo.d.ts +71 -0
- package/dist/feedback/feedback-status-undo.js +40 -0
- package/dist/feedback/feedback-status-undo.js.map +1 -0
- package/dist/feedback/feedback-submit.d.ts +182 -0
- package/dist/feedback/feedback-submit.js +101 -0
- package/dist/feedback/feedback-submit.js.map +1 -0
- package/dist/feedback/feedback-swipe.d.ts +152 -0
- package/dist/feedback/feedback-swipe.js +73 -0
- package/dist/feedback/feedback-swipe.js.map +1 -0
- package/dist/feedback/feedback-table.d.ts +71 -0
- package/dist/feedback/feedback-table.js +357 -0
- package/dist/feedback/feedback-table.js.map +1 -0
- package/dist/feedback/feedback-thread.d.ts +13 -9
- package/dist/feedback.d.ts +17 -9
- package/dist/feedback.js +11 -0
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-file-drop.d.ts +13 -9
- package/dist/i18n/defaults.d.ts +13 -9
- package/dist/i18n/defaults.js +16 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +13 -9
- package/dist/i18n/german.js +91 -5
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +7 -3
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +13 -9
- package/dist/i18n/locales/de-CH.d.ts +13 -9
- package/dist/i18n/locales/en.d.ts +13 -9
- package/dist/i18n/locales/en.js +10 -1
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +13 -9
- package/dist/i18n/locales/es.js +89 -5
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +13 -9
- package/dist/i18n/locales/fr.js +89 -5
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +13 -9
- package/dist/i18n/locales/hu.js +90 -5
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +13 -9
- package/dist/i18n/locales/it.js +89 -5
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +13 -9
- package/dist/i18n/locales/zh.js +89 -5
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +16 -11
- package/dist/i18n/review.js +11 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/i18n/swiss.js +3 -1
- package/dist/i18n/swiss.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/{feedback-BXKH-ToU.d.ts → kit-labels-DQg82eHB.d.ts} +1275 -25
- package/dist/rhf/fields.d.ts +13 -9
- package/dist/rhf/form.d.ts +13 -9
- package/dist/rhf.d.ts +13 -9
- package/dist/shell/app-shell.d.ts +13 -9
- package/dist/shell/top-bar-brand.d.ts +13 -9
- package/dist/shell.d.ts +11 -11
- package/dist/wizard/stepper-nav.d.ts +13 -9
- package/dist/wizard.d.ts +13 -9
- package/package.json +6 -1
- package/src/components/data-table.tsx +5 -0
- package/src/components/legal.tsx +8 -7
- package/src/feedback/feedback-attachment.tsx +19 -3
- package/src/feedback/feedback-capture.ts +285 -0
- package/src/feedback/feedback-context.tsx +217 -0
- package/src/feedback/feedback-crash.ts +547 -0
- package/src/feedback/feedback-dialog.tsx +29 -6
- package/src/feedback/feedback-inbox.tsx +131 -27
- package/src/feedback/feedback-labels.ts +127 -0
- package/src/feedback/feedback-menu.tsx +132 -0
- package/src/feedback/feedback-record.ts +345 -0
- package/src/feedback/feedback-row-detail.tsx +825 -0
- package/src/feedback/feedback-status-undo.ts +111 -0
- package/src/feedback/feedback-submit.tsx +224 -0
- package/src/feedback/feedback-swipe.tsx +170 -0
- package/src/feedback/feedback-table.tsx +757 -0
- package/src/feedback.ts +13 -0
- package/src/i18n/defaults.ts +16 -0
- package/src/i18n/german.ts +98 -5
- package/src/i18n/kit-labels.tsx +20 -0
- package/src/i18n/locales/en.ts +15 -1
- package/src/i18n/locales/es.ts +96 -5
- package/src/i18n/locales/fr.ts +95 -5
- package/src/i18n/locales/hu.ts +94 -5
- package/src/i18n/locales/it.ts +97 -5
- package/src/i18n/locales/zh.ts +90 -5
- package/src/i18n/review.ts +14 -2
- package/src/i18n/swiss.ts +5 -1
- package/src/index.ts +13 -0
- package/tokens.css +4 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { InputHTMLAttributes, ReactNode, AnchorHTMLAttributes, Ref, ReactElement, ButtonHTMLAttributes, SelectHTMLAttributes, ComponentPropsWithoutRef, TextareaHTMLAttributes, RefObject } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { f as DataTableLabels } from './data-table-labels-Cdn1QS2k.js';
|
|
3
|
+
import { d as DataTableColumn, f as DataTableLabels } from './data-table-labels-DUELeXpa.js';
|
|
5
4
|
import { MiniCalendarLabels, WeekDay } from './components/mini-calendar.js';
|
|
6
5
|
import { CalendarHeatmapLabels } from './components/calendar-heatmap.js';
|
|
7
6
|
import { PopoverLabels } from './components/popover.js';
|
|
8
7
|
import { ChipInputLabels } from './components/chip.js';
|
|
9
8
|
import { FieldSyncLabels } from './components/field-sync.js';
|
|
9
|
+
import { TooltipSide } from './components/tooltip.js';
|
|
10
10
|
import { WizardLabels } from './wizard/types.js';
|
|
11
11
|
import { TourLabels } from './tour/tour.js';
|
|
12
12
|
import { CommandPaletteLabels } from './search/command-palette.js';
|
|
@@ -24,8 +24,11 @@ import { IconPickerLabels } from './components/icon-picker.js';
|
|
|
24
24
|
import { DialogFrameProps, DialogFrameLabels } from './components/dialog-frame.js';
|
|
25
25
|
import { MeasuredGridLabels } from './components/measured-grid.js';
|
|
26
26
|
import { LucideIcon } from 'lucide-react';
|
|
27
|
+
import { UserAvatarBadge } from './components/user-avatar.js';
|
|
28
|
+
import { TopBarMenuEntry } from './shell/topbar-action-menu.js';
|
|
29
|
+
import { FloatingCorner, FloatingPanelLabels } from './components/floating-panel.js';
|
|
30
|
+
import { AuthedFetcher } from './hooks/use-authed-src.js';
|
|
27
31
|
import { AccountSettingsLabels } from './components/account-settings-labels.js';
|
|
28
|
-
import { FloatingPanelLabels } from './components/floating-panel.js';
|
|
29
32
|
import { BulkActionBarLabels } from './components/bulk-action-bar.js';
|
|
30
33
|
import { ListLabels } from './components/list.js';
|
|
31
34
|
import { BreadcrumbsLabels } from './components/breadcrumbs.js';
|
|
@@ -1948,7 +1951,8 @@ interface FeedbackDialogLabels extends FeedbackAttachmentLabels {
|
|
|
1948
1951
|
subject: string;
|
|
1949
1952
|
body: string;
|
|
1950
1953
|
attachment: string;
|
|
1951
|
-
submitHint
|
|
1954
|
+
/** See {@link FeedbackDialogTextLabels.submitHint}. */
|
|
1955
|
+
submitHint: FeedbackSubmitHint;
|
|
1952
1956
|
cancel: string;
|
|
1953
1957
|
save: string;
|
|
1954
1958
|
/** @deprecated since 0.16.0 — use `attachmentList`, the one key for the heading and
|
|
@@ -1956,6 +1960,8 @@ interface FeedbackDialogLabels extends FeedbackAttachmentLabels {
|
|
|
1956
1960
|
* `attachmentList` (but not over an `attachmentList` passed beside it). */
|
|
1957
1961
|
attachments?: string;
|
|
1958
1962
|
}
|
|
1963
|
+
/** The submit hint: a string, or the line for the platform (`apple`: Mac, iPhone, iPad). */
|
|
1964
|
+
type FeedbackSubmitHint = string | ((apple: boolean) => string);
|
|
1959
1965
|
/**
|
|
1960
1966
|
* The dialog's own strings — the `feedbackDialog` namespace of `<UiKitProvider
|
|
1961
1967
|
* labels>` (0.12.0). The attachment keys are not repeated here: they are the
|
|
@@ -1974,8 +1980,13 @@ interface FeedbackDialogTextLabels {
|
|
|
1974
1980
|
bodyOptional?: string;
|
|
1975
1981
|
/** The heading over the attachment buttons. */
|
|
1976
1982
|
attachment: string;
|
|
1977
|
-
/**
|
|
1978
|
-
|
|
1983
|
+
/**
|
|
1984
|
+
* The line beside the buttons naming the shortcut. Since 0.27.0 a function of the
|
|
1985
|
+
* platform, like `form.submitShortcut`: `apple` is true on a Mac, iPhone or iPad, where
|
|
1986
|
+
* the key is Cmd — "⌘ Enter to send" there, keksdose's "Ctrl+Enter to send" elsewhere
|
|
1987
|
+
* (§7.12). A plain string is still taken and shown everywhere, as before 0.27.0.
|
|
1988
|
+
*/
|
|
1989
|
+
submitHint: FeedbackSubmitHint;
|
|
1979
1990
|
cancel: string;
|
|
1980
1991
|
save: string;
|
|
1981
1992
|
/** @deprecated since 0.16.0 — use `feedbackAttachment.attachmentList`. It and this
|
|
@@ -1984,6 +1995,16 @@ interface FeedbackDialogTextLabels {
|
|
|
1984
1995
|
* honoured when set: it wins over `feedbackAttachment.attachmentList`. */
|
|
1985
1996
|
attachments?: string;
|
|
1986
1997
|
}
|
|
1998
|
+
/**
|
|
1999
|
+
* English. The feedback contract's wording (docs/feedback-harmonization.md §4.2, 0.27.0):
|
|
2000
|
+
* "Send", not keksdose's "Save", and the body asks "What happened?" (Marcel's two calls);
|
|
2001
|
+
* the rest is keksdose's. Since 0.27.0 `attachment`, the single-mode heading, is keksdose's
|
|
2002
|
+
* `feedback.attachment` **"Attachment"** (it said "Screenshot" over a button that says "Add
|
|
2003
|
+
* attachment" and a picker that may offer a PDF), and `submitHint` is keksdose's
|
|
2004
|
+
* **"Ctrl+Enter to send"** — "⌘ Enter to send" on Apple platforms, where the old
|
|
2005
|
+
* "Ctrl/⌘ + Enter" made every reader parse both. The `attachments="multiple"` heading is
|
|
2006
|
+
* `feedbackAttachment.attachmentList` ("Attachments") and did not change.
|
|
2007
|
+
*/
|
|
1987
2008
|
declare const DEFAULT_FEEDBACK_DIALOG_LABELS: FeedbackDialogTextLabels;
|
|
1988
2009
|
interface FeedbackSubmission {
|
|
1989
2010
|
title: string;
|
|
@@ -2142,6 +2163,20 @@ interface FeedbackAttachmentFieldLabels {
|
|
|
2142
2163
|
* the keys above. */
|
|
2143
2164
|
attachmentUploadFailed?: (name: string) => string;
|
|
2144
2165
|
}
|
|
2166
|
+
/**
|
|
2167
|
+
* English, in the feedback contract's wording since 0.27.0 (docs/feedback-harmonization.md
|
|
2168
|
+
* §4.2: keksdose's `feedback.*` keys are the canon, and the kit owns them now). Three
|
|
2169
|
+
* strings moved with it:
|
|
2170
|
+
*
|
|
2171
|
+
* - `attachmentAdd` "Attach image" → **"Add attachment"**: the field takes a PDF or a text
|
|
2172
|
+
* log as well wherever the app passes `FEEDBACK_ATTACHMENT_ACCEPT` (keksdose feedback
|
|
2173
|
+
* #87), and a button that says "image" over a picker that offers a PDF is wrong in one
|
|
2174
|
+
* of the two places.
|
|
2175
|
+
* - `attachmentPaste` → keksdose's longer line, which says WHY one would paste — a region
|
|
2176
|
+
* snip shows one corner rather than the whole page (Steering Design feedback #39).
|
|
2177
|
+
* - `attachmentLimit` for one: "remove **it**", as keksdose's `attachments_full_one`;
|
|
2178
|
+
* "remove one" of a single file read oddly.
|
|
2179
|
+
*/
|
|
2145
2180
|
declare const DEFAULT_FEEDBACK_ATTACHMENT_LABELS: FeedbackAttachmentFieldLabels;
|
|
2146
2181
|
/** Why a file was turned away. `"count"` only ever comes from `multiple` (and `refs`)
|
|
2147
2182
|
* mode: more files arrived at once than `max` left room for, and the surplus was
|
|
@@ -2421,13 +2456,20 @@ declare function pastedName(type: string): string;
|
|
|
2421
2456
|
* Design's was thinner in every one of those places and read as a different
|
|
2422
2457
|
* product for no reason anyone chose.
|
|
2423
2458
|
*
|
|
2424
|
-
* **
|
|
2425
|
-
*
|
|
2426
|
-
*
|
|
2427
|
-
* the row
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2430
|
-
*
|
|
2459
|
+
* **The report itself lives next door (0.27.0).** This file used to say there
|
|
2460
|
+
* was deliberately no `Feedback` type here: the apps stored a report differently
|
|
2461
|
+
* (a context JSON column and an object store in one, a `page_path` and the bytes
|
|
2462
|
+
* in the row in another). Marcel's feedback round (2026-10-04,
|
|
2463
|
+
* docs/feedback-harmonization.md §5) gave all three apps one contract, so the
|
|
2464
|
+
* shape is now shared — `FeedbackRecord` / `FeedbackContext` in
|
|
2465
|
+
* feedback-record.ts, with the constants and body helpers. The components HERE
|
|
2466
|
+
* still take the values they draw rather than a row, which keeps them usable
|
|
2467
|
+
* from any surface; the detail panel stays a *shell* the row detail fills.
|
|
2468
|
+
*
|
|
2469
|
+
* **The words are the kit's too (0.27.0).** The status and category names are
|
|
2470
|
+
* the `feedbackStatus` / `feedbackCategory` namespaces (feedback-labels.ts,
|
|
2471
|
+
* keksdose's wording), read by default by the badges and the transitions below;
|
|
2472
|
+
* a `label` the caller passes still wins.
|
|
2431
2473
|
*/
|
|
2432
2474
|
/** The seven states a report can be in.
|
|
2433
2475
|
*
|
|
@@ -2519,20 +2561,26 @@ declare function feedbackCategoryRank(category: FeedbackCategory): number;
|
|
|
2519
2561
|
* Falls back to OTHER's treatment for a category this build has never heard of,
|
|
2520
2562
|
* so an older client against a newer API degrades to a readable neutral pill
|
|
2521
2563
|
* instead of throwing on `meta.icon` — a crash report must not be able to cause
|
|
2522
|
-
* one. The *label*
|
|
2523
|
-
*
|
|
2564
|
+
* one. The *label* gets the same guard: by default the `feedbackCategory`
|
|
2565
|
+
* namespace's word (0.27.0), and for a value it has no word for, what OTHER is
|
|
2566
|
+
* called.
|
|
2524
2567
|
*/
|
|
2525
2568
|
declare function FeedbackCategoryBadge({ category, label, compact, className, }: {
|
|
2526
2569
|
category: FeedbackCategory;
|
|
2527
|
-
label:
|
|
2570
|
+
/** Over the `feedbackCategory` label (0.27.0: optional — before, every app passed
|
|
2571
|
+
* its own translation). */
|
|
2572
|
+
label?: ReactNode;
|
|
2528
2573
|
compact?: boolean;
|
|
2529
2574
|
className?: string;
|
|
2530
2575
|
}): react.JSX.Element;
|
|
2531
2576
|
/** Where a report stands, as a badge — for the places that only *report* the
|
|
2532
|
-
* status rather than offering to change it.
|
|
2577
|
+
* status rather than offering to change it. Named by the `feedbackStatus`
|
|
2578
|
+
* namespace unless `label` says otherwise (0.27.0); a status this build has never
|
|
2579
|
+
* heard of shows OPEN's look and its own raw value, never OPEN's name. */
|
|
2533
2580
|
declare function FeedbackStatusBadge({ status, label, className, }: {
|
|
2534
2581
|
status: FeedbackStatus;
|
|
2535
|
-
label:
|
|
2582
|
+
/** Over the `feedbackStatus` label (0.27.0: optional). */
|
|
2583
|
+
label?: ReactNode;
|
|
2536
2584
|
className?: string;
|
|
2537
2585
|
}): react.JSX.Element;
|
|
2538
2586
|
/**
|
|
@@ -2550,14 +2598,29 @@ declare function FeedbackStatusBadge({ status, label, className, }: {
|
|
|
2550
2598
|
* that policy readable at the two call sites instead of hiding it behind
|
|
2551
2599
|
* `variant`.
|
|
2552
2600
|
*/
|
|
2553
|
-
declare function FeedbackStatusTransitions({ status, statuses, canEdit, onPick, variant, label, className, }: {
|
|
2601
|
+
declare function FeedbackStatusTransitions({ status, statuses, canEdit, onPick, variant, label, commit, disabledReason: ownDisabledReason, className, }: {
|
|
2554
2602
|
status: FeedbackStatus;
|
|
2555
2603
|
statuses: FeedbackStatus[];
|
|
2556
2604
|
canEdit: boolean;
|
|
2557
2605
|
onPick: (status: FeedbackStatus) => void;
|
|
2558
2606
|
variant: "icon" | "pill";
|
|
2559
|
-
/** What each status is called
|
|
2560
|
-
|
|
2607
|
+
/** What each status is called. Default (0.27.0): the `feedbackStatus` namespace —
|
|
2608
|
+
* before, every app passed its own translation. */
|
|
2609
|
+
label?: (status: FeedbackStatus) => string;
|
|
2610
|
+
/**
|
|
2611
|
+
* Every button here SAVES the moment it is pressed (0.27.0) — opt in and, under a
|
|
2612
|
+
* locked {@link WriteLockProvider}, the row stays as it is and says why: each status
|
|
2613
|
+
* the row could move to is `aria-disabled` but focusable, the click does nothing, and
|
|
2614
|
+
* the lock's reason is in the kit Tooltip and the button's description — the
|
|
2615
|
+
* `disabledReason` path `Button` takes. Off by default: a feedback inbox is not what
|
|
2616
|
+
* every app's lock is about (keksdose's shell lock is a read-only demo BUDGET, and a
|
|
2617
|
+
* report is not budget data).
|
|
2618
|
+
*/
|
|
2619
|
+
commit?: boolean;
|
|
2620
|
+
/** Why the statuses cannot be changed right now, the same way (a lock's own reason
|
|
2621
|
+
* wins over it). Only read while `canEdit`: a read-only row is a picture of the
|
|
2622
|
+
* workflow, not a refused action. */
|
|
2623
|
+
disabledReason?: ReactNode;
|
|
2561
2624
|
className?: string;
|
|
2562
2625
|
}): react.JSX.Element;
|
|
2563
2626
|
/**
|
|
@@ -2588,7 +2651,7 @@ declare function FeedbackStatusTransitions({ status, statuses, canEdit, onPick,
|
|
|
2588
2651
|
* the editor re-seeds when THAT changes. Either way the decision is the caller's,
|
|
2589
2652
|
* which is the point: the editor cannot tell a new subject from a new render.
|
|
2590
2653
|
*/
|
|
2591
|
-
declare function FeedbackNoteEditor({ initial, pending, onSave, onCancel, saveLabel, cancelLabel, placeholder, rows, attachment, resetKey, }: {
|
|
2654
|
+
declare function FeedbackNoteEditor({ initial, pending, onSave, onCancel, saveLabel, cancelLabel, placeholder, rows, attachment, resetKey, required, commit, }: {
|
|
2592
2655
|
initial: string;
|
|
2593
2656
|
pending: boolean;
|
|
2594
2657
|
onSave: (value: string, attachment?: File | null) => void;
|
|
@@ -2610,13 +2673,29 @@ declare function FeedbackNoteEditor({ initial, pending, onSave, onCancel, saveLa
|
|
|
2610
2673
|
* that cannot put a `key` on the editor (see the note above). Leave it out and
|
|
2611
2674
|
* the draft is never thrown away behind the user's back. */
|
|
2612
2675
|
resetKey?: string | number;
|
|
2676
|
+
/**
|
|
2677
|
+
* A blank note cannot be saved (0.27.0): Save stays disabled — and Ctrl/⌘+Enter does
|
|
2678
|
+
* nothing — until the trimmed text is non-empty, and the box is `aria-required`. For
|
|
2679
|
+
* the rework note, which the feedback contract requires (§3.4: a file alone cannot
|
|
2680
|
+
* be sent; keksdose `feedback-page.tsx:577` dropped the click silently instead, so a
|
|
2681
|
+
* press on Send with an empty box looked like a lost send). Default `false`: an
|
|
2682
|
+
* outcome or a description may be cleared on purpose.
|
|
2683
|
+
*/
|
|
2684
|
+
required?: boolean;
|
|
2685
|
+
/** Save COMMITS (0.27.0): under a locked `WriteLockProvider` it is disabled the
|
|
2686
|
+
* `disabledReason` way, with the lock's reason, and Ctrl/⌘+Enter saves nothing. The
|
|
2687
|
+
* text box stays editable — nothing in it reaches the server until the save. */
|
|
2688
|
+
commit?: boolean;
|
|
2613
2689
|
}): react.JSX.Element;
|
|
2614
2690
|
/** What {@link FeedbackNoteEditor} needs in order to offer a picture with the
|
|
2615
2691
|
* note: the same things {@link FeedbackAttachmentField} takes, so the reply path is
|
|
2616
2692
|
* held to the app's own limits — and drawn in its own look — rather than the
|
|
2617
2693
|
* defaults. */
|
|
2618
2694
|
interface FeedbackNoteAttachment {
|
|
2619
|
-
|
|
2695
|
+
/** Over the field's own `feedbackAttachment` namespace. Optional since 0.27.0 — the
|
|
2696
|
+
* field has read the provider's words since 0.7.0, so an app with a catalogue has
|
|
2697
|
+
* nothing to restate. */
|
|
2698
|
+
labels?: Partial<FeedbackAttachmentLabels>;
|
|
2620
2699
|
accept?: string[];
|
|
2621
2700
|
maxBytes?: number;
|
|
2622
2701
|
/** `info` (0.23.0) names the refused file and the limit. */
|
|
@@ -2654,8 +2733,9 @@ declare function FeedbackDetailSection({ title, action, children, }: {
|
|
|
2654
2733
|
}): react.JSX.Element;
|
|
2655
2734
|
/** The stack a detail panel is. Here so the spacing between sections is decided
|
|
2656
2735
|
* once rather than by whichever app was written second. */
|
|
2657
|
-
declare function FeedbackDetail({ children }: {
|
|
2736
|
+
declare function FeedbackDetail({ children, className }: {
|
|
2658
2737
|
children: ReactNode;
|
|
2738
|
+
className?: string;
|
|
2659
2739
|
}): react.JSX.Element;
|
|
2660
2740
|
/** Prose inside a section — a body, an outcome, a note.
|
|
2661
2741
|
*
|
|
@@ -3025,6 +3105,1165 @@ type ChatComposerLabels = FeedbackComposerLabels;
|
|
|
3025
3105
|
/** {@link FeedbackComposerSlotContext}, for {@link ChatComposer}'s `attachmentSlot`. */
|
|
3026
3106
|
type ChatComposerSlotContext = FeedbackComposerSlotContext;
|
|
3027
3107
|
|
|
3108
|
+
/**
|
|
3109
|
+
* The words of a report's status and category, and the toasts a feedback page shows —
|
|
3110
|
+
* three `<UiKitProvider labels>` namespaces (0.27.0).
|
|
3111
|
+
*
|
|
3112
|
+
* Until 0.27 every app kept its own map from the enum to a translation key (keksdose
|
|
3113
|
+
* `STATUS_LABEL` and `categoryLabelKey`, "the package has no translations"), and the
|
|
3114
|
+
* three German maps had drifted apart: Kurvenschmiede said "In Arbeit", "In Prüfung",
|
|
3115
|
+
* "Test auf der Live-Umgebung" where keksdose and kastlan said "In Bearbeitung",
|
|
3116
|
+
* "Zur Prüfung", "Live testen". Marcel's feedback round (2026-10-04,
|
|
3117
|
+
* docs/feedback-harmonization.md §2.4) made keksdose's wording the canon and the kit its
|
|
3118
|
+
* owner, in all seven languages. The English defaults below are keksdose's
|
|
3119
|
+
* `frontend/src/shared/i18n/locales/en.json` (`feedback.*`); the de-CH canon for the
|
|
3120
|
+
* catalogues is noted on each key (keksdose `de-CH.json` — ss, never ß).
|
|
3121
|
+
*
|
|
3122
|
+
* `FeedbackStatusBadge`, `FeedbackCategoryBadge` and `FeedbackStatusTransitions` read
|
|
3123
|
+
* the first two by default, so an app drops its own maps; a `label` passed to them still
|
|
3124
|
+
* wins.
|
|
3125
|
+
*/
|
|
3126
|
+
/**
|
|
3127
|
+
* `feedbackStatus` — one word per status, keyed by the enum value so
|
|
3128
|
+
* `labels[row.status]` is the whole lookup.
|
|
3129
|
+
*
|
|
3130
|
+
* de-CH canon: OPEN "Offen" · IN_PROGRESS "In Bearbeitung" · IN_EVALUATION "Zur Prüfung"
|
|
3131
|
+
* · NEEDS_LIVE_TEST "Live testen" · POSTPONED "Zurückgestellt" · DONE "Erledigt" ·
|
|
3132
|
+
* WONT_DO "Wird nicht umgesetzt".
|
|
3133
|
+
*/
|
|
3134
|
+
type FeedbackStatusLabels = Record<FeedbackStatus, string>;
|
|
3135
|
+
declare const DEFAULT_FEEDBACK_STATUS_LABELS: FeedbackStatusLabels;
|
|
3136
|
+
/**
|
|
3137
|
+
* `feedbackCategory` — one word per category, keyed by the enum value. `CRASH` is never
|
|
3138
|
+
* pickable, but it is shown: on the badge, in the filter, in a row's detail.
|
|
3139
|
+
*
|
|
3140
|
+
* de-CH canon: CRASH "Absturz" · BUG "Fehler" · IDEA "Idee" · QUESTION "Frage" ·
|
|
3141
|
+
* OTHER "Sonstiges".
|
|
3142
|
+
*/
|
|
3143
|
+
type FeedbackCategoryLabels = Record<FeedbackCategory, string>;
|
|
3144
|
+
declare const DEFAULT_FEEDBACK_CATEGORY_LABELS: FeedbackCategoryLabels;
|
|
3145
|
+
/**
|
|
3146
|
+
* `feedbackToast` — what the submit dialog, a failed save and a status change say in a
|
|
3147
|
+
* toast (§4.2, §4.4, §4.5 of the contract). A server's `detail` sentence, when it sent
|
|
3148
|
+
* one, wins over `submitFailed` and `updateFailed` (§3.7); that choice is the caller's.
|
|
3149
|
+
*/
|
|
3150
|
+
interface FeedbackToastLabels {
|
|
3151
|
+
/** A report was filed. de-CH: "Danke für Ihr Feedback!" */
|
|
3152
|
+
submitted: string;
|
|
3153
|
+
/** `POST /feedback` failed with no `detail` of its own.
|
|
3154
|
+
* de-CH: "Feedback konnte nicht gesendet werden" */
|
|
3155
|
+
submitFailed: string;
|
|
3156
|
+
/** A picked file is not an image, a PDF or a text file (§3.5).
|
|
3157
|
+
* de-CH: "Nur Bilder, PDF- oder Textdateien sind erlaubt" */
|
|
3158
|
+
attachmentUnsupported: string;
|
|
3159
|
+
/** A picked file is over the 10 MB the contract allows (§3.5).
|
|
3160
|
+
* de-CH: "Datei ist grösser als 10 MB" */
|
|
3161
|
+
attachmentTooLarge: string;
|
|
3162
|
+
/** More files were picked than fit; `count` is how many fit.
|
|
3163
|
+
* de-CH: "Es passen nur {{count}} Anhänge – die übrigen wurden weggelassen." */
|
|
3164
|
+
attachmentTooMany: (count: number) => string;
|
|
3165
|
+
/** The screenshot capture failed (or its optional peer is not installed).
|
|
3166
|
+
* de-CH: "Screenshot konnte nicht aufgenommen werden" */
|
|
3167
|
+
captureFailed: string;
|
|
3168
|
+
/** A `PATCH /feedback/{id}` failed with no `detail` of its own (§4.4).
|
|
3169
|
+
* de-CH: "Änderung konnte nicht gespeichert werden." */
|
|
3170
|
+
updateFailed: string;
|
|
3171
|
+
/** A status change landed — the 8 s Undo toast (§4.5). `status` is the NEW status's
|
|
3172
|
+
* label, `title` the report's title. de-CH: "Auf „{{status}}“ gesetzt: {{title}}" */
|
|
3173
|
+
statusChanged: (status: string, title: string) => string;
|
|
3174
|
+
/** That toast's action. de-CH: "Rückgängig" */
|
|
3175
|
+
statusUndo: string;
|
|
3176
|
+
/** The Undo landed — `status` is the RESTORED status's label.
|
|
3177
|
+
* de-CH: "Zurück auf „{{status}}“: {{title}}" */
|
|
3178
|
+
statusRestored: (status: string, title: string) => string;
|
|
3179
|
+
}
|
|
3180
|
+
declare const DEFAULT_FEEDBACK_TOAST_LABELS: FeedbackToastLabels;
|
|
3181
|
+
/** `feedbackStatus` resolved: English, then `<UiKitProvider labels>`, then `labels`. For
|
|
3182
|
+
* the places that need a status's name outside the kit's own parts — a filter's options,
|
|
3183
|
+
* `feedbackSwipePlan`'s panels, an app's overview tile. */
|
|
3184
|
+
declare function useFeedbackStatusLabels(labels?: Partial<FeedbackStatusLabels>): FeedbackStatusLabels;
|
|
3185
|
+
/** `feedbackCategory` resolved: English, then the provider, then `labels`. */
|
|
3186
|
+
declare function useFeedbackCategoryLabels(labels?: Partial<FeedbackCategoryLabels>): FeedbackCategoryLabels;
|
|
3187
|
+
/** `feedbackToast` resolved: English, then the provider, then `labels`. */
|
|
3188
|
+
declare function useFeedbackToastLabels(labels?: Partial<FeedbackToastLabels>): FeedbackToastLabels;
|
|
3189
|
+
|
|
3190
|
+
/**
|
|
3191
|
+
* `feedbackMenu` — the top-bar feedback menu's own words (0.27.0). The four category rows
|
|
3192
|
+
* are `feedbackCategory`'s, shared with the badges and the dialog's picker. de-CH canon
|
|
3193
|
+
* (keksdose `de-CH.json`): `trigger` "Feedback senden", `myFeedback` "Mein Feedback",
|
|
3194
|
+
* `viewFeedback` "Feedback ansehen".
|
|
3195
|
+
*/
|
|
3196
|
+
interface FeedbackMenuLabels {
|
|
3197
|
+
/** The trigger's accessible name, and the menu's. keksdose `feedback.send`. */
|
|
3198
|
+
trigger: string;
|
|
3199
|
+
/** The link to the reporter's own reports — everyone but an admin. keksdose
|
|
3200
|
+
* `feedback.my_title`. */
|
|
3201
|
+
myFeedback: string;
|
|
3202
|
+
/** The link to the inbox — an admin's one list link, in place of "My feedback".
|
|
3203
|
+
* keksdose `feedback.view_all`. */
|
|
3204
|
+
viewFeedback: string;
|
|
3205
|
+
}
|
|
3206
|
+
declare const DEFAULT_FEEDBACK_MENU_LABELS: FeedbackMenuLabels;
|
|
3207
|
+
interface FeedbackMenuProps {
|
|
3208
|
+
/** A category row was chosen — open the submit dialog on it (`useFeedbackSubmit`'s
|
|
3209
|
+
* `open`). Never called with `CRASH`: there is no row for it. */
|
|
3210
|
+
onFile: (category: FeedbackCategory) => void;
|
|
3211
|
+
/** The list link is "View feedback" → {@link inboxHref} INSTEAD of "My feedback" (§4.1,
|
|
3212
|
+
* keksdose `top-bar.tsx:205-214`). The admin check is the app's (kastlan's is
|
|
3213
|
+
* company-scoped). Default `false`. */
|
|
3214
|
+
isAdmin?: boolean;
|
|
3215
|
+
/**
|
|
3216
|
+
* Rows of the app's own, between the divider and the list link — keksdose's
|
|
3217
|
+
* `Sparkles` Help assistant → `/assistant` and `MessagesSquare` Support chat →
|
|
3218
|
+
* `/support` with its unread chip. Any {@link TopBarMenuEntry}; keep their `key`s clear
|
|
3219
|
+
* of the menu's own (`BUG`, `IDEA`, `QUESTION`, `OTHER`, `feedback-divider`,
|
|
3220
|
+
* `my-feedback`, `view-feedback`).
|
|
3221
|
+
*/
|
|
3222
|
+
extraEntries?: TopBarMenuEntry[];
|
|
3223
|
+
/** The user's own reports, linked for everyone but an admin. Default `"/my-feedback"`
|
|
3224
|
+
* (§2.3). */
|
|
3225
|
+
myFeedbackHref?: string;
|
|
3226
|
+
/** The admin inbox, linked for an admin. Default `"/feedback"` (§2.3). */
|
|
3227
|
+
inboxHref?: string;
|
|
3228
|
+
/** A dot on the trigger — keksdose's unread support replies (`tone: "danger"`). Its
|
|
3229
|
+
* `label` joins the trigger's name ("Send feedback 2 unread"). The words are the app's. */
|
|
3230
|
+
iconBadge?: UserAvatarBadge | null;
|
|
3231
|
+
/** Prop > `<UiKitProvider labels={{ feedbackMenu }}>` > English. */
|
|
3232
|
+
labels?: Partial<FeedbackMenuLabels>;
|
|
3233
|
+
/** Which edge of the trigger the panel lines up with. Default `end`. */
|
|
3234
|
+
align?: "start" | "end" | "left" | "right";
|
|
3235
|
+
}
|
|
3236
|
+
/**
|
|
3237
|
+
* The top-bar feedback menu, the same in every app (0.27.0, docs/feedback-harmonization.md
|
|
3238
|
+
* §4.1) — keksdose `frontend/src/app/top-bar.tsx:156` on the kit's `TopBarActionMenu`:
|
|
3239
|
+
*
|
|
3240
|
+
* 1. a `MessageSquare` trigger named "Send feedback", with an optional `iconBadge`;
|
|
3241
|
+
* 2. Bug · Idea · Question · Other (`Bug`, `Lightbulb`, `HelpCircle`, `MoreHorizontal` —
|
|
3242
|
+
* the category badges' own glyphs), each calling `onFile(category)`;
|
|
3243
|
+
* 3. a divider, always;
|
|
3244
|
+
* 4. the app's `extraEntries`;
|
|
3245
|
+
* 5. ONE list link, `Inbox`: an admin's is "View feedback" → `/feedback`, everyone
|
|
3246
|
+
* else's "My feedback" → `/my-feedback` — keksdose's (`top-bar.tsx:205-214`, feedback
|
|
3247
|
+
* #331: admins get the management view, everyone else their own submissions).
|
|
3248
|
+
*
|
|
3249
|
+
* WHY A PRESET. Three apps had drawn three menus on the same primitive and they disagreed:
|
|
3250
|
+
* kastlan headed it "New submission", Kurvenschmiede "Send feedback" with a single link
|
|
3251
|
+
* that changed its words by role, keksdose had no heading and one link by role. keksdose
|
|
3252
|
+
* is the source (§2.4, §7.11): no heading, one link — the inbox for an admin, who reaches
|
|
3253
|
+
* their own reports there too — and the kit draws it, so the row order, the icons and the
|
|
3254
|
+
* words cannot drift again. Where the menu sits in the bar stays each app's.
|
|
3255
|
+
*
|
|
3256
|
+
* `CRASH` has no row and never will: crash reports are filed by the error boundary with
|
|
3257
|
+
* the stack and a fingerprint (keksdose feedback #160), and a hand-filed "crash" would sort
|
|
3258
|
+
* to the top like one with none of what makes it actionable. The server refuses it with a
|
|
3259
|
+
* 422 anyway (§3.4).
|
|
3260
|
+
*
|
|
3261
|
+
* The menu forwards nothing to its trigger, as `TopBarActionMenu` does not: keksdose's
|
|
3262
|
+
* tour anchor (`data-tour="feedback-menu"`) stays on a wrapping `<span>`. Needs a router,
|
|
3263
|
+
* as every `TopBarActionMenu` with link rows does.
|
|
3264
|
+
*/
|
|
3265
|
+
declare function FeedbackMenu({ onFile, isAdmin, extraEntries, myFeedbackHref, inboxHref, iconBadge, labels: labelsProp, align, }: FeedbackMenuProps): react.JSX.Element;
|
|
3266
|
+
|
|
3267
|
+
/**
|
|
3268
|
+
* One feedback report as the three apps send it, and the facts the kit reads off it.
|
|
3269
|
+
*
|
|
3270
|
+
* Until 0.27 the inbox parts took no report at all ("no `Feedback` type in this file",
|
|
3271
|
+
* feedback-inbox.tsx): keksdose kept its reporter context in a JSON column, kastlan a
|
|
3272
|
+
* thinner copy of it, Kurvenschmiede a `page_path` and the bytes in the row. Marcel's
|
|
3273
|
+
* feedback round (2026-10-04, docs/feedback-harmonization.md) settled one API contract
|
|
3274
|
+
* for all three — keksdose's, read off its code — so the apps now share one shape and the
|
|
3275
|
+
* kit can take a row instead of a dozen loose values. This file is that shape (§3.1,
|
|
3276
|
+
* §3.2), the contract's constants (§3.4, §3.5, §4.3) and the readers and writers of the
|
|
3277
|
+
* one field whose format is itself contract: the body, which carries the rework rounds
|
|
3278
|
+
* (§3.4) — lifted from keksdose `frontend/src/features/feedback/body-attachments.ts`.
|
|
3279
|
+
*
|
|
3280
|
+
* Plain functions and data, no components: everything here is as usable from a loop
|
|
3281
|
+
* script's helper or a test as from a page.
|
|
3282
|
+
*/
|
|
3283
|
+
/** Which copy of an app a report came from — keksdose `app/deploy-environment.ts`.
|
|
3284
|
+
* Any other string is read as "not prod" (the admin list shows it upper-cased as a
|
|
3285
|
+
* chip, §4.3), so an app with a `staging` loses nothing. */
|
|
3286
|
+
type FeedbackEnvironment = "prod" | "dev" | "local";
|
|
3287
|
+
/**
|
|
3288
|
+
* The reporter's context, written by the submit dialog (keksdose
|
|
3289
|
+
* `use-feedback-dialog.tsx:105`) — §3.2 of the contract.
|
|
3290
|
+
*
|
|
3291
|
+
* **Every key is optional on read**, and a reader treats a missing one as "unknown":
|
|
3292
|
+
* kastlan's client sends six of them (no email, no version), Kurvenschmiede's old rows are
|
|
3293
|
+
* backfilled from two columns with a path-only `url`, crash rows add their own (and
|
|
3294
|
+
* `origin` / `environment` from the crash payload, §3.6 — older crash rows have neither),
|
|
3295
|
+
* and an erased account's row keeps only an allow-list (§3.2: `environment` stays,
|
|
3296
|
+
* `origin` goes). Extra keys are allowed — a server may add its own — hence the index
|
|
3297
|
+
* signature.
|
|
3298
|
+
*/
|
|
3299
|
+
interface FeedbackContext {
|
|
3300
|
+
/** The full `location.href` at open time; `""` when "Attach current page URL" was off.
|
|
3301
|
+
* Readers also accept a path-only value (Kurvenschmiede's backfilled rows). */
|
|
3302
|
+
url?: string;
|
|
3303
|
+
/** Its pathname; `""` when the URL was withheld. */
|
|
3304
|
+
route?: string;
|
|
3305
|
+
/** `location.origin` — which copy of the app, not personal; never withheld. */
|
|
3306
|
+
origin?: string;
|
|
3307
|
+
/** {@link FeedbackEnvironment}, or whatever the app calls its other copies. */
|
|
3308
|
+
environment?: FeedbackEnvironment | (string & {});
|
|
3309
|
+
/** The submitter, as a snapshot. The server should overwrite the three `user_*` keys
|
|
3310
|
+
* from the session (Kurvenschmiede does), so nobody files in someone else's name. */
|
|
3311
|
+
user_id?: number | null;
|
|
3312
|
+
user_email?: string | null;
|
|
3313
|
+
user_display_name?: string | null;
|
|
3314
|
+
/** `"406x816"`. */
|
|
3315
|
+
viewport?: string;
|
|
3316
|
+
/** `navigator.userAgent`. */
|
|
3317
|
+
ua?: string;
|
|
3318
|
+
/** The app build (`__APP_VERSION__`). */
|
|
3319
|
+
version?: string;
|
|
3320
|
+
fingerprint?: string;
|
|
3321
|
+
/** Which error boundary caught it. */
|
|
3322
|
+
boundary?: "app" | "page" | (string & {});
|
|
3323
|
+
online?: boolean;
|
|
3324
|
+
occurrences?: number;
|
|
3325
|
+
first_seen_at?: string;
|
|
3326
|
+
last_seen_at?: string;
|
|
3327
|
+
auto_reported?: boolean;
|
|
3328
|
+
[key: string]: unknown;
|
|
3329
|
+
}
|
|
3330
|
+
/**
|
|
3331
|
+
* One report — `FeedbackResponse`, keksdose `schemas/feedback.py:117`, §3.1 of the
|
|
3332
|
+
* contract. What `GET /feedback`, `GET /feedback/my`, `POST /feedback` and
|
|
3333
|
+
* `PATCH /feedback/{id}` answer in all three apps.
|
|
3334
|
+
*
|
|
3335
|
+
* Extra fields are allowed and ignored (kastlan's `company_id`, `user_name`): an app's
|
|
3336
|
+
* own row type with more fields is assignable to this one as it stands.
|
|
3337
|
+
*
|
|
3338
|
+
* There is **no rework counter field** — {@link reworkCount} reads it off the body, so
|
|
3339
|
+
* nothing can forget to set it.
|
|
3340
|
+
*/
|
|
3341
|
+
interface FeedbackRecord {
|
|
3342
|
+
id: number;
|
|
3343
|
+
/** `null`: the author's account was erased (keksdose live #275). */
|
|
3344
|
+
user_id: number | null;
|
|
3345
|
+
/** The admin list only (joined); `null` — or absent — on `/my`, create and update. */
|
|
3346
|
+
user_email?: string | null;
|
|
3347
|
+
/** 1–255 characters, never blank. */
|
|
3348
|
+
title: string;
|
|
3349
|
+
/** `""` when none. Rework rounds are appended here (§3.4) — read it with
|
|
3350
|
+
* {@link splitDescription} / {@link splitBodyAttachments}, write it with
|
|
3351
|
+
* {@link appendRework} / {@link replaceDescription}. */
|
|
3352
|
+
body: string;
|
|
3353
|
+
category: FeedbackCategory;
|
|
3354
|
+
status: FeedbackStatus;
|
|
3355
|
+
/** §3.2; `null` on rows that have none. */
|
|
3356
|
+
context: FeedbackContext | null;
|
|
3357
|
+
/** The ONE captured screenshot's attachment URL, or `null`. */
|
|
3358
|
+
screenshot_url: string | null;
|
|
3359
|
+
/** Up to 5 other files; `null` = none. Optional because a server older than the
|
|
3360
|
+
* column sends no key at all (keksdose dev #578 met exactly that). */
|
|
3361
|
+
attachment_urls?: string[] | null;
|
|
3362
|
+
/** The team's answer; `null` = none written. */
|
|
3363
|
+
outcome: string | null;
|
|
3364
|
+
/** Set on ENTERING `DONE` / `WONT_DO`, cleared on leaving them. ISO 8601. */
|
|
3365
|
+
resolved_at: string | null;
|
|
3366
|
+
/** ISO 8601. */
|
|
3367
|
+
created_at: string;
|
|
3368
|
+
/** ISO 8601. */
|
|
3369
|
+
updated_at: string;
|
|
3370
|
+
}
|
|
3371
|
+
/**
|
|
3372
|
+
* What a report may carry (§3.5, keksdose `upload_guards.py:197` and its
|
|
3373
|
+
* `attachment-options.ts`): the kit's four image types plus a PDF and a plain-text log
|
|
3374
|
+
* (keksdose feedback #87). SVG and HTML stay out — both are script-carrying documents in
|
|
3375
|
+
* an image's clothing. For `FeedbackAttachmentField` / `FeedbackDialog`'s `accept`, with
|
|
3376
|
+
* `DEFAULT_MAX_ATTACHMENTS` (5) and `DEFAULT_MAX_ATTACHMENT_BYTES` (10 MB) from
|
|
3377
|
+
* feedback-attachment.tsx, which already are the contract's limits.
|
|
3378
|
+
*
|
|
3379
|
+
* A plain array (not frozen) because `accept` takes `string[]`, as
|
|
3380
|
+
* `DEFAULT_ATTACHMENT_ACCEPT` does — copy it before changing it.
|
|
3381
|
+
*/
|
|
3382
|
+
declare const FEEDBACK_ATTACHMENT_ACCEPT: string[];
|
|
3383
|
+
/**
|
|
3384
|
+
* The categories a person can pick — every one but `CRASH`, in triage order (Bug, Idea,
|
|
3385
|
+
* Question, Other: the feedback menu's rows, §4.1). `CRASH` is filed by an error boundary
|
|
3386
|
+
* and the server refuses it by hand with a 422 (keksdose `_reject_manual_crash`), so no
|
|
3387
|
+
* picker may offer it.
|
|
3388
|
+
*/
|
|
3389
|
+
declare const FEEDBACK_PICKABLE_CATEGORIES: readonly FeedbackCategory[];
|
|
3390
|
+
/**
|
|
3391
|
+
* The statuses a report can be sent back for **rework** from (§3.4): everything the team
|
|
3392
|
+
* has answered — `IN_EVALUATION`, `NEEDS_LIVE_TEST`, `POSTPONED`, `DONE`, `WONT_DO`. On
|
|
3393
|
+
* these the server reopens the row when the body grows by a rework block; on `OPEN` /
|
|
3394
|
+
* `IN_PROGRESS` there is nothing to send back yet.
|
|
3395
|
+
*/
|
|
3396
|
+
declare const FEEDBACK_REWORKABLE_STATUSES: readonly FeedbackStatus[];
|
|
3397
|
+
/**
|
|
3398
|
+
* The statuses in which the AUTHOR may still edit title, body and category (§3.4,
|
|
3399
|
+
* keksdose `canAuthorEdit`, `feedback-page.tsx:398`): `OPEN` and `IN_PROGRESS`. Once the
|
|
3400
|
+
* team has answered, the description is what they answered and stays as it was; a
|
|
3401
|
+
* correction goes in as a rework.
|
|
3402
|
+
*/
|
|
3403
|
+
declare const FEEDBACK_AUTHOR_EDITABLE_STATUSES: readonly FeedbackStatus[];
|
|
3404
|
+
/**
|
|
3405
|
+
* The two statuses that wait on the person triaging: answered and handed back for a
|
|
3406
|
+
* verdict, or answered and only checkable on a deployed build (keksdose
|
|
3407
|
+
* `AWAITING_VERDICT`, live #330). The phone's "Only what is waiting for you" toggle
|
|
3408
|
+
* narrows the inbox to exactly these (§4.3).
|
|
3409
|
+
*/
|
|
3410
|
+
declare const FEEDBACK_AWAITING_STATUSES: readonly FeedbackStatus[];
|
|
3411
|
+
/** The body line for one uploaded attachment — what {@link appendRework} writes under
|
|
3412
|
+
* the note. */
|
|
3413
|
+
declare function feedbackAttachmentLine(url: string): string;
|
|
3414
|
+
/**
|
|
3415
|
+
* A rework round appended to a body — the exact text `PATCH /feedback/{id}` takes as the
|
|
3416
|
+
* new `body` (§3.4; keksdose `submitRework`, `feedback-page.tsx:350`):
|
|
3417
|
+
*
|
|
3418
|
+
* ```text
|
|
3419
|
+
* <old body>\n\n--- REWORK 2026-10-04 09:12 ---\n<note>\n[screenshot] /api/v1/feedback/attachments/<key>
|
|
3420
|
+
* ```
|
|
3421
|
+
*
|
|
3422
|
+
* - The stamp is UTC, `YYYY-MM-DD HH:MM` (`toISOString().slice(0, 16)`), from `now`.
|
|
3423
|
+
* - The old body is kept byte for byte, so the result STRICTLY EXTENDS it — which is
|
|
3424
|
+
* how the server recognises an append and reopens an answered row (keksdose
|
|
3425
|
+
* `_is_rework_append`, live #331). `\n\n` is left out when the old body is `""`.
|
|
3426
|
+
* - ONE file per round (`url`), uploaded first by the caller: a body appended without
|
|
3427
|
+
* it would be a rework whose evidence never arrived.
|
|
3428
|
+
*
|
|
3429
|
+
* **Returns `null` when the trimmed note is empty** — a file alone cannot be sent
|
|
3430
|
+
* (keksdose `:577`). Null rather than a throw because an empty note is something a
|
|
3431
|
+
* person does, not a bug, and the type then makes every caller decide what an empty
|
|
3432
|
+
* box means before anything can reach the PATCH.
|
|
3433
|
+
*
|
|
3434
|
+
* **Throws a `TypeError` when `url` is not an attachment URL** of the contract's shape
|
|
3435
|
+
* (`/api/v1/feedback/attachments/<key>`): that one IS a bug — the upload handed back
|
|
3436
|
+
* something foreign — and written anyway it would sit in the body as prose that no
|
|
3437
|
+
* reader recognises as a file.
|
|
3438
|
+
*/
|
|
3439
|
+
declare function appendRework(body: string, note: string, url?: string | null, now?: Date): string | null;
|
|
3440
|
+
/**
|
|
3441
|
+
* How many times a report has been sent back — the count of `--- REWORK … ---` rule
|
|
3442
|
+
* lines in its body (keksdose live #331: the "category for items in rework", derived
|
|
3443
|
+
* rather than stored, so nothing can forget to set it or leave it set). Folded kastlan
|
|
3444
|
+
* comments do not count.
|
|
3445
|
+
*/
|
|
3446
|
+
declare function reworkCount(body: string): number;
|
|
3447
|
+
/**
|
|
3448
|
+
* A body split into the prose to render and the files to show beside it, in body order.
|
|
3449
|
+
*
|
|
3450
|
+
* The file lines are stripped from the text: they are a location, not something anybody
|
|
3451
|
+
* wrote, and left in they would put a raw URL in the middle of a sentence. Trailing blank
|
|
3452
|
+
* lines go (what a stripped marker leaves at the end of a note); every blank line between
|
|
3453
|
+
* paragraphs stays.
|
|
3454
|
+
*/
|
|
3455
|
+
declare function splitBodyAttachments(body: string): {
|
|
3456
|
+
text: string;
|
|
3457
|
+
urls: string[];
|
|
3458
|
+
};
|
|
3459
|
+
/**
|
|
3460
|
+
* The ORIGINAL description, apart from everything appended to it since (§4.4.1).
|
|
3461
|
+
*
|
|
3462
|
+
* - `description` — what the author wrote when filing, without its file lines and
|
|
3463
|
+
* trailing blank lines: the text the "Edit description" editor holds.
|
|
3464
|
+
* - `appended` — every `--- REWORK … ---` round and folded `--- COMMENT … ---` block,
|
|
3465
|
+
* from the first one's rule line to the end, verbatim (`""` when there are none).
|
|
3466
|
+
*
|
|
3467
|
+
* keksdose's editor edits the raw body (`initial={fb.body}`, `feedback-page.tsx:420`), so
|
|
3468
|
+
* an author tidying a typo could delete the rework history and the file lines with it.
|
|
3469
|
+
* The kit's editor holds `description` only and saves through
|
|
3470
|
+
* {@link replaceDescription}, which puts the rest back unchanged.
|
|
3471
|
+
*/
|
|
3472
|
+
declare function splitDescription(body: string): {
|
|
3473
|
+
description: string;
|
|
3474
|
+
appended: string;
|
|
3475
|
+
};
|
|
3476
|
+
/**
|
|
3477
|
+
* The body with its original description replaced and everything else kept — the file
|
|
3478
|
+
* lines of the original part (after the new text, in their order) and the appended
|
|
3479
|
+
* blocks (verbatim, after a blank line). The new text is trimmed; clearing it saves the
|
|
3480
|
+
* rest alone (`""` when there is no rest).
|
|
3481
|
+
*
|
|
3482
|
+
* `replaceDescription(body, splitDescription(body).description)` gives back a canonical
|
|
3483
|
+
* body unchanged — a trimmed description, then each block after one blank line, as the
|
|
3484
|
+
* dialog and {@link appendRework} write them.
|
|
3485
|
+
*/
|
|
3486
|
+
declare function replaceDescription(body: string, description: string): string;
|
|
3487
|
+
/**
|
|
3488
|
+
* Whether an attachment URL is a picture (keksdose dev #578). The upload route takes
|
|
3489
|
+
* PDFs and text logs too and serves them as downloads, never inline, so an `<img>` on
|
|
3490
|
+
* one shows a broken icon. The key's extension is the server's own statement of the
|
|
3491
|
+
* type (minted from the sniffed content), so it is the thing to read.
|
|
3492
|
+
*/
|
|
3493
|
+
declare function isImageAttachment(url: string): boolean;
|
|
3494
|
+
/** The file name an attachment downloads as — its key, the last segment of the URL. */
|
|
3495
|
+
declare function attachmentName(url: string): string;
|
|
3496
|
+
|
|
3497
|
+
/**
|
|
3498
|
+
* The reporter context of a feedback report: the box the submit dialog shows, and the
|
|
3499
|
+
* `context` object `POST /feedback` sends (0.27.0, docs/feedback-harmonization.md §3.2,
|
|
3500
|
+
* §4.2). One file for both, so what the box SAYS will be sent and what IS sent cannot
|
|
3501
|
+
* drift apart.
|
|
3502
|
+
*
|
|
3503
|
+
* Each app had written both halves itself (keksdose `use-feedback-dialog.tsx:105` and
|
|
3504
|
+
* `:222`, kastlan `feedback-button.tsx:80`, Kurvenschmiede `use-feedback-dialog.tsx:124`),
|
|
3505
|
+
* and they had drifted: kastlan sent six keys with `user_email: null` and no version,
|
|
3506
|
+
* Kurvenschmiede a `page_path` column and a sentence where keksdose had a labelled box,
|
|
3507
|
+
* and only keksdose showed the full URL it was about to send.
|
|
3508
|
+
*/
|
|
3509
|
+
/**
|
|
3510
|
+
* The person filing, as the app knows them. Snake case on purpose: keksdose's and
|
|
3511
|
+
* Kurvenschmiede's user objects already have this shape (`id`, `email`, `display_name`),
|
|
3512
|
+
* so they pass their `user` as it is; kastlan maps its token's `sub` and name parts.
|
|
3513
|
+
*
|
|
3514
|
+
* A SNAPSHOT for the report and the box. The server should overwrite the three `user_*`
|
|
3515
|
+
* keys from the session (§3.2; Kurvenschmiede does), so nobody can file in someone else's
|
|
3516
|
+
* name — this is what the reporter is SHOWN, not proof of who they are.
|
|
3517
|
+
*/
|
|
3518
|
+
interface FeedbackSubmitter {
|
|
3519
|
+
id?: number | null;
|
|
3520
|
+
email?: string | null;
|
|
3521
|
+
display_name?: string | null;
|
|
3522
|
+
}
|
|
3523
|
+
/** What {@link feedbackContext} is built from. */
|
|
3524
|
+
interface FeedbackContextInput {
|
|
3525
|
+
/** The submitter, or `null` when signed out / unknown — the three `user_*` keys are then
|
|
3526
|
+
* `null`, as keksdose sends them. */
|
|
3527
|
+
user?: FeedbackSubmitter | null;
|
|
3528
|
+
/** The box's "Attach current page URL" checkbox. `false` withholds `url` and `route`
|
|
3529
|
+
* (both `""`); nothing else is withheld — `origin` says which copy of the app, which is
|
|
3530
|
+
* not personal, and triage needs it first (keksdose `use-feedback-dialog.tsx:110`). */
|
|
3531
|
+
attachUrl: boolean;
|
|
3532
|
+
/** `location.href` when the dialog OPENED — the page the report is about, not the one
|
|
3533
|
+
* the reporter may have navigated to while typing. */
|
|
3534
|
+
url: string;
|
|
3535
|
+
/** Its pathname. Default: read off `url` (a path-only `url` works too). */
|
|
3536
|
+
route?: string;
|
|
3537
|
+
/** Default `location.origin`. */
|
|
3538
|
+
origin?: string;
|
|
3539
|
+
/** `"prod"` / `"dev"` / `"local"` — the app's own reading (keksdose
|
|
3540
|
+
* `app/deploy-environment.ts`). Left out of the object when not given. */
|
|
3541
|
+
environment?: FeedbackEnvironment | (string & {});
|
|
3542
|
+
/** The app build (`__APP_VERSION__`). Left out of the object when not given. */
|
|
3543
|
+
version?: string;
|
|
3544
|
+
/** Default `${innerWidth}x${innerHeight}` at the time of the call — "406x816". */
|
|
3545
|
+
viewport?: string;
|
|
3546
|
+
/** Default `navigator.userAgent`. */
|
|
3547
|
+
ua?: string;
|
|
3548
|
+
}
|
|
3549
|
+
/**
|
|
3550
|
+
* The `context` object of `POST /feedback` (§3.2), keys in keksdose's order: `url`, `route`,
|
|
3551
|
+
* `origin`, `environment`, `user_id`, `user_email`, `user_display_name`, `viewport`, `ua`,
|
|
3552
|
+
* `version`.
|
|
3553
|
+
*
|
|
3554
|
+
* With `attachUrl: false`, `url` and `route` are `""` — present and empty, as keksdose
|
|
3555
|
+
* sends them, so a reader can tell "withheld" from "an old row without the key".
|
|
3556
|
+
* `environment` and `version` are omitted when the app passes none (kastlan had no
|
|
3557
|
+
* version), rather than sent as `undefined`.
|
|
3558
|
+
*
|
|
3559
|
+
* Reads `window` / `navigator` only for the defaults it was not given, so it is a pure
|
|
3560
|
+
* function in a test that passes everything.
|
|
3561
|
+
*/
|
|
3562
|
+
declare function feedbackContext(input: FeedbackContextInput): FeedbackContext;
|
|
3563
|
+
/**
|
|
3564
|
+
* `feedbackContext` — the context box's words. de-CH canon (keksdose `de-CH.json`):
|
|
3565
|
+
* `user` "Nutzer", `attachUrl` "Aktuelle Seiten-URL anhängen".
|
|
3566
|
+
*/
|
|
3567
|
+
interface FeedbackContextLabels {
|
|
3568
|
+
/** The submitter line's label — the box draws "**User:** Ada Example (ada@example.com)".
|
|
3569
|
+
* The label alone: the colon between it and the person is the language's
|
|
3570
|
+
* `common.fieldValue` (fr "Utilisateur : …", zh "用户:…"). */
|
|
3571
|
+
user: string;
|
|
3572
|
+
/** The checkbox. The URL it is about is the line beneath it. */
|
|
3573
|
+
attachUrl: string;
|
|
3574
|
+
}
|
|
3575
|
+
declare const DEFAULT_FEEDBACK_CONTEXT_LABELS: FeedbackContextLabels;
|
|
3576
|
+
interface FeedbackContextBoxProps {
|
|
3577
|
+
/** Who is filing — the same value handed to {@link feedbackContext}. */
|
|
3578
|
+
user?: FeedbackSubmitter | null;
|
|
3579
|
+
/** The URL that goes with the report — the same `url` handed to {@link feedbackContext}. */
|
|
3580
|
+
url: string;
|
|
3581
|
+
/** The checkbox, on by default in the dialog (§4.2) — the same `attachUrl`. */
|
|
3582
|
+
attachUrl: boolean;
|
|
3583
|
+
onAttachUrlChange: (next: boolean) => void;
|
|
3584
|
+
/** Prop > `<UiKitProvider labels={{ feedbackContext }}>` > English. */
|
|
3585
|
+
labels?: Partial<FeedbackContextLabels>;
|
|
3586
|
+
className?: string;
|
|
3587
|
+
}
|
|
3588
|
+
/**
|
|
3589
|
+
* The submit dialog's context box — the dialog's `contextSlot` (keksdose
|
|
3590
|
+
* `use-feedback-dialog.tsx:222`, §4.2): who is filing, and the page URL with a checkbox to
|
|
3591
|
+
* leave it out, ON by default.
|
|
3592
|
+
*
|
|
3593
|
+
* The URL stays visible when the box is off, struck through: the reporter sees what they
|
|
3594
|
+
* withheld rather than a line that vanished, and ticking it back on is the same glance.
|
|
3595
|
+
* Kurvenschmiede's argument for the checkbox is why it exists at all (its feedback #101):
|
|
3596
|
+
* the route is right for a bug and wrong for an idea about a screen one is not on — filed
|
|
3597
|
+
* against whatever screen happened to be open, the queue reads as if it were about that
|
|
3598
|
+
* screen.
|
|
3599
|
+
*
|
|
3600
|
+
* Nothing in here is the app's: hand it the values handed to {@link feedbackContext}, and
|
|
3601
|
+
* what the box shows is what is sent.
|
|
3602
|
+
*/
|
|
3603
|
+
declare function FeedbackContextBox({ user, url, attachUrl, onAttachUrlChange, labels: labelsProp, className, }: FeedbackContextBoxProps): react.JSX.Element;
|
|
3604
|
+
|
|
3605
|
+
/**
|
|
3606
|
+
* A status change that can be taken back — §4.5 of the feedback contract, lifted from
|
|
3607
|
+
* keksdose's `changeStatusUndoably` (`frontend/src/features/feedback/feedback-page.tsx`,
|
|
3608
|
+
* dev #587: *"Just clicked somewhere and a feedback row disappeared without knowing what
|
|
3609
|
+
* I set as new status."*).
|
|
3610
|
+
*
|
|
3611
|
+
* An inbox is usually filtered by status, so a row whose new status falls outside the
|
|
3612
|
+
* filter leaves the table the moment the change lands, with nothing on screen saying what
|
|
3613
|
+
* it became. The toast says it, names the report, and offers the old status back for 8 s
|
|
3614
|
+
* (the window keksdose's transactions Undo uses, feedback #184). Undo PATCHes the old
|
|
3615
|
+
* status and confirms that too. kastlan and Kurvenschmiede had no Undo at all.
|
|
3616
|
+
*/
|
|
3617
|
+
/** The one PATCH a status change is: `PATCH /feedback/{id}` with `{ status }`. */
|
|
3618
|
+
interface FeedbackStatusPatch {
|
|
3619
|
+
id: number;
|
|
3620
|
+
status: FeedbackStatus;
|
|
3621
|
+
}
|
|
3622
|
+
/**
|
|
3623
|
+
* The page's status mutation. **TanStack Query's `mutate` fits as it stands** — pass
|
|
3624
|
+
* `updateMutation.mutate` (stable across renders), not `mutateAsync`. A hand-written
|
|
3625
|
+
* one sends the PATCH and calls `onSuccess` once it has landed, and only then.
|
|
3626
|
+
*
|
|
3627
|
+
* Failure is the mutation's own business: its `onError` toasts the server's `detail`,
|
|
3628
|
+
* else `feedbackToast.updateFailed` (§4.4) — this hook adds no second error toast.
|
|
3629
|
+
*/
|
|
3630
|
+
type FeedbackStatusMutate = (patch: FeedbackStatusPatch, callbacks: {
|
|
3631
|
+
onSuccess: () => void;
|
|
3632
|
+
}) => void;
|
|
3633
|
+
/** What the change needs to know about the row: which one, what it is called in the
|
|
3634
|
+
* toast, and where it stood (the status Undo goes back to). */
|
|
3635
|
+
type FeedbackStatusRow = Pick<FeedbackRecord, "id" | "title" | "status">;
|
|
3636
|
+
/** Set `row` to `status`, with the Undo toast. A no-op when it is already there. */
|
|
3637
|
+
type FeedbackStatusChange = (row: FeedbackStatusRow, status: FeedbackStatus) => void;
|
|
3638
|
+
interface FeedbackStatusUndoOptions {
|
|
3639
|
+
/** Over the `feedbackToast` namespace (the toast's words). */
|
|
3640
|
+
labels?: Partial<FeedbackToastLabels>;
|
|
3641
|
+
/** Over the `feedbackStatus` namespace (the status names inside them). */
|
|
3642
|
+
statusLabels?: Partial<FeedbackStatusLabels>;
|
|
3643
|
+
}
|
|
3644
|
+
/** How long the Undo stays reachable: 8 s, keksdose's window (feedback #184). */
|
|
3645
|
+
declare const FEEDBACK_UNDO_DURATION = 8000;
|
|
3646
|
+
/**
|
|
3647
|
+
* The undoable status change — for the table's status cell, the row detail's pills and
|
|
3648
|
+
* the phone swipes alike (§4.5, §7.10: swipes included, Marcel 2026-10-04 — keksdose's
|
|
3649
|
+
* swipe commits go through its `changeStatusUndoably` too). Wire every one of them
|
|
3650
|
+
* through the function this returns:
|
|
3651
|
+
*
|
|
3652
|
+
* ```tsx
|
|
3653
|
+
* const update = useMutation({ mutationFn: api.updateFeedback, onError: … });
|
|
3654
|
+
* const changeStatus = useFeedbackStatusUndo(update.mutate);
|
|
3655
|
+
* <FeedbackStatusTransitions … onPick={(status) => changeStatus(row, status)} />
|
|
3656
|
+
* ```
|
|
3657
|
+
*
|
|
3658
|
+
* Give it the PAGE's mutation, not one a row owns: TanStack only calls per-call
|
|
3659
|
+
* callbacks while the observer that started the mutation is mounted, and a row the new
|
|
3660
|
+
* status filters out unmounts before the PATCH answers — the toast would never come.
|
|
3661
|
+
*
|
|
3662
|
+
* The flow: PATCH `{ id, status }` → on success a success toast
|
|
3663
|
+
* `statusChanged(newName, title)` with the action `statusUndo`, 8 s → Undo PATCHes
|
|
3664
|
+
* `{ id, status: previous }` → on success `statusRestored(previousName, title)`.
|
|
3665
|
+
*/
|
|
3666
|
+
declare function useFeedbackStatusUndo(mutate: FeedbackStatusMutate, { labels, statusLabels }?: FeedbackStatusUndoOptions): FeedbackStatusChange;
|
|
3667
|
+
|
|
3668
|
+
/**
|
|
3669
|
+
* The two feedback pages' TABLE — `/feedback` (the admin inbox) and `/my-feedback` (the
|
|
3670
|
+
* user's own) — as the kit's parts: §4.3 of the feedback contract
|
|
3671
|
+
* (docs/feedback-harmonization.md), lifted from keksdose
|
|
3672
|
+
* `frontend/src/features/feedback/feedback-page.tsx` (columns :711–963, the phone card
|
|
3673
|
+
* :1038–1065, the awaiting toggle :689–709 and :1086–1101).
|
|
3674
|
+
*
|
|
3675
|
+
* Until 0.27 each app wrote this table itself, and the three had drifted exactly where a
|
|
3676
|
+
* table drifts: kastlan's showed five statuses and no environment, Kurvenschmiede's one
|
|
3677
|
+
* page served both audiences, and keksdose's — the canon, argued over a year of feedback
|
|
3678
|
+
* items — had a latent bug of its own: its subject cell carries the environment and
|
|
3679
|
+
* rework chips "so a second attempt is marked on every surface at once" (:760), but the
|
|
3680
|
+
* phone card is a `mobileCard`, and `DataTable` never renders the `mobilePrimary` cell
|
|
3681
|
+
* when `mobileCard` is set (data-table.tsx, `cardInner`). Its phone cards showed neither
|
|
3682
|
+
* chip. {@link FeedbackMobileCard} draws both itself, from the same
|
|
3683
|
+
* {@link FeedbackSubject} the column uses, so the two surfaces cannot part again.
|
|
3684
|
+
*
|
|
3685
|
+
* What stays the app's: the rows (its API client), who is an admin, the routes and the
|
|
3686
|
+
* `/feedback` → `/my-feedback` redirect, the `?row=` deep link and the row detail. What
|
|
3687
|
+
* is here: the nine columns ({@link feedbackColumns} / {@link useFeedbackColumns}), the
|
|
3688
|
+
* phone card and its day headers ({@link FeedbackMobileCard},
|
|
3689
|
+
* {@link feedbackMobileGroupBy}), the phone's "Only what is waiting for you" toggle
|
|
3690
|
+
* ({@link FeedbackAwaitingToggle}), the empty state ({@link FeedbackEmptyState}) and the
|
|
3691
|
+
* `feedbackPage` words. The swipes are `feedbackSwipePlan` (feedback-swipe.tsx), passed
|
|
3692
|
+
* by the app because whether the page may write is the app's call.
|
|
3693
|
+
*/
|
|
3694
|
+
/**
|
|
3695
|
+
* `feedbackPage` — the words of the two feedback pages (§4.3). The English is keksdose's
|
|
3696
|
+
* `en.json` (`feedback.*`, `common.none`); the de-CH canon is noted on each key (keksdose
|
|
3697
|
+
* `de-CH.json`, ss never ß).
|
|
3698
|
+
*/
|
|
3699
|
+
interface FeedbackPageLabels {
|
|
3700
|
+
/** The admin inbox's page title, `/feedback`. de-CH: "Feedback" */
|
|
3701
|
+
title: string;
|
|
3702
|
+
/** The user's own list, `/my-feedback`. de-CH: "Mein Feedback" */
|
|
3703
|
+
myTitle: string;
|
|
3704
|
+
/** Column 1, the id. de-CH: "#" */
|
|
3705
|
+
columnId: string;
|
|
3706
|
+
/** Column 2, when it was filed. de-CH: "Datum" */
|
|
3707
|
+
columnDate: string;
|
|
3708
|
+
/** Column 3. de-CH: "Kategorie" */
|
|
3709
|
+
columnCategory: string;
|
|
3710
|
+
/** Column 4, the title with its chips. de-CH: "Betreff" */
|
|
3711
|
+
columnSubject: string;
|
|
3712
|
+
/** Column 5, who filed it. de-CH: "Nutzer" */
|
|
3713
|
+
columnUser: string;
|
|
3714
|
+
/** Column 6. de-CH: "E-Mail" */
|
|
3715
|
+
columnEmail: string;
|
|
3716
|
+
/** Column 7, the page it was filed from. de-CH: "URL" */
|
|
3717
|
+
columnUrl: string;
|
|
3718
|
+
/** Column 8. de-CH: "Status" */
|
|
3719
|
+
columnStatus: string;
|
|
3720
|
+
/** Column 9, when it was settled. de-CH: "Erledigt am" */
|
|
3721
|
+
columnResolved: string;
|
|
3722
|
+
/** What the URL cell's link does, said before the path to a screen reader.
|
|
3723
|
+
* de-CH: "Seite öffnen" */
|
|
3724
|
+
openPage: string;
|
|
3725
|
+
/** The table with nothing to show, on both pages (§7.13) — keksdose's `common.none`.
|
|
3726
|
+
* de-CH: "Keine" */
|
|
3727
|
+
empty: string;
|
|
3728
|
+
/** The submitter of a report whose account was erased (`user_id: null`, keksdose live
|
|
3729
|
+
* #275). de-CH: "<gelöschter Nutzer>" */
|
|
3730
|
+
deletedUser: string;
|
|
3731
|
+
/** The submitter when neither a name nor an email is known — `id` is `user_id`.
|
|
3732
|
+
* de-CH: "Nutzer #{{id}}" */
|
|
3733
|
+
userFallback: (id: number) => string;
|
|
3734
|
+
/** The chip for a report filed from a copy of the app that is not production —
|
|
3735
|
+
* `environment` is `context.environment` as filed ("dev", "local", or the app's own
|
|
3736
|
+
* word). Upper-cased like the top bar's environment badge, so the marker the tester saw
|
|
3737
|
+
* is the marker triage sees. Guarded: a context is untyped JSON, so a value that is no
|
|
3738
|
+
* string (an app calling it with `context.environment` as stored) is printed, never
|
|
3739
|
+
* thrown on. de-CH: the same. */
|
|
3740
|
+
environment: (environment: string) => string;
|
|
3741
|
+
/** The chip for a report sent back for rework — `count` ≥ 1 is how many times
|
|
3742
|
+
* (`reworkCount`, keksdose live #331). de-CH: "Nacharbeit" / "Nacharbeit ×{{count}}" */
|
|
3743
|
+
reworkChip: (count: number) => string;
|
|
3744
|
+
/** The phone toggle narrowing the inbox to IN_EVALUATION + NEEDS_LIVE_TEST.
|
|
3745
|
+
* de-CH: "Nur was auf Sie wartet" */
|
|
3746
|
+
awaitingFilter: string;
|
|
3747
|
+
/** The name of the phone's floating group that holds it. de-CH: "Feedback-Aktionen" */
|
|
3748
|
+
phoneActions: string;
|
|
3749
|
+
}
|
|
3750
|
+
declare const DEFAULT_FEEDBACK_PAGE_LABELS: FeedbackPageLabels;
|
|
3751
|
+
/** `feedbackPage` resolved: English, then `<UiKitProvider labels>`, then `labels`. The
|
|
3752
|
+
* page's title comes from here too: `mine ? labels.myTitle : labels.title`. */
|
|
3753
|
+
declare function useFeedbackPageLabels(labels?: Partial<FeedbackPageLabels>): FeedbackPageLabels;
|
|
3754
|
+
/** What the table reads off a report — {@link FeedbackRecord}'s fields, so an app's own
|
|
3755
|
+
* row type with more of them passes as it is. */
|
|
3756
|
+
type FeedbackTableRow = Pick<FeedbackRecord, "id" | "user_id" | "user_email" | "title" | "body" | "category" | "status" | "context" | "resolved_at" | "created_at">;
|
|
3757
|
+
/**
|
|
3758
|
+
* The environment a report was filed from when it is NOT production, as filed — or
|
|
3759
|
+
* `null` (keksdose `environmentLabel`, :142). Production is silent, as the top bar's
|
|
3760
|
+
* badge is: the marker earns attention by being rare. A missing value is production too —
|
|
3761
|
+
* every row filed before a test copy existed, and crash rows filed before the crash
|
|
3762
|
+
* payload carried `environment` (§3.2, §3.6). An unknown value is shown rather than swallowed: an environment
|
|
3763
|
+
* nobody recognises is exactly when triage needs to see one.
|
|
3764
|
+
*/
|
|
3765
|
+
declare function feedbackEnvironment(context: FeedbackContext | null | undefined): string | null;
|
|
3766
|
+
/**
|
|
3767
|
+
* Who filed a report, as one string (keksdose `submitterLabel`, :163): the display name,
|
|
3768
|
+
* else the email, else `userFallback(user_id)`. An erased account (`user_id: null`) is
|
|
3769
|
+
* `deletedUser` FIRST, ahead of the context: the server scrubs the name with the account,
|
|
3770
|
+
* but a row that somehow kept one must still not go on naming somebody who asked to be
|
|
3771
|
+
* forgotten. The joined `user_email` (admin list) is preferred to the context snapshot.
|
|
3772
|
+
*/
|
|
3773
|
+
declare function feedbackSubmitter(row: Pick<FeedbackRecord, "user_id" | "user_email" | "context">, labels: Pick<FeedbackPageLabels, "deletedUser" | "userFallback">): string;
|
|
3774
|
+
/**
|
|
3775
|
+
* The submitter's email (keksdose `submitterEmail`, :175): the joined `user_email` —
|
|
3776
|
+
* authoritative, the display name is self-chosen (feedback #108) — else the context
|
|
3777
|
+
* snapshot, which is what `/my-feedback` has (`GET /feedback/my` sends no join). `""`
|
|
3778
|
+
* for an erased account, for the reason {@link feedbackSubmitter} gives.
|
|
3779
|
+
*/
|
|
3780
|
+
declare function feedbackSubmitterEmail(row: Pick<FeedbackRecord, "user_id" | "user_email" | "context">): string;
|
|
3781
|
+
/** Whether a status is one the phone's awaiting toggle keeps — `FEEDBACK_AWAITING_STATUSES`,
|
|
3782
|
+
* the two that wait on the person triaging. `rows.filter((r) => isFeedbackAwaiting(r.status))`. */
|
|
3783
|
+
declare function isFeedbackAwaiting(status: FeedbackStatus): boolean;
|
|
3784
|
+
/**
|
|
3785
|
+
* How the pages write a date: an ISO timestamp in, one string out. keksdose passes its
|
|
3786
|
+
* `useDateCell()` (the app's date-format preference, weekday and all — dev#546), which
|
|
3787
|
+
* fits as it is.
|
|
3788
|
+
*
|
|
3789
|
+
* ONE function for the date column, the resolved column, the phone card and the phone's
|
|
3790
|
+
* day headers, so the four cannot disagree (keksdose dev#546 round 3: the header is a
|
|
3791
|
+
* string and must say what the card says).
|
|
3792
|
+
*/
|
|
3793
|
+
type FeedbackRenderDate = (iso: string) => string;
|
|
3794
|
+
/**
|
|
3795
|
+
* `renderDate` when the app gave one, else the kit's: the `<UiKitProvider formatDate>`
|
|
3796
|
+
* when the app set one (called with the LOCAL calendar day and `weekday: true`, as
|
|
3797
|
+
* `DateMark` calls it), else `Intl` in the provider's locale with the weekday. An
|
|
3798
|
+
* unparseable value is passed through rather than hidden.
|
|
3799
|
+
*/
|
|
3800
|
+
declare function useFeedbackRenderDate(renderDate?: FeedbackRenderDate, locale?: string): FeedbackRenderDate;
|
|
3801
|
+
interface FeedbackSubjectProps {
|
|
3802
|
+
row: Pick<FeedbackRecord, "title" | "body" | "context">;
|
|
3803
|
+
/**
|
|
3804
|
+
* `false` (default): one line — the title truncates and the chips stay whole, as a
|
|
3805
|
+
* table cell wants. `true`: the title wraps and the chips flow after its last word, as
|
|
3806
|
+
* the phone card wants (a phone shows the whole title or nothing useful).
|
|
3807
|
+
*/
|
|
3808
|
+
wrap?: boolean;
|
|
3809
|
+
/** Over the `feedbackPage` namespace. */
|
|
3810
|
+
labels?: Partial<FeedbackPageLabels>;
|
|
3811
|
+
className?: string;
|
|
3812
|
+
}
|
|
3813
|
+
/**
|
|
3814
|
+
* A report's title with its two chips (§4.3, column 4): the environment when it is not
|
|
3815
|
+
* production (keksdose's amber outline, the top bar's environment badge — the marker the
|
|
3816
|
+
* tester saw is the marker triage sees) and **Rework** / **Rework ×n** when the author has
|
|
3817
|
+
* sent it back (keksdose live #331: *"have a new category for items in rework"*, as a fact
|
|
3818
|
+
* derived from the body rather than a seventh status).
|
|
3819
|
+
*
|
|
3820
|
+
* No tooltip on either chip (keksdose dev#523): each already says what it means.
|
|
3821
|
+
*/
|
|
3822
|
+
declare function FeedbackSubject({ row, wrap, labels: labelsProp, className }: FeedbackSubjectProps): react.JSX.Element;
|
|
3823
|
+
/** What {@link feedbackColumns} takes — every word already resolved (see
|
|
3824
|
+
* {@link useFeedbackColumns}, which resolves them from the provider). */
|
|
3825
|
+
interface FeedbackColumnsOptions {
|
|
3826
|
+
/** `/my-feedback`. The same nine columns (one page component, §4.3), but the status
|
|
3827
|
+
* cell is read-only there whatever `canEdit` says — "no status or outcome editing
|
|
3828
|
+
* there (also for an admin)". */
|
|
3829
|
+
mine?: boolean;
|
|
3830
|
+
/** The admin on `/feedback` — the status cell offers its steps. Otherwise it shows them
|
|
3831
|
+
* read-only. */
|
|
3832
|
+
canEdit: boolean;
|
|
3833
|
+
/** The undoable status change, `useFeedbackStatusUndo(update.mutate)` (§4.5) — the
|
|
3834
|
+
* same one the row detail and the swipes use. */
|
|
3835
|
+
onStatus: FeedbackStatusChange;
|
|
3836
|
+
/** See {@link FeedbackRenderDate}. */
|
|
3837
|
+
renderDate: FeedbackRenderDate;
|
|
3838
|
+
/** The status buttons SAVE when pressed: pass `true` to put them under the page's
|
|
3839
|
+
* `WriteLockProvider` (FeedbackStatusTransitions' `commit`). Off by default — a
|
|
3840
|
+
* feedback inbox is rarely what an app's lock is about. */
|
|
3841
|
+
commit?: boolean;
|
|
3842
|
+
labels: FeedbackPageLabels;
|
|
3843
|
+
statusLabels: FeedbackStatusLabels;
|
|
3844
|
+
categoryLabels: FeedbackCategoryLabels;
|
|
3845
|
+
}
|
|
3846
|
+
/**
|
|
3847
|
+
* The nine columns of §4.3, in the contract's order, for `DataTable` — keksdose's
|
|
3848
|
+
* (:711–963) with the contract's changes:
|
|
3849
|
+
*
|
|
3850
|
+
* | # | key | cell | filter | phone |
|
|
3851
|
+
* |---|---|---|---|---|
|
|
3852
|
+
* | 1 | `id` | id, mono | number | hidden |
|
|
3853
|
+
* | 2 | `date` | `created_at`, weekday ({@link FeedbackRenderDate}) | date | |
|
|
3854
|
+
* | 3 | `category` | `FeedbackCategoryBadge`, sorted CRASH first | select, all five | |
|
|
3855
|
+
* | 4 | `title` | {@link FeedbackSubject} | text: title + environment | card heading |
|
|
3856
|
+
* | 5 | `user` | {@link feedbackSubmitter} | text: name + email, or "deleted user" | |
|
|
3857
|
+
* | 6 | `email` | {@link feedbackSubmitterEmail} | text | hidden |
|
|
3858
|
+
* | 7 | `url` | the page, a link (`noRowLink`) | text: path | hidden |
|
|
3859
|
+
* | 8 | `status` | `FeedbackStatusTransitions variant="icon"`, `visibleFeedbackStatuses` | select, chain order | |
|
|
3860
|
+
* | 9 | `resolved` | `resolved_at` | date | hidden |
|
|
3861
|
+
*
|
|
3862
|
+
* - Both select filters list the WHOLE vocabulary in its own order, translated —
|
|
3863
|
+
* without `options` the popover lists the bare enum values found in the rows
|
|
3864
|
+
* (keksdose feedback #160), and kastlan's first CRASH row would otherwise appear in a
|
|
3865
|
+
* filter that never offered it.
|
|
3866
|
+
* - The status column owns the status filter, and only it (keksdose live #330's revert:
|
|
3867
|
+
* the phone's filter sheet already exposes every column filter).
|
|
3868
|
+
* - `/my-feedback` shows the same nine: the Email cell's context fallback exists for it
|
|
3869
|
+
* (the admin list's join is absent there, §3.1), and its phone card drops the submitter
|
|
3870
|
+
* instead ({@link FeedbackMobileCardProps.showSubmitter}).
|
|
3871
|
+
*
|
|
3872
|
+
* Pure: hand it resolved words, or use {@link useFeedbackColumns}. Keys are stable, so
|
|
3873
|
+
* an app may drop or add a column (`columns.filter((c) => c.key !== "email")`).
|
|
3874
|
+
*/
|
|
3875
|
+
declare function feedbackColumns<T extends FeedbackTableRow>({ mine, canEdit, onStatus, renderDate, commit, labels, statusLabels, categoryLabels, }: FeedbackColumnsOptions): DataTableColumn<T>[];
|
|
3876
|
+
/** What {@link useFeedbackColumns} takes: {@link FeedbackColumnsOptions} with the words
|
|
3877
|
+
* read from the provider, and `renderDate` optional. */
|
|
3878
|
+
interface UseFeedbackColumnsOptions extends Omit<FeedbackColumnsOptions, "renderDate" | "labels" | "statusLabels" | "categoryLabels"> {
|
|
3879
|
+
/** Default: the kit's date with the weekday ({@link useFeedbackRenderDate}). */
|
|
3880
|
+
renderDate?: FeedbackRenderDate;
|
|
3881
|
+
/** Over the `feedbackPage` namespace. Keep it stable (module scope or memoised): it is
|
|
3882
|
+
* a dependency of the memo. */
|
|
3883
|
+
labels?: Partial<FeedbackPageLabels>;
|
|
3884
|
+
}
|
|
3885
|
+
/**
|
|
3886
|
+
* {@link feedbackColumns} with the `feedbackPage`, `feedbackStatus` and
|
|
3887
|
+
* `feedbackCategory` words from the provider, memoised on what it reads — pass
|
|
3888
|
+
* `update.mutate` through `useFeedbackStatusUndo` (stable) rather than a fresh function
|
|
3889
|
+
* per render, or every row's columns are rebuilt on each render (keksdose :667).
|
|
3890
|
+
*/
|
|
3891
|
+
declare function useFeedbackColumns<T extends FeedbackTableRow>({ mine, canEdit, onStatus, renderDate: renderDateProp, commit, labels: labelsProp, }: UseFeedbackColumnsOptions): DataTableColumn<T>[];
|
|
3892
|
+
/**
|
|
3893
|
+
* The phone list's day headers (keksdose feedback #325: the list "grouped by date, like
|
|
3894
|
+
* the transactions list"): `DataTable mobileGroupBy={feedbackMobileGroupBy(renderDate)}`.
|
|
3895
|
+
*
|
|
3896
|
+
* The key IS the header — the same string the card's date says. DataTable groups runs of
|
|
3897
|
+
* equal keys, so the rows must come newest first, as `GET /feedback` and
|
|
3898
|
+
* `GET /feedback/my` send them (§3.3).
|
|
3899
|
+
*/
|
|
3900
|
+
declare function feedbackMobileGroupBy(renderDate: FeedbackRenderDate): (row: Pick<FeedbackRecord, "created_at">) => string;
|
|
3901
|
+
interface FeedbackMobileCardProps {
|
|
3902
|
+
row: FeedbackTableRow;
|
|
3903
|
+
/** Add the submitter after the date — the admin inbox (`canEdit`). On `/my-feedback`
|
|
3904
|
+
* every row is the viewer's own and the name would repeat down the list. */
|
|
3905
|
+
showSubmitter?: boolean;
|
|
3906
|
+
/** See {@link FeedbackRenderDate}; the same function as the columns' and the day
|
|
3907
|
+
* headers'. Default: the kit's ({@link useFeedbackRenderDate}). */
|
|
3908
|
+
renderDate?: FeedbackRenderDate;
|
|
3909
|
+
/** Over the `feedbackPage` namespace. */
|
|
3910
|
+
labels?: Partial<FeedbackPageLabels>;
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* One report as a phone card (§4.3; keksdose :1042, feedback #325 rework: *"compact card
|
|
3914
|
+
* instead of a wasteful label/value row per field"*): the title with its environment and
|
|
3915
|
+
* rework chips and the status badge on one line, then the compact category badge, the
|
|
3916
|
+
* date and (admin) the submitter beneath.
|
|
3917
|
+
*
|
|
3918
|
+
* `DataTable mobileCard={(row) => <FeedbackMobileCard row={row} … />}`. It draws the
|
|
3919
|
+
* chips itself: with a `mobileCard`, DataTable never renders the `mobilePrimary` cell,
|
|
3920
|
+
* which is how keksdose's cards lost them.
|
|
3921
|
+
*/
|
|
3922
|
+
declare function FeedbackMobileCard({ row, showSubmitter, renderDate: renderDateProp, labels: labelsProp }: FeedbackMobileCardProps): react.JSX.Element;
|
|
3923
|
+
interface FeedbackAwaitingToggleProps {
|
|
3924
|
+
/** Narrowed to what waits on the triager. Page state, not the URL (keksdose :699): a
|
|
3925
|
+
* thumb gesture on one screen, not a view worth linking to. */
|
|
3926
|
+
pressed: boolean;
|
|
3927
|
+
onPressedChange: (pressed: boolean) => void;
|
|
3928
|
+
/** Further `FloatingAction`s, after the toggle — the group is what makes them free. */
|
|
3929
|
+
children?: ReactNode;
|
|
3930
|
+
/** Default `bottom-end`, the register's corner. */
|
|
3931
|
+
corner?: FloatingCorner;
|
|
3932
|
+
/** Over the group's default distance from the bottom. */
|
|
3933
|
+
offset?: string;
|
|
3934
|
+
/** On the group. It is `md:hidden` already: the desktop has the status column's
|
|
3935
|
+
* filter, the same question asked where there is room for it. */
|
|
3936
|
+
className?: string;
|
|
3937
|
+
/** Over the `feedbackPage` namespace. */
|
|
3938
|
+
labels?: Partial<FeedbackPageLabels>;
|
|
3939
|
+
}
|
|
3940
|
+
/**
|
|
3941
|
+
* The phone inbox's one filter button — **"Only what is waiting for you"** (§4.3;
|
|
3942
|
+
* keksdose live #330 rework: *"a button that toggles the evaluation items (in evaluation,
|
|
3943
|
+
* test when live) as toggle button similar to the show/hide upcoming/scheduled for the tx
|
|
3944
|
+
* page"*).
|
|
3945
|
+
*
|
|
3946
|
+
* It narrows TO `FEEDBACK_AWAITING_STATUSES` rather than hiding them: after a loop run
|
|
3947
|
+
* those ARE the queue — the rows handed back for a verdict — and everything else is
|
|
3948
|
+
* history. A `FloatingActionGroup` with one member anyway, in the register's corner and
|
|
3949
|
+
* shape (keksdose feedback #58), so a second action costs nothing; the glyph is
|
|
3950
|
+
* IN_EVALUATION's own, so the button reads in the vocabulary of the badges it filters on.
|
|
3951
|
+
*
|
|
3952
|
+
* Admin inbox only: on `/my-feedback` the author is not waiting on their own verdict.
|
|
3953
|
+
* The filtering is the page's — `rows.filter((r) => isFeedbackAwaiting(r.status))` —
|
|
3954
|
+
* so the table's own filters, sort and URL stay untouched.
|
|
3955
|
+
*/
|
|
3956
|
+
declare function FeedbackAwaitingToggle({ pressed, onPressedChange, children, corner, offset, className, labels: labelsProp, }: FeedbackAwaitingToggleProps): react.JSX.Element;
|
|
3957
|
+
interface FeedbackEmptyStateProps {
|
|
3958
|
+
/** Over the `feedbackPage` namespace. */
|
|
3959
|
+
labels?: Partial<FeedbackPageLabels>;
|
|
3960
|
+
}
|
|
3961
|
+
/**
|
|
3962
|
+
* The table with nothing to show (§7.13): the one word **None** / **Keine**, on both
|
|
3963
|
+
* pages — keksdose's `empty={t("common.none")}` (`feedback-page.tsx:982`). No hint
|
|
3964
|
+
* beneath it: where reports are sent from is the feedback menu's business, and the inbox
|
|
3965
|
+
* needs no sentence to say it is empty.
|
|
3966
|
+
*
|
|
3967
|
+
* For `DataTable empty`, which sits in a centred, muted cell — so no box of its own.
|
|
3968
|
+
* keksdose passes it unconditionally, so a filter (or the awaiting toggle) that leaves
|
|
3969
|
+
* nothing says "None" too: `empty={<FeedbackEmptyState />}`.
|
|
3970
|
+
*/
|
|
3971
|
+
declare function FeedbackEmptyState({ labels: labelsProp }: FeedbackEmptyStateProps): react.JSX.Element;
|
|
3972
|
+
|
|
3973
|
+
/**
|
|
3974
|
+
* One report, opened — the panel a feedback table unfolds under its row (§4.4 of
|
|
3975
|
+
* docs/feedback-harmonization.md), lifted from keksdose's `FeedbackRow`
|
|
3976
|
+
* (`frontend/src/features/feedback/feedback-page.tsx:329-612`) with the contract's
|
|
3977
|
+
* changes applied.
|
|
3978
|
+
*
|
|
3979
|
+
* Until 0.27 each app wrote this panel itself out of the kit's shell
|
|
3980
|
+
* (`FeedbackDetail` / `FeedbackDetailSection` / `FeedbackNoteEditor`), and the three
|
|
3981
|
+
* copies had drifted in exactly the places a user notices:
|
|
3982
|
+
*
|
|
3983
|
+
* - **the description editor held the raw body** — keksdose `initial={fb.body}`
|
|
3984
|
+
* (`:420`), kastlan the same (`pages/feedback-page.tsx:226`) — so an author fixing a
|
|
3985
|
+
* typo could delete the `--- REWORK … ---` rounds and the `[screenshot]` file lines
|
|
3986
|
+
* with it, and the history was gone. Here the editor holds the ORIGINAL description
|
|
3987
|
+
* only ({@link splitDescription}) and saves through {@link replaceDescription}, which
|
|
3988
|
+
* puts every appended block and file line back unchanged;
|
|
3989
|
+
* - **kastlan's rework sent `status: "OPEN"`** from the client (`:189-195`) and offered it
|
|
3990
|
+
* on three statuses; keksdose sends the body alone and lets the server reopen the row
|
|
3991
|
+
* on five (live #331). The contract is keksdose's (§3.4) — {@link appendRework}, one
|
|
3992
|
+
* file, the server reopens;
|
|
3993
|
+
* - **kastlan showed only `screenshot_url`** (`:265-276`) and no downloads; keksdose the
|
|
3994
|
+
* screenshot, the other files and the rework pictures, a PDF as a download (dev #578);
|
|
3995
|
+
* - **the URL fell away on Kurvenschmiede's rows**, whose backfilled `context.url` is a
|
|
3996
|
+
* path (`/accounts?p=2`): keksdose's `new URL(…)` throws on one and the section
|
|
3997
|
+
* vanished. kastlan already fell back to `context.route` (`feedbackRaw`, `:97`).
|
|
3998
|
+
*
|
|
3999
|
+
* The sections, in the contract's order: **Description** (the body; the author's
|
|
4000
|
+
* Edit while OPEN / IN_PROGRESS), **URL**, **Attachment**, **Outcome** (the admin's
|
|
4001
|
+
* outcome editor, Rework, Open page), **Send for rework** (only while reworking) and
|
|
4002
|
+
* **Status**. No comment thread (§2.2: kastlan's comments fold into the body, §7.7).
|
|
4003
|
+
*
|
|
4004
|
+
* The app keeps its data and its API: the row it renders, the PATCH (`onUpdate`), the
|
|
4005
|
+
* upload (`onUpload`), the authenticated GET (`fetcher`) and who is looking (`canEdit`,
|
|
4006
|
+
* `viewerId`). Every word is the kit's `feedbackDetail` namespace.
|
|
4007
|
+
*/
|
|
4008
|
+
/**
|
|
4009
|
+
* `feedbackDetail` — the row detail's words (§4.4). English: keksdose's `en.json`
|
|
4010
|
+
* (`feedback.*`, `common.*`, `more.copy_url`) with the contract's changes; the de-CH
|
|
4011
|
+
* canon (keksdose `de-CH.json`, ss never ß, "Nacharbeit" throughout — §7.14) on each key.
|
|
4012
|
+
*/
|
|
4013
|
+
interface FeedbackDetailLabels {
|
|
4014
|
+
/** Section 1's heading — keksdose's `feedback.body` (§7.3). The dialog's field asks
|
|
4015
|
+
* "What happened? (optional)"; the description, once filed, is headed as what it is
|
|
4016
|
+
* — and as the author's "Edit description" calls it. de-CH: "Beschreibung" */
|
|
4017
|
+
body: string;
|
|
4018
|
+
/** The author's button on that heading. de-CH: "Bearbeiten" */
|
|
4019
|
+
edit: string;
|
|
4020
|
+
/** Its accessible name — says WHAT is edited; contains the visible word.
|
|
4021
|
+
* de-CH: "Beschreibung bearbeiten" */
|
|
4022
|
+
editDescription: string;
|
|
4023
|
+
/** The editors' save button (description, outcome). de-CH: "Speichern" */
|
|
4024
|
+
save: string;
|
|
4025
|
+
/** Every editor's cancel button. de-CH: "Abbrechen" */
|
|
4026
|
+
cancel: string;
|
|
4027
|
+
/** Section 2's heading. de-CH: "URL" */
|
|
4028
|
+
url: string;
|
|
4029
|
+
/** The copy button beside the link — copies the FULL `context.url`.
|
|
4030
|
+
* de-CH: "URL kopieren" */
|
|
4031
|
+
copyUrl: string;
|
|
4032
|
+
/** Section 3's heading, and each picture's alt text. de-CH: "Anhang" */
|
|
4033
|
+
attachment: string;
|
|
4034
|
+
/** A PDF's / text file's download button, given the file's name.
|
|
4035
|
+
* de-CH: "{{name}} herunterladen" */
|
|
4036
|
+
download: (name: string) => string;
|
|
4037
|
+
/** The toast when that download failed. de-CH: "Der Anhang konnte nicht
|
|
4038
|
+
* heruntergeladen werden." */
|
|
4039
|
+
downloadFailed: string;
|
|
4040
|
+
/** Section 4's heading. de-CH: "Ergebnis" */
|
|
4041
|
+
outcome: string;
|
|
4042
|
+
/** On section 4's heading line once the row is settled, given the formatted
|
|
4043
|
+
* `resolved_at` date. de-CH: "Erledigt am: {{date}}" */
|
|
4044
|
+
resolvedAt: (date: string) => string;
|
|
4045
|
+
/** The admin's button while there is no outcome. de-CH: "Ergebnis hinzufügen" */
|
|
4046
|
+
outcomeAdd: string;
|
|
4047
|
+
/** The same button once there is one. de-CH: "Aktualisieren" */
|
|
4048
|
+
outcomeUpdate: string;
|
|
4049
|
+
/** The line above the outcome editor. de-CH: "Was wurde umgesetzt, entschieden oder
|
|
4050
|
+
* warum nicht." */
|
|
4051
|
+
outcomePlaceholder: string;
|
|
4052
|
+
/** The button that opens section 5 on an answered row. de-CH: "Nacharbeit" */
|
|
4053
|
+
rework: string;
|
|
4054
|
+
/** The link to the page the report was filed on. de-CH: "Seite öffnen" */
|
|
4055
|
+
openPage: string;
|
|
4056
|
+
/** Section 5's heading. de-CH: "Zur Nacharbeit senden" */
|
|
4057
|
+
reworkTitle: string;
|
|
4058
|
+
/** Section 5's send button. de-CH: "Nacharbeit senden" */
|
|
4059
|
+
reworkSend: string;
|
|
4060
|
+
/** The line above the rework note. de-CH: "Was muss noch angepasst werden? Neue
|
|
4061
|
+
* Anforderungen oder Richtungswechsel." */
|
|
4062
|
+
reworkPlaceholder: string;
|
|
4063
|
+
/** The toast when the rework's file did not upload — and so nothing was sent. Names
|
|
4064
|
+
* no screenshot: it fires for a PDF or a log too (§7.14; keksdose's said
|
|
4065
|
+
* "screenshot"). de-CH: "Die Datei konnte nicht hochgeladen werden. Die Nacharbeit
|
|
4066
|
+
* wurde nicht gesendet." */
|
|
4067
|
+
reworkUploadFailed: string;
|
|
4068
|
+
/** Section 6's heading. de-CH: "Status" */
|
|
4069
|
+
status: string;
|
|
4070
|
+
}
|
|
4071
|
+
declare const DEFAULT_FEEDBACK_DETAIL_LABELS: FeedbackDetailLabels;
|
|
4072
|
+
/** `feedbackDetail` resolved: English, then `<UiKitProvider labels>`, then `labels`. */
|
|
4073
|
+
declare function useFeedbackDetailLabels(labels?: Partial<FeedbackDetailLabels>): FeedbackDetailLabels;
|
|
4074
|
+
/**
|
|
4075
|
+
* The in-app link to the page a report was filed on — path + query + hash (keksdose
|
|
4076
|
+
* `localHref`: the query and hash carry the state worth restoring, `?tab=import`, a
|
|
4077
|
+
* drill-down). Read from `context.url`, else `context.route`; a path-only `url` works.
|
|
4078
|
+
* Always a path on THIS app, never another origin's address, so it is safe as an
|
|
4079
|
+
* `href` whatever the row says. `null` when there is nothing to link.
|
|
4080
|
+
*/
|
|
4081
|
+
declare function feedbackPageHref(context: FeedbackContext | null | undefined): string | null;
|
|
4082
|
+
/** The page's path alone — the compact text a URL column shows (§4.3). */
|
|
4083
|
+
declare function feedbackPagePath(context: FeedbackContext | null | undefined): string | null;
|
|
4084
|
+
/**
|
|
4085
|
+
* What "Copy URL" copies: the FULL `context.url` as the reporter's browser had it —
|
|
4086
|
+
* origin included, which is the one part that says which copy of the app it was
|
|
4087
|
+
* (feedback #278) — else `context.route`. `null` when there is no page.
|
|
4088
|
+
*/
|
|
4089
|
+
declare function feedbackPageUrl(context: FeedbackContext | null | undefined): string | null;
|
|
4090
|
+
/**
|
|
4091
|
+
* Every file a report carries, in the order the detail shows them (§4.4.3): the
|
|
4092
|
+
* screenshot it was filed with, then its other files, then whatever the rework rounds
|
|
4093
|
+
* brought, in body order (keksdose dev #578, live #291). One list rather than three —
|
|
4094
|
+
* a reader looking for "the picture" should not have to know at which point in the
|
|
4095
|
+
* conversation it was taken.
|
|
4096
|
+
*
|
|
4097
|
+
* Each URL once: keksdose and Kurvenschmiede content-address their keys, so the same
|
|
4098
|
+
* screenshot pasted twice is one URL — shown twice it is the same picture twice (and,
|
|
4099
|
+
* keyed by URL, a React key clash keksdose had).
|
|
4100
|
+
*/
|
|
4101
|
+
declare function feedbackAttachmentUrls(row: Pick<FeedbackRecord, "screenshot_url" | "attachment_urls" | "body">): string[];
|
|
4102
|
+
/** What the person looking at a row may do with it — {@link feedbackRowAccess}. */
|
|
4103
|
+
interface FeedbackRowAccess {
|
|
4104
|
+
/** The viewer filed it (`viewerId === row.user_id`; never for an erased author). */
|
|
4105
|
+
isAuthor: boolean;
|
|
4106
|
+
/** "Edit description": the author, while the row is OPEN or IN_PROGRESS — on either
|
|
4107
|
+
* page (keksdose `canAuthorEdit`, `feedback-page.tsx:398`, is not gated on `mine`). */
|
|
4108
|
+
canEditDescription: boolean;
|
|
4109
|
+
/** The outcome editor: the admin on the inbox (`canEdit`). */
|
|
4110
|
+
canEditOutcome: boolean;
|
|
4111
|
+
/** "Rework": the admin or the author, on an answered row
|
|
4112
|
+
* ({@link FEEDBACK_REWORKABLE_STATUSES}) — §3.4, anyone else gets the server's 403. */
|
|
4113
|
+
canRework: boolean;
|
|
4114
|
+
/** The status pills: the admin on the inbox; everyone else sees them read-only. */
|
|
4115
|
+
canChangeStatus: boolean;
|
|
4116
|
+
}
|
|
4117
|
+
/**
|
|
4118
|
+
* The detail's permission rules in one place — the same answers the server gives
|
|
4119
|
+
* (§3.4), so the panel never offers what the PATCH would refuse.
|
|
4120
|
+
*
|
|
4121
|
+
* `canEdit` is the app's "admin on `/feedback`": an admin on `/my-feedback` edits no
|
|
4122
|
+
* status and no outcome there (§4.3), but as the author still edits the description
|
|
4123
|
+
* and sends for rework. `viewerId` is the signed-in user's id.
|
|
4124
|
+
*/
|
|
4125
|
+
declare function feedbackRowAccess(row: Pick<FeedbackRecord, "user_id" | "status">, { canEdit, viewerId }: {
|
|
4126
|
+
canEdit: boolean;
|
|
4127
|
+
viewerId?: number | null;
|
|
4128
|
+
}): FeedbackRowAccess;
|
|
4129
|
+
/** What the detail sends as `PATCH /feedback/{id}` (§3.4): the body (a description
|
|
4130
|
+
* edit, a rework append) or the outcome — and, through {@link useFeedbackStatusUndo}
|
|
4131
|
+
* when no `statusChange` is given, the status. Never two at once: a rework PATCH must
|
|
4132
|
+
* carry the body ALONE for the server to read it as an append (keksdose live #331). */
|
|
4133
|
+
interface FeedbackDetailPatch {
|
|
4134
|
+
id: number;
|
|
4135
|
+
body?: string;
|
|
4136
|
+
/** `null` clears it — a real edit. */
|
|
4137
|
+
outcome?: string | null;
|
|
4138
|
+
status?: FeedbackStatus;
|
|
4139
|
+
}
|
|
4140
|
+
/**
|
|
4141
|
+
* The page's update mutation. **TanStack Query's `mutate` fits as it stands** — pass
|
|
4142
|
+
* `update.mutate` (and `pending={update.isPending}`), the same function
|
|
4143
|
+
* `useFeedbackStatusUndo` takes. A hand-written one sends the PATCH and calls
|
|
4144
|
+
* `onSuccess` once it has landed, and only then: that is when an editor closes. A
|
|
4145
|
+
* failure is the mutation's own to toast — the server's `detail`, else
|
|
4146
|
+
* `feedbackToast.updateFailed` (§4.4) — and leaves the editor open with its draft.
|
|
4147
|
+
*/
|
|
4148
|
+
type FeedbackDetailUpdate = (patch: FeedbackDetailPatch, callbacks: {
|
|
4149
|
+
onSuccess: () => void;
|
|
4150
|
+
}) => void;
|
|
4151
|
+
/** `POST /feedback/attachments` (§3.3): one file up, its URL
|
|
4152
|
+
* (`/api/v1/feedback/attachments/<key>`) back. Reject on failure. */
|
|
4153
|
+
type FeedbackUpload = (file: File) => Promise<string>;
|
|
4154
|
+
interface FeedbackReworkSectionProps {
|
|
4155
|
+
/** The row being sent back: its id, and the body the round is appended to. */
|
|
4156
|
+
row: Pick<FeedbackRecord, "id" | "body">;
|
|
4157
|
+
/** The PATCH — see {@link FeedbackDetailUpdate}. Called with `{ id, body }` only. */
|
|
4158
|
+
onUpdate: FeedbackDetailUpdate;
|
|
4159
|
+
/** The PATCH is in flight (`update.isPending`). The upload adds its own. */
|
|
4160
|
+
pending?: boolean;
|
|
4161
|
+
/** The upload. Left out, the note goes without a file field. */
|
|
4162
|
+
onUpload?: FeedbackUpload;
|
|
4163
|
+
/** Snapshot the app view as a File — the kit's `captureAppScreenshot`, or the app's
|
|
4164
|
+
* own. A "Capture screenshot" button appears only with it; a rejection (no
|
|
4165
|
+
* `modern-screenshot`, a canvas that would not draw) toasts
|
|
4166
|
+
* `feedbackToast.captureFailed` and adds nothing. */
|
|
4167
|
+
onCaptureScreenshot?: () => Promise<File | null>;
|
|
4168
|
+
/** The round landed — the server has the note and, on an answered row, reopened it. */
|
|
4169
|
+
onSent?: () => void;
|
|
4170
|
+
onCancel: () => void;
|
|
4171
|
+
/** Over the field's defaults: the contract's types and 10 MB (§3.5), the
|
|
4172
|
+
* `feedbackToast` refusals, the `feedbackAttachment` words. */
|
|
4173
|
+
attachment?: Partial<FeedbackNoteAttachment>;
|
|
4174
|
+
/** The send COMMITS — see `FeedbackNoteEditor`'s `commit`. */
|
|
4175
|
+
commit?: boolean;
|
|
4176
|
+
labels?: Partial<FeedbackDetailLabels>;
|
|
4177
|
+
}
|
|
4178
|
+
/**
|
|
4179
|
+
* The rework editor (§4.4.5): the submitter — or the admin — sends an answered report
|
|
4180
|
+
* back with a note and at most one file, and the server reopens it (§3.4, keksdose
|
|
4181
|
+
* `submitRework`, `feedback-page.tsx:350`).
|
|
4182
|
+
*
|
|
4183
|
+
* - **The note is required** and trimmed: Send stays disabled on a blank box (a file
|
|
4184
|
+
* alone cannot be sent; keksdose `:577` dropped that click silently).
|
|
4185
|
+
* - **One file**, added, captured or pasted — the dialog's types and 10 MB. It is
|
|
4186
|
+
* uploaded FIRST and awaited: a body naming a file that never arrived would be a
|
|
4187
|
+
* rework whose evidence is missing, and the upload is the half that fails on its
|
|
4188
|
+
* own. If it fails nothing is sent and the toast says so; the note stays in the box.
|
|
4189
|
+
* - **The body alone goes in the PATCH** — {@link appendRework}'s strict extension of
|
|
4190
|
+
* the current body, which is how the server recognises the append and reopens the
|
|
4191
|
+
* row. Never a `status` (kastlan's client sent `OPEN`; an author may not write one,
|
|
4192
|
+
* so the server refused the whole PATCH, note and all — keksdose live #331).
|
|
4193
|
+
*
|
|
4194
|
+
* Exported on its own for an app that offers rework somewhere other than the row
|
|
4195
|
+
* detail. Kurvenschmiede's author verdict ("That is it, done") is NOT here: an answered
|
|
4196
|
+
* item stays answered unless its author sends it back (§7.2).
|
|
4197
|
+
*/
|
|
4198
|
+
declare function FeedbackReworkSection({ row, onUpdate, pending, onUpload, onCaptureScreenshot, onSent, onCancel, attachment, commit, labels: labelsProp, }: FeedbackReworkSectionProps): react.JSX.Element;
|
|
4199
|
+
interface FeedbackRowDetailProps {
|
|
4200
|
+
/** The report — the list's own row, as `GET /feedback` / `GET /feedback/my` sent it. */
|
|
4201
|
+
row: FeedbackRecord;
|
|
4202
|
+
/** The admin on the inbox (`/feedback`): status pills and the outcome editor. `false`
|
|
4203
|
+
* on `/my-feedback`, for an admin too (§4.3). */
|
|
4204
|
+
canEdit: boolean;
|
|
4205
|
+
/** The signed-in user's id — who the AUTHOR is: the description editor and rework. */
|
|
4206
|
+
viewerId?: number | null;
|
|
4207
|
+
/** The page's PATCH — `update.mutate` (see {@link FeedbackDetailUpdate}). */
|
|
4208
|
+
onUpdate: FeedbackDetailUpdate;
|
|
4209
|
+
/** `update.isPending`: the editors' save buttons wait for it. */
|
|
4210
|
+
pending?: boolean;
|
|
4211
|
+
/** `POST /feedback/attachments` for the rework's one file. Left out, a rework is a
|
|
4212
|
+
* note alone. */
|
|
4213
|
+
onUpload?: FeedbackUpload;
|
|
4214
|
+
/** The app's authenticated GET (`AuthedImage`'s) — the pictures and the downloads go
|
|
4215
|
+
* through it, since the attachment route wants the bearer token (§3.5). Left out,
|
|
4216
|
+
* the browser's own fetch with same-origin cookies. */
|
|
4217
|
+
fetcher?: AuthedFetcher | null;
|
|
4218
|
+
/**
|
|
4219
|
+
* The undoable status change — what `useFeedbackStatusUndo(update.mutate)` returns,
|
|
4220
|
+
* the SAME one the table's status cell and the swipes use (§4.5). Left out, the
|
|
4221
|
+
* detail builds one from `onUpdate`, which is equivalent as long as `onUpdate` is the
|
|
4222
|
+
* page's mutation (its observer outlives the row a status filter removes).
|
|
4223
|
+
*/
|
|
4224
|
+
statusChange?: FeedbackStatusChange;
|
|
4225
|
+
/** For the rework's "Capture screenshot" — see {@link FeedbackReworkSectionProps}. */
|
|
4226
|
+
onCaptureScreenshot?: () => Promise<File | null>;
|
|
4227
|
+
/** The rework's file field, over the contract's defaults. */
|
|
4228
|
+
reworkAttachment?: Partial<FeedbackNoteAttachment>;
|
|
4229
|
+
/** How the `resolved_at` date reads — the app's date cell, as a string. Default: the
|
|
4230
|
+
* kit's `formatDate` in the provider's locale, `medium` style. */
|
|
4231
|
+
renderDate?: (iso: string) => string;
|
|
4232
|
+
/**
|
|
4233
|
+
* The detail's saves COMMIT (description, outcome, rework, status): under a locked
|
|
4234
|
+
* `WriteLockProvider` each is `aria-disabled` with the lock's reason, and nothing
|
|
4235
|
+
* reaches `onUpdate`. Off by default — keksdose's shell lock is a read-only demo
|
|
4236
|
+
* BUDGET, and a person viewing one still owns their own reports.
|
|
4237
|
+
*/
|
|
4238
|
+
commit?: boolean;
|
|
4239
|
+
labels?: Partial<FeedbackDetailLabels>;
|
|
4240
|
+
className?: string;
|
|
4241
|
+
}
|
|
4242
|
+
/**
|
|
4243
|
+
* The expanded row of a feedback table (§4.4): what was reported, where, with which
|
|
4244
|
+
* files, what the answer was, and where it stands — with the edits each viewer may
|
|
4245
|
+
* make. Hand it to `DataTable`'s `expandedRow`:
|
|
4246
|
+
*
|
|
4247
|
+
* ```tsx
|
|
4248
|
+
* const update = useMutation({ mutationFn: ({ id, ...patch }) => api.updateFeedback(id, patch),
|
|
4249
|
+
* onSuccess: invalidate, onError: (e) => toast.error(detailOf(e) ?? toastLabels.updateFailed) });
|
|
4250
|
+
* const changeStatus = useFeedbackStatusUndo(update.mutate);
|
|
4251
|
+
* <DataTable
|
|
4252
|
+
* isExpanded={(row) => expandedId === row.id}
|
|
4253
|
+
* expandedRow={(row) => (
|
|
4254
|
+
* <FeedbackRowDetail row={row} canEdit={isAdmin && !mine} viewerId={user?.id}
|
|
4255
|
+
* onUpdate={update.mutate} pending={update.isPending} statusChange={changeStatus}
|
|
4256
|
+
* onUpload={api.uploadFeedbackAttachment} fetcher={authedImageFetcher}
|
|
4257
|
+
* onCaptureScreenshot={captureAppScreenshot} />
|
|
4258
|
+
* )} … />
|
|
4259
|
+
* ```
|
|
4260
|
+
*
|
|
4261
|
+
* Mounted only while its row is expanded, so which editor is open lives here, and
|
|
4262
|
+
* collapsing the row closes it. Each editor is keyed by the row's id, so a draft is
|
|
4263
|
+
* never thrown away by a list refresh under it.
|
|
4264
|
+
*/
|
|
4265
|
+
declare function FeedbackRowDetail({ row, canEdit, viewerId, onUpdate, pending, onUpload, fetcher, statusChange, onCaptureScreenshot, reworkAttachment, renderDate, commit, labels: labelsProp, className, }: FeedbackRowDetailProps): react.JSX.Element;
|
|
4266
|
+
|
|
3028
4267
|
/**
|
|
3029
4268
|
* The `confirmDialog` namespace of `<UiKitProvider labels>`: the two buttons' fallback
|
|
3030
4269
|
* text. A call that knows what it is confirming says so per call (`confirmLabel:
|
|
@@ -4008,6 +5247,17 @@ interface UiKitLabels {
|
|
|
4008
5247
|
feedbackDialog: FeedbackDialogTextLabels;
|
|
4009
5248
|
feedbackThread: FeedbackThreadLabels;
|
|
4010
5249
|
feedbackComposer: FeedbackComposerLabels;
|
|
5250
|
+
/** 0.27.0: the feedback harmonisation (docs/feedback-harmonization.md §4) — a report's
|
|
5251
|
+
* status and category words, keyed by the enum value; the toasts a feedback page
|
|
5252
|
+
* shows; `FeedbackMenu`; `FeedbackContextBox`; the two pages' table; the row detail.
|
|
5253
|
+
* keksdose's wording is the canon, in every language. */
|
|
5254
|
+
feedbackStatus: FeedbackStatusLabels;
|
|
5255
|
+
feedbackCategory: FeedbackCategoryLabels;
|
|
5256
|
+
feedbackToast: FeedbackToastLabels;
|
|
5257
|
+
feedbackMenu: FeedbackMenuLabels;
|
|
5258
|
+
feedbackContext: FeedbackContextLabels;
|
|
5259
|
+
feedbackPage: FeedbackPageLabels;
|
|
5260
|
+
feedbackDetail: FeedbackDetailLabels;
|
|
4011
5261
|
/** 0.12.0: `ProfileSetting`, `PasswordSetting`, `TwoFactorSetting`, `PasskeysSetting`
|
|
4012
5262
|
* — one record per section. The provider takes a whole section record; the
|
|
4013
5263
|
* components merge it key by key, as a prop. */
|
|
@@ -4250,4 +5500,4 @@ declare function useKitFileLabels(prop?: Partial<FileLabels>): FileLabels;
|
|
|
4250
5500
|
*/
|
|
4251
5501
|
declare function missingKitLabels(labels: UiKitLabelOverrides | undefined, reference: UiKitLabels): string[];
|
|
4252
5502
|
|
|
4253
|
-
export {
|
|
5503
|
+
export { type FeedbackContext as $, type FeedbackAttachmentError as A, type FeedbackAttachmentErrorInfo as B, ChatComposer as C, DEFAULT_ATTACHMENT_ACCEPT as D, FeedbackAttachmentField as E, FEEDBACK_ATTACHMENT_ACCEPT as F, type FeedbackAttachmentFieldLabels as G, type FeedbackAttachmentFieldMultipleProps as H, type FeedbackAttachmentFieldProps as I, type FeedbackAttachmentFieldRefsProps as J, type FeedbackAttachmentFieldSingleProps as K, type FeedbackAttachmentLabels as L, type FeedbackAttachmentRef as M, type FeedbackAttachmentRefsError as N, FeedbackAwaitingToggle as O, type FeedbackAwaitingToggleProps as P, type FeedbackCategory as Q, FeedbackCategoryBadge as R, type FeedbackCategoryLabels as S, type FeedbackCategoryOption as T, type FeedbackColumnsOptions as U, FeedbackComposer as V, type FeedbackComposerAttachment as W, type FeedbackComposerHandle as X, type FeedbackComposerLabels as Y, type FeedbackComposerProps as Z, type FeedbackComposerSlotContext as _, type ChatComposerHandle as a, feedbackAttachmentUrls as a$, FeedbackContextBox as a0, type FeedbackContextBoxProps as a1, type FeedbackContextInput as a2, type FeedbackContextLabels as a3, FeedbackDetail as a4, type FeedbackDetailLabels as a5, type FeedbackDetailPatch as a6, FeedbackDetailSection as a7, type FeedbackDetailUpdate as a8, FeedbackDialog as a9, FeedbackStatusBadge as aA, type FeedbackStatusChange as aB, type FeedbackStatusLabels as aC, type FeedbackStatusMutate as aD, type FeedbackStatusPatch as aE, type FeedbackStatusRow as aF, FeedbackStatusTransitions as aG, type FeedbackStatusUndoOptions as aH, FeedbackSubject as aI, type FeedbackSubjectProps as aJ, type FeedbackSubmission as aK, type FeedbackSubmitHint as aL, type FeedbackSubmitter as aM, type FeedbackTableRow as aN, FeedbackThread as aO, type FeedbackThreadAttachment as aP, type FeedbackThreadEvent as aQ, type FeedbackThreadItem as aR, type FeedbackThreadLabels as aS, type FeedbackThreadMessage as aT, type FeedbackThreadProps as aU, type FeedbackToastLabels as aV, type FeedbackUpload as aW, type UseFeedbackColumnsOptions as aX, appendRework as aY, attachmentName as aZ, feedbackAttachmentLine as a_, type FeedbackDialogLabels as aa, type FeedbackDialogMultipleProps as ab, type FeedbackDialogProps as ac, type FeedbackDialogSingleProps as ad, type FeedbackDialogTextLabels as ae, FeedbackEmptyState as af, type FeedbackEmptyStateProps as ag, type FeedbackEnvironment as ah, FeedbackMenu as ai, type FeedbackMenuLabels as aj, type FeedbackMenuProps as ak, FeedbackMobileCard as al, type FeedbackMobileCardProps as am, type FeedbackMultipleSubmission as an, type FeedbackNoteAttachment as ao, FeedbackNoteEditor as ap, type FeedbackPageLabels as aq, FeedbackProse as ar, type FeedbackRecord as as, type FeedbackRenderDate as at, FeedbackReworkSection as au, type FeedbackReworkSectionProps as av, type FeedbackRowAccess as aw, FeedbackRowDetail as ax, type FeedbackRowDetailProps as ay, type FeedbackStatus as az, type ChatComposerLabels as b, type ConfirmProviderProps as b$, feedbackCategoryRank as b0, feedbackColumns as b1, feedbackContext as b2, feedbackEnvironment as b3, feedbackMobileGroupBy as b4, feedbackPageHref as b5, feedbackPagePath as b6, feedbackPageUrl as b7, feedbackRowAccess as b8, feedbackSubmitter as b9, type ButtonVariant as bA, type CalculatorLabels as bB, Card as bC, CardAction as bD, type CardActionProps as bE, CardContent as bF, type CardContentProps as bG, type CardDensity as bH, CardDescription as bI, type CardDescriptionProps as bJ, CardFooter as bK, type CardFooterProps as bL, CardHeader as bM, type CardHeaderProps as bN, type CardProps as bO, CardTitle as bP, type CardTitleLevel as bQ, type CardTitleProps as bR, type CardTone as bS, type CharacterCountLabels as bT, type ChartTooltipPlacement as bU, type ComboboxLabels as bV, type CommonLabels as bW, type ConfirmDialogLabels as bX, type ConfirmFn as bY, type ConfirmOptions as bZ, ConfirmProvider as b_, feedbackSubmitterEmail as ba, isFeedbackAwaiting as bb, isImageAttachment as bc, nextFeedbackStatus as bd, pastedName as be, replaceDescription as bf, reworkCount as bg, selectableFeedbackStatuses as bh, splitBodyAttachments as bi, splitDescription as bj, useFeedbackCategoryLabels as bk, useFeedbackColumns as bl, useFeedbackDetailLabels as bm, useFeedbackPageLabels as bn, useFeedbackRenderDate as bo, useFeedbackStatusLabels as bp, useFeedbackStatusUndo as bq, useFeedbackToastLabels as br, visibleFeedbackStatuses as bs, type AppShellLabels as bt, Button as bu, type ButtonClassesOptions as bv, type ButtonLinkProps as bw, type ButtonProps as bx, type ButtonSize as by, type ButtonTone as bz, type ChatComposerProps as c, type FormActionsPlacement as c$, type ConfirmTone as c0, CopyButton as c1, type CopyButtonLabels as c2, type CopyButtonProps as c3, type CurrencyLabels as c4, CurrentPasswordInput as c5, type CurrentPasswordInputLabels as c6, type CurrentPasswordInputProps as c7, DEFAULT_APP_SHELL_LABELS as c8, DEFAULT_CALCULATOR_LABELS as c9, EmptyState as cA, type EmptyStateProps as cB, FIELD_BASE as cC, FIELD_DISPLAY as cD, FIELD_FLOATING_PAD as cE, FIELD_INVALID as cF, FIELD_TRIGGER as cG, FIELD_WRITABLE_LOOK as cH, FLOATING_INPUT_CLASS as cI, FLOATING_LABEL_CLASS as cJ, FieldChevron as cK, type FieldChevronProps as cL, FieldHint as cM, type FieldHintProps as cN, FileButton as cO, type FileButtonProps as cP, type FileLabels as cQ, type FilePickerLabels as cR, type FileRejection as cS, type FileRejectionReason as cT, type FileScreenOptions as cU, FloatingField as cV, type FloatingFieldProps as cW, FormActions as cX, type FormActionsAlign as cY, type FormActionsDestructive as cZ, type FormActionsLabels as c_, DEFAULT_CHARACTER_COUNT_LABELS as ca, DEFAULT_COMBOBOX_LABELS as cb, DEFAULT_COMMON_LABELS as cc, DEFAULT_CONFIRM_DIALOG_LABELS as cd, DEFAULT_COPY_BUTTON_LABELS as ce, DEFAULT_CURRENCY_LABELS as cf, DEFAULT_DANGER_CONFIRM_LABELS as cg, DEFAULT_DATE_PICKER_LABELS as ch, DEFAULT_FILE_LABELS as ci, DEFAULT_FILE_PICKER_LABELS as cj, DEFAULT_FORM_ACTIONS_LABELS as ck, DEFAULT_IBAN_INPUT_LABELS as cl, DEFAULT_MULTI_SELECT_LABELS as cm, DEFAULT_PASSWORD_REVEAL_LABELS as cn, DEFAULT_PHONE_INPUT_LABELS as co, DEFAULT_PICKER_SHEET_LABELS as cp, DEFAULT_SHARE_CARD_LABELS as cq, DEFAULT_SWIPEABLE_ROW_LABELS as cr, DEFAULT_TABS_LABELS as cs, DEFAULT_TOP_BAR_LABELS as ct, DangerConfirm as cu, type DangerConfirmLabels as cv, type DangerConfirmProps as cw, type DangerConfirmValues as cx, type DangerConsequence as cy, type DatePickerLabels as cz, type ChatComposerSlotContext as d, UiKitProvider as d$, type FormActionsProps as d0, type FormActionsStickyWithin as d1, type FormActionsSubmitProps as d2, type FormActionsSubmitShortcut as d3, IbanInput as d4, type IbanInputLabels as d5, type IbanInputProps as d6, IconButton as d7, type IconButtonGlyphSize as d8, type IconButtonLinkProps as d9, type ShareCandidate as dA, ShareCard as dB, type ShareCardLabels as dC, type ShareCardProps as dD, ShareDialog as dE, type ShareDialogProps as dF, type ShareGrantee as dG, SharePanel as dH, type SharePanelProps as dI, type SharePendingGrant as dJ, type ShareRole as dK, Spinner as dL, type SpinnerProps as dM, type SwipeableRowLabels as dN, type TabItem as dO, Tabs as dP, type TabsLabels as dQ, type TabsProps as dR, Textarea as dS, type TextareaProps as dT, type TopBarLabels as dU, TypedConfirmField as dV, type TypedConfirmFieldLabels as dW, type TypedConfirmFieldProps as dX, type TypedMatch as dY, type UiKitLabelOverrides as dZ, type UiKitLabels as d_, type IconButtonProps as da, type IconButtonSize as db, type IconButtonTone as dc, type IconButtonToneColor as dd, type IconButtonVariant as de, Input as df, type InputProps as dg, type KitDateFormatContext as dh, type KitDateFormatter as di, type KitLinkComponent as dj, type KitLinkProps as dk, Label as dl, type LabelOverride as dm, type LabelProps as dn, type MultiSelectLabels as dp, PHONE_QUERY as dq, type PasswordRevealLabels as dr, PhoneInput as ds, type PhoneInputLabels as dt, type PhoneInputProps as du, type PickerSheetLabels as dv, type ResponsiveFormActionsPlacement as dw, Select as dx, type SelectProps as dy, type ShareAddRequest as dz, DEFAULT_FEEDBACK_ATTACHMENT_LABELS as e, type UiKitProviderProps as e0, type UseFilePickerOptions as e1, type UseFilePickerReturn as e2, buttonClasses as e3, formatFileSize as e4, matchesAccept as e5, missingKitLabels as e6, resolvePasswordRevealLabels as e7, typedMatches as e8, useConfirm as e9, useFilePicker as ea, useKitChartTooltipPlacement as eb, useKitDateFormatter as ec, useKitFileLabels as ed, useKitLabelOverrides as ee, useKitLabels as ef, useKitLink as eg, useKitLocale as eh, useKitWeekStart as ei, type FilePickHandler as ej, usePromisePending as ek, formatAccept as el, judgePick as em, screenFiles as en, summariseRejections as eo, DEFAULT_FEEDBACK_CATEGORY_LABELS as f, DEFAULT_FEEDBACK_COMPOSER_LABELS as g, DEFAULT_FEEDBACK_CONTEXT_LABELS as h, DEFAULT_FEEDBACK_DETAIL_LABELS as i, DEFAULT_FEEDBACK_DIALOG_LABELS as j, DEFAULT_FEEDBACK_MENU_LABELS as k, DEFAULT_FEEDBACK_PAGE_LABELS as l, DEFAULT_FEEDBACK_STATUS_LABELS as m, DEFAULT_FEEDBACK_THREAD_LABELS as n, DEFAULT_FEEDBACK_TOAST_LABELS as o, DEFAULT_MAX_ATTACHMENTS as p, DEFAULT_MAX_ATTACHMENT_BYTES as q, FEEDBACK_AUTHOR_EDITABLE_STATUSES as r, FEEDBACK_AWAITING_STATUSES as s, FEEDBACK_CATEGORY_META as t, FEEDBACK_CATEGORY_ORDER as u, FEEDBACK_PICKABLE_CATEGORIES as v, FEEDBACK_REWORKABLE_STATUSES as w, FEEDBACK_STATUS_META as x, FEEDBACK_STATUS_ORDER as y, FEEDBACK_UNDO_DURATION as z };
|