@ciridae/ui 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/components/blocks/comment-composer.d.ts +3 -1
  2. package/dist/components/blocks/comment-composer.js +13 -7
  3. package/dist/components/blocks/comment-thread.d.ts +2 -1
  4. package/dist/components/blocks/comment-thread.js +2 -2
  5. package/dist/components/blocks/feedback-center-comments.d.ts +3 -1
  6. package/dist/components/blocks/feedback-center-comments.js +9 -3
  7. package/dist/components/blocks/feedback-center-footer.d.ts +12 -0
  8. package/dist/components/blocks/feedback-center-footer.js +10 -0
  9. package/dist/components/blocks/feedback-center-sheet-parts.d.ts +14 -11
  10. package/dist/components/blocks/feedback-center-sheet-parts.js +17 -22
  11. package/dist/components/blocks/feedback-center-sheet.d.ts +8 -2
  12. package/dist/components/blocks/feedback-center-sheet.js +72 -15
  13. package/dist/components/blocks/feedback-composer-parts.d.ts +4 -4
  14. package/dist/components/blocks/feedback-composer-parts.js +4 -4
  15. package/dist/components/blocks/feedback-composer-types.d.ts +5 -4
  16. package/dist/components/blocks/feedback-composer.d.ts +7 -3
  17. package/dist/components/blocks/feedback-composer.js +49 -79
  18. package/dist/components/blocks/feedback-detail-sheet.d.ts +9 -3
  19. package/dist/components/blocks/feedback-detail-sheet.js +27 -9
  20. package/dist/components/blocks/feedback-emergency-fields.d.ts +17 -0
  21. package/dist/components/blocks/feedback-emergency-fields.js +32 -0
  22. package/dist/components/blocks/feedback-metrics.js +1 -1
  23. package/dist/components/blocks/feedback-sheet.d.ts +1 -1
  24. package/dist/components/blocks/feedback-sheet.js +17 -4
  25. package/dist/components/blocks/feedback-status-badge.js +3 -3
  26. package/dist/components/blocks/feedback-table-row.js +8 -18
  27. package/dist/components/blocks/feedback-table-sort.js +4 -4
  28. package/dist/components/blocks/feedback-table.js +2 -2
  29. package/dist/components/blocks/feedback-tracking-types.d.ts +20 -11
  30. package/dist/components/blocks/feedback-tracking-types.js +15 -7
  31. package/dist/components/blocks/feedback-type-badge.js +1 -1
  32. package/dist/components/blocks/feedback-user-detail.d.ts +2 -1
  33. package/dist/components/blocks/feedback-user-detail.js +13 -6
  34. package/dist/components/blocks/feedback-user-list.d.ts +3 -1
  35. package/dist/components/blocks/feedback-user-list.js +11 -3
  36. package/dist/components/blocks/submit-feedback-composer.d.ts +5 -0
  37. package/dist/components/blocks/submit-feedback-composer.js +40 -0
  38. package/dist/components/blocks/use-feedback-composer-state.d.ts +27 -0
  39. package/dist/components/blocks/use-feedback-composer-state.js +60 -0
  40. package/dist/components/ui/form-sheet.d.ts +15 -0
  41. package/dist/components/ui/form-sheet.js +106 -0
  42. package/dist/entries/feedback-center-footer.d.ts +1 -0
  43. package/dist/entries/feedback-center-footer.js +1 -0
  44. package/dist/entries/feedback-emergency-fields.d.ts +1 -0
  45. package/dist/entries/feedback-emergency-fields.js +1 -0
  46. package/dist/entries/form-sheet.d.ts +1 -0
  47. package/dist/entries/form-sheet.js +1 -0
  48. package/dist/entries/submit-feedback-composer.d.ts +1 -0
  49. package/dist/entries/submit-feedback-composer.js +1 -0
  50. package/dist/entries/use-feedback-composer-state.d.ts +1 -0
  51. package/dist/entries/use-feedback-composer-state.js +1 -0
  52. package/package.json +1 -1
@@ -7,10 +7,12 @@ type CommentComposerProps = {
7
7
  defaultValue?: string;
8
8
  disabled?: boolean;
9
9
  isSubmitting?: boolean;
10
+ loadingLabel?: string;
10
11
  onSubmit: (body: string) => void | Promise<void>;
11
12
  onValueChange?: (value: string) => void;
12
13
  placeholder?: string;
14
+ submitLabel?: string;
13
15
  value?: string;
14
16
  };
15
- declare function CommentComposer({ actionsSlot, author, defaultExpanded, defaultValue, disabled, isSubmitting, onSubmit, onValueChange, placeholder, value, }: CommentComposerProps): React.JSX.Element;
17
+ declare function CommentComposer({ actionsSlot, author, defaultExpanded, defaultValue, disabled, isSubmitting, loadingLabel, onSubmit, onValueChange, placeholder, submitLabel, value, }: CommentComposerProps): React.JSX.Element;
16
18
  export { CommentComposer, type CommentComposerProps };
@@ -4,7 +4,7 @@ import { getCommentAuthorInitials } from "./comment-types";
4
4
  import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
5
5
  import { InputGroupButton, InputGroupTextarea } from "../ui/input-group";
6
6
  const COMMENT_TEXTAREA_MAX_HEIGHT = 128;
7
- function CommentComposer({ actionsSlot, author, defaultExpanded = false, defaultValue = "", disabled = false, isSubmitting = false, onSubmit, onValueChange, placeholder = "Write a reply...", value, }) {
7
+ function CommentComposer({ actionsSlot, author, defaultExpanded = false, defaultValue = "", disabled = false, isSubmitting = false, loadingLabel = "Posting...", onSubmit, onValueChange, placeholder = "Write a reply...", submitLabel = "Post", value, }) {
8
8
  const textareaRef = React.useRef(null);
9
9
  const state = useCommentComposerState({
10
10
  defaultExpanded,
@@ -19,7 +19,7 @@ function CommentComposer({ actionsSlot, author, defaultExpanded = false, default
19
19
  resizeCommentTextarea(textareaRef.current);
20
20
  }, [state.value]);
21
21
  const isDisabled = disabled || isSubmitting;
22
- return (_jsx("form", { onSubmit: state.onSubmit, children: _jsxs("div", { className: "grid w-full gap-1.5 rounded-md border border-input bg-background px-2.5 py-2 shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[var(--focus-ring-width)] focus-within:ring-ring/50 dark:bg-input/30", "data-disabled": isDisabled ? "true" : undefined, children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [_jsx(CommentComposerAvatar, { author: author }), _jsx(InputGroupTextarea, { "aria-label": "Comment", className: "max-h-32 min-h-7 overflow-y-hidden px-0 py-1 text-sm leading-5", disabled: isDisabled, onBlur: state.onBlur, onChange: state.onValueChange, onFocus: state.onFocus, placeholder: placeholder, ref: textareaRef, rows: 1, value: state.value })] }), _jsx(CommentComposerActions, { actionsSlot: actionsSlot, canSubmit: state.canSubmit, isSubmitting: isSubmitting, showActions: state.showActions })] }) }));
22
+ return (_jsx("form", { onSubmit: state.onSubmit, children: _jsxs("div", { className: "grid w-full gap-1.5 rounded-md border border-input bg-background px-2.5 py-2 shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[var(--focus-ring-width)] focus-within:ring-ring/50 dark:bg-input/30", "data-disabled": isDisabled ? "true" : undefined, children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [_jsx(CommentComposerAvatar, { author: author }), _jsx(InputGroupTextarea, { "aria-label": "Comment", className: "max-h-32 min-h-7 overflow-y-hidden px-0 py-1 text-sm leading-5", disabled: isDisabled, onBlur: state.onBlur, onChange: state.onValueChange, onFocus: state.onFocus, placeholder: placeholder, ref: textareaRef, rows: 1, value: state.value })] }), _jsx(CommentComposerActions, { actionsSlot: actionsSlot, canSubmit: state.canSubmit, isSubmitting: isSubmitting, loadingLabel: loadingLabel, showActions: state.showActions, submitLabel: submitLabel })] }) }));
23
23
  }
24
24
  function useCommentComposerState({ defaultExpanded, defaultValue, disabled, isSubmitting, onSubmit, onValueChange, value, }) {
25
25
  const [localValue, setLocalValue] = React.useState(defaultValue);
@@ -75,7 +75,13 @@ function getSubmitHandler({ currentValue, disabled, isControlled, isSubmitting,
75
75
  const body = currentValue.trim();
76
76
  if (!body || disabled || isSubmitting)
77
77
  return;
78
- await onSubmit(body);
78
+ try {
79
+ await onSubmit(body);
80
+ }
81
+ catch {
82
+ // a rejected submit keeps the draft; messaging is the handler's job
83
+ return;
84
+ }
79
85
  if (!isControlled) {
80
86
  setLocalValue("");
81
87
  setIsActive(false);
@@ -90,17 +96,17 @@ function getValueChangeHandler({ isControlled, onValueChange, setLocalValue, })
90
96
  onValueChange?.(nextValue);
91
97
  };
92
98
  }
93
- function CommentComposerActions({ actionsSlot, canSubmit, isSubmitting, showActions, }) {
99
+ function CommentComposerActions({ actionsSlot, canSubmit, isSubmitting, loadingLabel, showActions, submitLabel, }) {
94
100
  if (!showActions)
95
101
  return null;
96
- return (_jsxs("div", { className: "flex min-h-8 items-center justify-between gap-2 pl-8", children: [_jsx("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: actionsSlot }), _jsx(CommentSubmitButton, { canSubmit: canSubmit, isSubmitting: isSubmitting })] }));
102
+ return (_jsxs("div", { className: "flex min-h-8 items-center justify-between gap-2 pl-8", children: [_jsx("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: actionsSlot }), _jsx(CommentSubmitButton, { canSubmit: canSubmit, isSubmitting: isSubmitting, loadingLabel: loadingLabel, submitLabel: submitLabel })] }));
97
103
  }
98
104
  function CommentComposerAvatar({ author }) {
99
105
  const name = author?.name ?? "You";
100
106
  return (_jsxs(Avatar, { size: "sm", children: [_jsx(AvatarImage, { alt: "", src: author?.avatarUrl }), _jsx(AvatarFallback, { children: getCommentAuthorInitials(name) })] }));
101
107
  }
102
- function CommentSubmitButton({ canSubmit, isSubmitting, }) {
103
- return (_jsx(InputGroupButton, { disabled: !canSubmit, loading: isSubmitting, loadingLabel: "Posting comment", size: "sm", type: "submit", children: "Post" }));
108
+ function CommentSubmitButton({ canSubmit, isSubmitting, loadingLabel, submitLabel, }) {
109
+ return (_jsx(InputGroupButton, { disabled: !canSubmit, loading: isSubmitting, loadingLabel: loadingLabel, size: "sm", type: "submit", children: submitLabel }));
104
110
  }
105
111
  function resizeCommentTextarea(textarea) {
106
112
  if (!textarea)
@@ -4,6 +4,7 @@ type CommentThreadProps = {
4
4
  comments: CommentItemData[];
5
5
  composerAuthor?: CommentAuthor;
6
6
  composerPlaceholder?: string;
7
+ composerSubmitLabel?: string;
7
8
  emptyDescription?: string;
8
9
  emptyTitle?: string;
9
10
  isLoading?: boolean;
@@ -14,5 +15,5 @@ type CommentThreadProps = {
14
15
  showHeader?: boolean;
15
16
  title?: string;
16
17
  };
17
- declare function CommentThread({ comments, composerAuthor, composerPlaceholder, emptyDescription, emptyTitle, isLoading, isSubmitting, onSubmit, readOnly, renderCommentActions, showHeader, title, }: CommentThreadProps): React.JSX.Element;
18
+ declare function CommentThread({ comments, composerAuthor, composerPlaceholder, composerSubmitLabel, emptyDescription, emptyTitle, isLoading, isSubmitting, onSubmit, readOnly, renderCommentActions, showHeader, title, }: CommentThreadProps): React.JSX.Element;
18
19
  export { CommentThread, type CommentThreadProps };
@@ -3,9 +3,9 @@ import { MessageSquareTextIcon } from "lucide-react";
3
3
  import { CommentComposer } from "./comment-composer";
4
4
  import { CommentItem } from "./comment-item";
5
5
  import { Skeleton } from "../ui/skeleton";
6
- function CommentThread({ comments, composerAuthor, composerPlaceholder, emptyDescription = "Start the conversation with a short update or question.", emptyTitle = "No comments yet", isLoading = false, isSubmitting = false, onSubmit, readOnly = false, renderCommentActions, showHeader = true, title = "Comments", }) {
6
+ function CommentThread({ comments, composerAuthor, composerPlaceholder, composerSubmitLabel, emptyDescription = "Start the conversation with a short update or question.", emptyTitle = "No comments yet", isLoading = false, isSubmitting = false, onSubmit, readOnly = false, renderCommentActions, showHeader = true, title = "Comments", }) {
7
7
  const showComposer = !readOnly && Boolean(onSubmit);
8
- return (_jsxs("section", { className: "grid w-full gap-(--panel-gap)", "aria-label": title, children: [showHeader ? _jsx(CommentThreadHeader, { count: comments.length, title: title }) : null, _jsx(CommentThreadBody, { comments: comments, emptyDescription: emptyDescription, emptyTitle: emptyTitle, isLoading: isLoading, renderCommentActions: renderCommentActions }), showComposer && onSubmit ? (_jsx(CommentComposer, { author: composerAuthor, isSubmitting: isSubmitting, onSubmit: onSubmit, placeholder: composerPlaceholder })) : null] }));
8
+ return (_jsxs("section", { className: "grid w-full gap-(--panel-gap)", "aria-label": title, children: [showHeader ? _jsx(CommentThreadHeader, { count: comments.length, title: title }) : null, _jsx(CommentThreadBody, { comments: comments, emptyDescription: emptyDescription, emptyTitle: emptyTitle, isLoading: isLoading, renderCommentActions: renderCommentActions }), showComposer && onSubmit ? (_jsx(CommentComposer, { author: composerAuthor, isSubmitting: isSubmitting, onSubmit: onSubmit, placeholder: composerPlaceholder, submitLabel: composerSubmitLabel })) : null] }));
9
9
  }
10
10
  function CommentThreadHeader({ count, title }) {
11
11
  return (_jsxs("header", { className: "flex items-center justify-between gap-3", children: [_jsx("h3", { className: "text-sm font-medium text-foreground", children: title }), _jsx("span", { className: "font-mono text-xs text-muted-foreground tabular-nums", children: count })] }));
@@ -3,10 +3,12 @@ import type { UserFeedbackItem } from "./feedback-tracking-types";
3
3
  type FeedbackCommentSubmitHandler = (feedback: UserFeedbackItem, body: string) => void | Promise<void>;
4
4
  type FeedbackCenterCommentThreadProps = {
5
5
  comments: CommentItemData[];
6
+ error?: Error | null;
6
7
  feedback?: UserFeedbackItem;
7
8
  isCommentSubmitting: boolean;
8
9
  isCommentsLoading: boolean;
10
+ onRetry?: () => void;
9
11
  onSubmitComment?: FeedbackCommentSubmitHandler;
10
12
  };
11
- declare function FeedbackCenterCommentThread({ comments, feedback, isCommentSubmitting, isCommentsLoading, onSubmitComment, }: FeedbackCenterCommentThreadProps): import("@types/react").JSX.Element;
13
+ declare function FeedbackCenterCommentThread({ comments, error, feedback, isCommentSubmitting, isCommentsLoading, onRetry, onSubmitComment, }: FeedbackCenterCommentThreadProps): import("@types/react").JSX.Element;
12
14
  export { FeedbackCenterCommentThread, type FeedbackCommentSubmitHandler };
@@ -1,7 +1,13 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AlertCircleIcon } from "lucide-react";
2
3
  import { CommentThread } from "./comment-thread";
3
- function FeedbackCenterCommentThread({ comments, feedback, isCommentSubmitting, isCommentsLoading, onSubmitComment, }) {
4
- return (_jsx(CommentThread, { comments: comments, composerPlaceholder: "Write a reply...", isLoading: isCommentsLoading, isSubmitting: isCommentSubmitting, onSubmit: getCommentSubmitHandler(feedback, onSubmitComment), readOnly: !onSubmitComment, showHeader: false }));
4
+ import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
5
+ import { Button } from "../ui/button";
6
+ function FeedbackCenterCommentThread({ comments, error, feedback, isCommentSubmitting, isCommentsLoading, onRetry, onSubmitComment, }) {
7
+ if (error) {
8
+ return (_jsxs(Alert, { variant: "destructive", children: [_jsx(AlertCircleIcon, {}), _jsx(AlertTitle, { children: "Conversation could not be loaded" }), _jsxs(AlertDescription, { className: "grid gap-3", children: [_jsx("span", { children: error.message }), onRetry ? (_jsx(Button, { className: "justify-self-start", onClick: onRetry, size: "sm", type: "button", variant: "outline", children: "Try again" })) : null] })] }));
9
+ }
10
+ return (_jsx(CommentThread, { comments: comments, composerPlaceholder: "Write a reply...", composerSubmitLabel: "Reply", isLoading: isCommentsLoading, isSubmitting: isCommentSubmitting, onSubmit: getCommentSubmitHandler(feedback, onSubmitComment), readOnly: !onSubmitComment, showHeader: false }));
5
11
  }
6
12
  function getCommentSubmitHandler(feedback, onSubmitComment) {
7
13
  if (!feedback || !onSubmitComment)
@@ -0,0 +1,12 @@
1
+ import type { FeedbackCenterTab } from "./feedback-center-sheet-parts";
2
+ import type { FeedbackUrgency } from "./feedback-composer-types";
3
+ type FeedbackCenterFooterProps = {
4
+ activeTab: FeedbackCenterTab;
5
+ formId: string;
6
+ isSubmitted: boolean;
7
+ isSubmitting: boolean;
8
+ onCancel: () => void;
9
+ urgency: FeedbackUrgency;
10
+ };
11
+ declare function FeedbackCenterFooter({ activeTab, formId, isSubmitted, isSubmitting, onCancel, urgency, }: FeedbackCenterFooterProps): import("@types/react").JSX.Element | null;
12
+ export { FeedbackCenterFooter, type FeedbackCenterFooterProps };
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AlertTriangleIcon, SendIcon } from "lucide-react";
3
+ import { Button } from "../ui/button";
4
+ import { SheetFooter } from "../ui/sheet";
5
+ function FeedbackCenterFooter({ activeTab, formId, isSubmitted, isSubmitting, onCancel, urgency, }) {
6
+ if (activeTab !== "submit" || isSubmitted)
7
+ return null;
8
+ return (_jsxs(SheetFooter, { children: [_jsx(Button, { disabled: isSubmitting, onClick: onCancel, type: "button", variant: "outline", children: "Cancel" }), _jsxs(Button, { form: formId, loading: isSubmitting, type: "submit", variant: urgency === "emergency" ? "destructive" : "default", children: [urgency === "emergency" ? _jsx(AlertTriangleIcon, {}) : _jsx(SendIcon, {}), urgency === "emergency" ? "Send emergency" : "Submit feedback"] })] }));
9
+ }
10
+ export { FeedbackCenterFooter };
@@ -2,33 +2,36 @@ import type * as React from "react";
2
2
  import type { CommentItemData } from "./comment-types";
3
3
  import { type FeedbackCommentSubmitHandler } from "./feedback-center-comments";
4
4
  import { type FeedbackComposerProps } from "./feedback-composer";
5
+ import type { FeedbackUrgency } from "./feedback-composer-types";
5
6
  import type { UserFeedbackItem } from "./feedback-tracking-types";
6
7
  type FeedbackCenterTab = "mine" | "submit";
7
8
  type FeedbackCenterView = "detail" | "list";
8
- type FeedbackCenterComposerProps = Omit<FeedbackComposerProps, "actions" | "formId" | "onCancel" | "onSubmittedChange">;
9
+ type FeedbackCenterComposerProps = Omit<FeedbackComposerProps, "actions" | "formId" | "onCancel" | "onDirtyChange" | "onSubmittedChange" | "onUrgencyChange">;
9
10
  type FeedbackCenterTabsProps = {
10
11
  commentsByFeedbackId: Record<string, CommentItemData[]>;
12
+ commentsError?: Error | null;
11
13
  composerProps: FeedbackCenterComposerProps;
12
14
  formId: string;
15
+ onDirtyChange: (dirty: boolean) => void;
16
+ onUrgencyChange: (urgency: FeedbackUrgency) => void;
13
17
  isCommentSubmitting: boolean;
14
18
  isCommentsLoading: boolean;
15
19
  isLoading: boolean;
16
20
  items: UserFeedbackItem[];
21
+ itemsError?: Error | null;
17
22
  onBack: () => void;
18
23
  onCancel: () => void;
19
24
  onSelectFeedback: (item: UserFeedbackItem) => void;
25
+ onRetryComments?: () => void;
26
+ onRetryItems?: () => void;
20
27
  onSubmittedChange: (submitted: boolean) => void;
21
28
  onSubmitComment?: FeedbackCommentSubmitHandler;
22
29
  selectedFeedback?: UserFeedbackItem;
30
+ renderEvidence?: (item: UserFeedbackItem) => React.ReactNode;
23
31
  view: FeedbackCenterView;
24
32
  };
25
- declare function FeedbackCenterSheetHeader(): React.JSX.Element;
26
- declare function FeedbackCenterTabs({ composerProps, commentsByFeedbackId, formId, isCommentSubmitting, isCommentsLoading, isLoading, items, onBack, onCancel, onSelectFeedback, onSubmittedChange, onSubmitComment, selectedFeedback, view, }: FeedbackCenterTabsProps): React.JSX.Element;
27
- declare function FeedbackCenterFooter({ activeTab, formId, isSubmitted, isSubmitting, onCancel, }: {
28
- activeTab: FeedbackCenterTab;
29
- formId: string;
30
- isSubmitted: boolean;
31
- isSubmitting: boolean;
32
- onCancel: () => void;
33
- }): React.JSX.Element | null;
34
- export { FeedbackCenterFooter, FeedbackCenterSheetHeader, FeedbackCenterTabs, type FeedbackCenterTab, type FeedbackCenterView, };
33
+ declare function FeedbackCenterSheetHeader({ unreadReplyCount }: {
34
+ unreadReplyCount: number;
35
+ }): React.JSX.Element;
36
+ declare function FeedbackCenterTabs({ composerProps, commentsByFeedbackId, commentsError, formId, onDirtyChange, onUrgencyChange, isCommentSubmitting, isCommentsLoading, isLoading, items, itemsError, onBack, onCancel, onSelectFeedback, onRetryComments, onRetryItems, onSubmittedChange, onSubmitComment, selectedFeedback, renderEvidence, view, }: FeedbackCenterTabsProps): React.JSX.Element;
37
+ export { FeedbackCenterSheetHeader, FeedbackCenterTabs, type FeedbackCenterTab, type FeedbackCenterView, };
@@ -1,44 +1,39 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { SendIcon } from "lucide-react";
3
2
  import { FeedbackCenterCommentThread, } from "./feedback-center-comments";
4
3
  import { FeedbackComposer } from "./feedback-composer";
5
4
  import { FeedbackUserDetail } from "./feedback-user-detail";
6
5
  import { FeedbackUserList } from "./feedback-user-list";
7
- import { Button } from "../ui/button";
8
- import { SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "../ui/sheet";
6
+ import { Badge } from "../ui/badge";
7
+ import { SheetDescription, SheetHeader, SheetTitle } from "../ui/sheet";
9
8
  import { TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
10
- function FeedbackCenterSheetHeader() {
11
- return (_jsxs(SheetHeader, { className: "gap-3", children: [_jsxs("div", { className: "grid gap-1.5 pr-12", children: [_jsx(SheetTitle, { children: "Feedback" }), _jsx(SheetDescription, { children: "Send feedback and track your submissions in one place." })] }), _jsx(FeedbackCenterTabsList, {})] }));
9
+ function FeedbackCenterSheetHeader({ unreadReplyCount }) {
10
+ return (_jsxs(SheetHeader, { className: "gap-3", children: [_jsxs("div", { className: "grid gap-1.5 pr-12", children: [_jsx(SheetTitle, { children: "Feedback" }), _jsx(SheetDescription, { children: "Send feedback and track your submissions in one place." })] }), _jsx(FeedbackCenterTabsList, { unreadReplyCount: unreadReplyCount })] }));
12
11
  }
13
- function FeedbackCenterTabs({ composerProps, commentsByFeedbackId, formId, isCommentSubmitting, isCommentsLoading, isLoading, items, onBack, onCancel, onSelectFeedback, onSubmittedChange, onSubmitComment, selectedFeedback, view, }) {
14
- return (_jsxs(_Fragment, { children: [_jsx(FeedbackCenterSubmitTab, { composerProps: composerProps, formId: formId, onCancel: onCancel, onSubmittedChange: onSubmittedChange }), _jsx(FeedbackCenterMineContent, { commentsByFeedbackId: commentsByFeedbackId, isCommentSubmitting: isCommentSubmitting, isCommentsLoading: isCommentsLoading, isLoading: isLoading, items: items, onBack: onBack, onSelectFeedback: onSelectFeedback, onSubmitComment: onSubmitComment, selectedFeedback: selectedFeedback, view: view })] }));
12
+ function FeedbackCenterTabs({ composerProps, commentsByFeedbackId, commentsError, formId, onDirtyChange, onUrgencyChange, isCommentSubmitting, isCommentsLoading, isLoading, items, itemsError, onBack, onCancel, onSelectFeedback, onRetryComments, onRetryItems, onSubmittedChange, onSubmitComment, selectedFeedback, renderEvidence, view, }) {
13
+ return (_jsxs(_Fragment, { children: [_jsx(FeedbackCenterSubmitTab, { composerProps: composerProps, formId: formId, onCancel: onCancel, onDirtyChange: onDirtyChange, onSubmittedChange: onSubmittedChange, onUrgencyChange: onUrgencyChange }), _jsx(FeedbackCenterMineContent, { commentsByFeedbackId: commentsByFeedbackId, commentsError: commentsError, isCommentSubmitting: isCommentSubmitting, isCommentsLoading: isCommentsLoading, isLoading: isLoading, items: items, itemsError: itemsError, onBack: onBack, onSelectFeedback: onSelectFeedback, onRetryComments: onRetryComments, onRetryItems: onRetryItems, onSubmitComment: onSubmitComment, selectedFeedback: selectedFeedback, renderEvidence: renderEvidence, view: view })] }));
15
14
  }
16
- function FeedbackCenterTabsList() {
17
- return (_jsxs(TabsList, { className: "w-full", children: [_jsx(TabsTrigger, { value: "submit", children: "Send feedback" }), _jsx(TabsTrigger, { value: "mine", children: "My feedback" })] }));
15
+ function FeedbackCenterTabsList({ unreadReplyCount }) {
16
+ return (_jsxs(TabsList, { className: "w-full", children: [_jsx(TabsTrigger, { value: "submit", children: "Send feedback" }), _jsxs(TabsTrigger, { value: "mine", children: ["My feedback", unreadReplyCount > 0 ? _jsx(Badge, { variant: "secondary", children: unreadReplyCount }) : null] })] }));
18
17
  }
19
- function FeedbackCenterSubmitTab({ composerProps, formId, onCancel, onSubmittedChange, }) {
20
- return (_jsx(TabsContent, { className: "mt-0", value: "submit", children: _jsx(FeedbackComposer, { ...composerProps, actions: "external", formId: formId, onCancel: onCancel, onSubmittedChange: onSubmittedChange }) }));
21
- }
22
- function FeedbackCenterFooter({ activeTab, formId, isSubmitted, isSubmitting, onCancel, }) {
23
- if (activeTab !== "submit" || isSubmitted)
24
- return null;
25
- return (_jsxs(SheetFooter, { children: [_jsx(Button, { disabled: isSubmitting, onClick: onCancel, type: "button", variant: "outline", children: "Cancel" }), _jsxs(Button, { form: formId, loading: isSubmitting, type: "submit", children: [_jsx(SendIcon, {}), "Submit feedback"] })] }));
18
+ function FeedbackCenterSubmitTab({ composerProps, formId, onCancel, onDirtyChange, onSubmittedChange, onUrgencyChange, }) {
19
+ // forceMount keeps the composer draft alive across tab switches
20
+ return (_jsx(TabsContent, { className: "mt-0 data-[state=inactive]:hidden", forceMount: true, value: "submit", children: _jsx(FeedbackComposer, { ...composerProps, actions: "external", formId: formId, onCancel: onCancel, onDirtyChange: onDirtyChange, onSubmittedChange: onSubmittedChange, onUrgencyChange: onUrgencyChange }) }));
26
21
  }
27
22
  function FeedbackCenterMineContent(props) {
28
23
  return (_jsx(TabsContent, { className: "mt-0", value: "mine", children: _jsx(FeedbackCenterMineTab, { ...props }) }));
29
24
  }
30
- function FeedbackCenterMineTab({ isLoading, commentsByFeedbackId, isCommentSubmitting, isCommentsLoading, items, onBack, onSelectFeedback, onSubmitComment, selectedFeedback, view, }) {
25
+ function FeedbackCenterMineTab({ isLoading, commentsByFeedbackId, commentsError, isCommentSubmitting, isCommentsLoading, items, itemsError, onBack, onSelectFeedback, onRetryComments, onRetryItems, onSubmitComment, selectedFeedback, renderEvidence, view, }) {
31
26
  if (view === "detail") {
32
- return (_jsx(FeedbackCenterDetailPane, { comments: getFeedbackComments(commentsByFeedbackId, selectedFeedback), feedback: selectedFeedback, isCommentSubmitting: isCommentSubmitting, isCommentsLoading: isCommentsLoading, onBack: onBack, onSubmitComment: onSubmitComment }));
27
+ return (_jsx(FeedbackCenterDetailPane, { comments: getFeedbackComments(commentsByFeedbackId, selectedFeedback), commentsError: commentsError, feedback: selectedFeedback, isCommentSubmitting: isCommentSubmitting, isCommentsLoading: isCommentsLoading, onBack: onBack, onRetryComments: onRetryComments, onSubmitComment: onSubmitComment, renderEvidence: renderEvidence }));
33
28
  }
34
- return (_jsx(FeedbackUserList, { isLoading: isLoading, items: items, onSelectFeedback: onSelectFeedback, selectedId: selectedFeedback?.id }));
29
+ return (_jsx(FeedbackUserList, { error: itemsError, isLoading: isLoading, items: items, onSelectFeedback: onSelectFeedback, onRetry: onRetryItems, selectedId: selectedFeedback?.id }));
35
30
  }
36
- function FeedbackCenterDetailPane({ comments, feedback, isCommentSubmitting, isCommentsLoading, onBack, onSubmitComment, }) {
37
- return (_jsx(FeedbackUserDetail, { activityLabel: "Comments", activitySlot: _jsx(FeedbackCenterCommentThread, { comments: comments, feedback: feedback, isCommentSubmitting: isCommentSubmitting, isCommentsLoading: isCommentsLoading, onSubmitComment: onSubmitComment }), feedback: feedback, onBack: onBack }));
31
+ function FeedbackCenterDetailPane({ comments, commentsError, feedback, isCommentSubmitting, isCommentsLoading, onBack, onRetryComments, onSubmitComment, renderEvidence, }) {
32
+ return (_jsx(FeedbackUserDetail, { activityLabel: "Public conversation", activitySlot: _jsx(FeedbackCenterCommentThread, { comments: comments, error: commentsError, feedback: feedback, isCommentSubmitting: isCommentSubmitting, isCommentsLoading: isCommentsLoading, onSubmitComment: onSubmitComment, onRetry: onRetryComments }), evidenceSlot: feedback ? renderEvidence?.(feedback) : undefined, feedback: feedback, onBack: onBack }));
38
33
  }
39
34
  function getFeedbackComments(commentsByFeedbackId, feedback) {
40
35
  if (!feedback)
41
36
  return [];
42
37
  return commentsByFeedbackId[feedback.id] ?? [];
43
38
  }
44
- export { FeedbackCenterFooter, FeedbackCenterSheetHeader, FeedbackCenterTabs, };
39
+ export { FeedbackCenterSheetHeader, FeedbackCenterTabs, };
@@ -4,16 +4,22 @@ import type { FeedbackCommentSubmitHandler } from "./feedback-center-comments";
4
4
  import { type FeedbackCenterTab } from "./feedback-center-sheet-parts";
5
5
  import type { FeedbackComposerProps } from "./feedback-composer";
6
6
  import type { UserFeedbackItem } from "./feedback-tracking-types";
7
- type FeedbackCenterSheetProps = Omit<FeedbackComposerProps, "actions" | "formId" | "onCancel" | "onSubmittedChange"> & {
7
+ type FeedbackCenterSheetProps = Omit<FeedbackComposerProps, "actions" | "formId" | "onCancel" | "onDirtyChange" | "onSubmittedChange" | "onUrgencyChange"> & {
8
8
  commentsByFeedbackId?: Record<string, CommentItemData[]>;
9
+ commentsError?: Error | null;
9
10
  defaultTab?: FeedbackCenterTab;
10
11
  isCommentSubmitting?: boolean;
11
12
  isCommentsLoading?: boolean;
12
13
  isLoading?: boolean;
14
+ itemsError?: Error | null;
13
15
  items: UserFeedbackItem[];
16
+ onSelectFeedback?: (item: UserFeedbackItem | undefined) => void;
17
+ onRetryComments?: () => void;
18
+ onRetryItems?: () => void;
14
19
  onSubmitComment?: FeedbackCommentSubmitHandler;
15
20
  onOpenChange: (open: boolean) => void;
16
21
  open: boolean;
22
+ renderEvidence?: (item: UserFeedbackItem) => React.ReactNode;
17
23
  };
18
- declare function FeedbackCenterSheet({ commentsByFeedbackId, defaultTab, isCommentSubmitting, isCommentsLoading, isLoading, items, onSubmitComment, onOpenChange, open, ...composerProps }: FeedbackCenterSheetProps): React.JSX.Element;
24
+ declare function FeedbackCenterSheet(props: FeedbackCenterSheetProps): React.JSX.Element;
19
25
  export { FeedbackCenterSheet, type FeedbackCenterSheetProps };
@@ -1,36 +1,93 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
- import { FeedbackCenterFooter, FeedbackCenterSheetHeader, FeedbackCenterTabs, } from "./feedback-center-sheet-parts";
4
- import { Sheet, SheetBody, SheetContent } from "../ui/sheet";
3
+ import { FeedbackCenterFooter } from "./feedback-center-footer";
4
+ import { FeedbackCenterSheetHeader, FeedbackCenterTabs, } from "./feedback-center-sheet-parts";
5
+ import { FormSheet, useFormSheetActions } from "../ui/form-sheet";
6
+ import { SheetBody, SheetContent } from "../ui/sheet";
5
7
  import { Tabs } from "../ui/tabs";
6
- function FeedbackCenterSheet({ commentsByFeedbackId = {}, defaultTab = "submit", isCommentSubmitting = false, isCommentsLoading = false, isLoading = false, items, onSubmitComment, onOpenChange, open, ...composerProps }) {
8
+ function FeedbackCenterSheet(props) {
9
+ const state = useFeedbackCenterSheetState(props);
10
+ return (_jsx(FormSheet, { closeDisabled: props.isSubmitting ?? false, isDirty: state.isDirty, onOpenChange: props.onOpenChange, open: props.open, children: _jsx(FeedbackCenterSheetContent, { props: props, state: state }) }));
11
+ }
12
+ function FeedbackCenterSheetContent({ props, state, }) {
13
+ const { requestClose } = useFormSheetActions();
14
+ return (_jsx(SheetContent, { size: "md", children: _jsxs(Tabs, { className: "min-h-0 flex-1", onValueChange: setFeedbackCenterTab(state.handleTabChange), value: state.activeTab, children: [_jsx(FeedbackCenterSheetHeader, { unreadReplyCount: state.unreadReplyCount }), _jsx(SheetBody, { className: "pb-(--panel-padding)", children: _jsx(FeedbackCenterTabs, { composerProps: getFeedbackComposerProps(props), commentsByFeedbackId: props.commentsByFeedbackId ?? {}, commentsError: props.commentsError, formId: state.formId, isCommentSubmitting: props.isCommentSubmitting ?? false, isCommentsLoading: props.isCommentsLoading ?? false, isLoading: props.isLoading ?? false, items: props.items, itemsError: props.itemsError, onBack: state.handleBack, onCancel: requestClose, onDirtyChange: state.setIsDirty, onRetryComments: props.onRetryComments, onRetryItems: props.onRetryItems, onSelectFeedback: state.handleSelectFeedback, onSubmitComment: props.onSubmitComment, onSubmittedChange: state.setIsSubmitted, onUrgencyChange: state.setUrgency, renderEvidence: props.renderEvidence, selectedFeedback: state.selectedFeedback, view: state.view }) }), _jsx(FeedbackCenterFooter, { activeTab: state.activeTab, formId: state.formId, isSubmitted: state.isSubmitted, isSubmitting: props.isSubmitting ?? false, onCancel: requestClose, urgency: state.urgency })] }) }));
15
+ }
16
+ function useFeedbackCenterSheetState(props) {
17
+ const defaultTab = props.defaultTab ?? "submit";
18
+ const onSelectFeedback = props.onSelectFeedback;
7
19
  const formId = React.useId();
8
20
  const [activeTab, setActiveTab] = React.useState(defaultTab);
9
21
  const [isSubmitted, setIsSubmitted] = React.useState(false);
22
+ const [isDirty, setIsDirty] = React.useState(false);
10
23
  const [selectedId, setSelectedId] = React.useState();
24
+ const [urgency, setUrgency] = React.useState("normal");
11
25
  const [view, setView] = React.useState("list");
12
- const selectedFeedback = getSelectedFeedback(items, selectedId);
26
+ const selectedFeedback = getSelectedFeedback(props.items, selectedId);
27
+ const unreadReplyCount = props.items.reduce((count, item) => count + (item.unreadReplyCount ?? 0), 0);
13
28
  const handleBack = React.useCallback(() => {
29
+ setSelectedId(undefined);
14
30
  setView("list");
15
- }, []);
31
+ onSelectFeedback?.(undefined);
32
+ }, [onSelectFeedback]);
16
33
  const handleSelectFeedback = React.useCallback((item) => {
17
34
  setSelectedId(item.id);
18
35
  setView("detail");
19
- }, []);
36
+ onSelectFeedback?.(item);
37
+ }, [onSelectFeedback]);
20
38
  const handleTabChange = React.useCallback((tab) => {
21
39
  setActiveTab(tab);
22
- setIsSubmitted(false);
40
+ setSelectedId(undefined);
23
41
  setView("list");
24
- }, []);
42
+ onSelectFeedback?.(undefined);
43
+ }, [onSelectFeedback]);
44
+ const wasOpenRef = React.useRef(props.open);
25
45
  React.useEffect(() => {
26
- if (!open) {
27
- setActiveTab(defaultTab);
28
- setIsSubmitted(false);
29
- setSelectedId(undefined);
30
- setView("list");
46
+ if (props.open) {
47
+ wasOpenRef.current = true;
48
+ return;
31
49
  }
32
- }, [defaultTab, open]);
33
- return (_jsx(Sheet, { onOpenChange: onOpenChange, open: open, children: _jsx(SheetContent, { size: "md", children: _jsxs(Tabs, { className: "min-h-0 flex-1", onValueChange: setFeedbackCenterTab(handleTabChange), value: activeTab, children: [_jsx(FeedbackCenterSheetHeader, {}), _jsx(SheetBody, { className: "pb-(--panel-padding)", children: _jsx(FeedbackCenterTabs, { composerProps: composerProps, commentsByFeedbackId: commentsByFeedbackId, formId: formId, isCommentSubmitting: isCommentSubmitting, isCommentsLoading: isCommentsLoading, isLoading: isLoading, items: items, onBack: handleBack, onCancel: () => onOpenChange(false), onSelectFeedback: handleSelectFeedback, onSubmittedChange: setIsSubmitted, onSubmitComment: onSubmitComment, selectedFeedback: selectedFeedback, view: view }) }), _jsx(FeedbackCenterFooter, { activeTab: activeTab, formId: formId, isSubmitted: isSubmitted, isSubmitting: composerProps.isSubmitting ?? false, onCancel: () => onOpenChange(false) })] }) }) }));
50
+ if (!wasOpenRef.current)
51
+ return;
52
+ wasOpenRef.current = false;
53
+ setActiveTab(defaultTab);
54
+ setIsSubmitted(false);
55
+ setIsDirty(false);
56
+ setSelectedId(undefined);
57
+ setUrgency("normal");
58
+ setView("list");
59
+ // hosts tracking selection drop it when the sheet closes
60
+ onSelectFeedback?.(undefined);
61
+ }, [defaultTab, onSelectFeedback, props.open]);
62
+ return {
63
+ activeTab,
64
+ formId,
65
+ handleBack,
66
+ handleSelectFeedback,
67
+ handleTabChange,
68
+ isDirty,
69
+ isSubmitted,
70
+ selectedFeedback,
71
+ setIsDirty,
72
+ setIsSubmitted,
73
+ setUrgency,
74
+ unreadReplyCount,
75
+ urgency,
76
+ view,
77
+ };
78
+ }
79
+ function getFeedbackComposerProps(props) {
80
+ return {
81
+ acceptedFileTypes: props.acceptedFileTypes,
82
+ attachmentsEnabled: props.attachmentsEnabled,
83
+ defaultType: props.defaultType,
84
+ defaultUrgency: props.defaultUrgency,
85
+ isSubmitting: props.isSubmitting,
86
+ maxAttachmentSizeMb: props.maxAttachmentSizeMb,
87
+ maxAttachments: props.maxAttachments,
88
+ onSubmit: props.onSubmit,
89
+ urgencyEnabled: props.urgencyEnabled,
90
+ };
34
91
  }
35
92
  function getSelectedFeedback(items, selectedId) {
36
93
  if (!selectedId)
@@ -1,16 +1,16 @@
1
- import type { FeedbackType } from "./feedback-composer-types";
1
+ import type { FeedbackType, FeedbackUrgency } from "./feedback-composer-types";
2
2
  declare function FeedbackTypeField({ disabled, onTypeChange, type, }: {
3
3
  disabled: boolean;
4
4
  onTypeChange: (type: FeedbackType) => void;
5
5
  type: FeedbackType;
6
6
  }): import("@types/react").JSX.Element;
7
- declare function FeedbackSubmittedState({ onCancel, onSendAnother, referenceId, }: {
7
+ declare function FeedbackSubmittedState({ onCancel, onSendAnother, }: {
8
8
  onCancel?: () => void;
9
9
  onSendAnother: () => void;
10
- referenceId?: string;
11
10
  }): import("@types/react").JSX.Element;
12
- declare function FeedbackComposerActions({ isSubmitting, onCancel, }: {
11
+ declare function FeedbackComposerActions({ isSubmitting, onCancel, urgency, }: {
13
12
  isSubmitting: boolean;
14
13
  onCancel?: () => void;
14
+ urgency: FeedbackUrgency;
15
15
  }): import("@types/react").JSX.Element;
16
16
  export { FeedbackComposerActions, FeedbackSubmittedState, FeedbackTypeField };
@@ -31,11 +31,11 @@ function FeedbackTypeOption({ option }) {
31
31
  const Icon = option.icon;
32
32
  return (_jsxs(ToggleGroupItem, { className: "w-full px-2", value: option.value, children: [_jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate", children: option.label })] }));
33
33
  }
34
- function FeedbackSubmittedState({ onCancel, onSendAnother, referenceId, }) {
35
- return (_jsxs("div", { className: "grid gap-(--panel-gap)", children: [_jsxs(Alert, { children: [_jsx(CheckCircle2Icon, {}), _jsx(AlertTitle, { children: "Feedback submitted" }), _jsxs(AlertDescription, { children: [_jsx("p", { children: "Your feedback has been sent to the team." }), referenceId ? (_jsxs("p", { children: ["Reference", " ", _jsx("span", { className: "font-mono text-foreground tabular-nums", children: referenceId })] })) : null] })] }), _jsxs("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [_jsx(Button, { onClick: onSendAnother, type: "button", variant: "outline", children: "Send another" }), onCancel ? (_jsx(Button, { onClick: onCancel, type: "button", children: "Done" })) : null] })] }));
34
+ function FeedbackSubmittedState({ onCancel, onSendAnother, }) {
35
+ return (_jsxs("div", { className: "grid gap-(--panel-gap)", children: [_jsxs(Alert, { children: [_jsx(CheckCircle2Icon, {}), _jsx(AlertTitle, { children: "Feedback sent" }), _jsx(AlertDescription, { children: _jsx("p", { children: "The team will follow up here." }) })] }), _jsxs("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [_jsx(Button, { onClick: onSendAnother, type: "button", variant: "outline", children: "Send another" }), onCancel ? (_jsx(Button, { onClick: onCancel, type: "button", children: "Done" })) : null] })] }));
36
36
  }
37
- function FeedbackComposerActions({ isSubmitting, onCancel, }) {
38
- return (_jsxs("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [onCancel ? (_jsx(Button, { disabled: isSubmitting, onClick: onCancel, type: "button", variant: "outline", children: "Cancel" })) : null, _jsxs(Button, { loading: isSubmitting, type: "submit", children: [_jsx(SendIcon, {}), "Submit feedback"] })] }));
37
+ function FeedbackComposerActions({ isSubmitting, onCancel, urgency, }) {
38
+ return (_jsxs("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [onCancel ? (_jsx(Button, { disabled: isSubmitting, onClick: onCancel, type: "button", variant: "outline", children: "Cancel" })) : null, _jsxs(Button, { loading: isSubmitting, type: "submit", variant: urgency === "emergency" ? "destructive" : "default", children: [_jsx(SendIcon, {}), urgency === "emergency" ? "Send emergency" : "Submit feedback"] })] }));
39
39
  }
40
40
  function isFeedbackType(value) {
41
41
  return feedbackTypeOptions.some((option) => option.value === value);
@@ -1,11 +1,12 @@
1
+ import type { FeedbackAffectedScope, FeedbackUrgency } from "./feedback-tracking-types";
1
2
  type FeedbackType = "bug" | "feature" | "general";
2
3
  type FeedbackSubmitValues = {
4
+ affectedScope: FeedbackAffectedScope;
3
5
  attachments: File[];
4
6
  description: string;
5
7
  subject: string;
6
8
  type: FeedbackType;
9
+ urgency: FeedbackUrgency;
7
10
  };
8
- type FeedbackSubmitResult = {
9
- referenceId?: string;
10
- };
11
- export type { FeedbackSubmitResult, FeedbackSubmitValues, FeedbackType };
11
+ type FeedbackValidationError = "description" | "emergency_confirmation" | "subject" | undefined;
12
+ export type { FeedbackAffectedScope, FeedbackSubmitValues, FeedbackType, FeedbackUrgency, FeedbackValidationError, };
@@ -1,17 +1,21 @@
1
1
  import * as React from "react";
2
- import type { FeedbackSubmitResult, FeedbackSubmitValues, FeedbackType } from "./feedback-composer-types";
2
+ import type { FeedbackSubmitValues, FeedbackType, FeedbackUrgency } from "./feedback-composer-types";
3
3
  type FeedbackComposerProps = {
4
4
  acceptedFileTypes?: string;
5
5
  actions?: "inline" | "external";
6
6
  attachmentsEnabled?: boolean;
7
7
  defaultType?: FeedbackType;
8
+ defaultUrgency?: FeedbackUrgency;
8
9
  formId?: string;
9
10
  isSubmitting?: boolean;
10
11
  maxAttachmentSizeMb?: number;
11
12
  maxAttachments?: number;
12
13
  onCancel?: () => void;
14
+ onDirtyChange?: (dirty: boolean) => void;
13
15
  onSubmittedChange?: (submitted: boolean) => void;
14
- onSubmit: (values: FeedbackSubmitValues) => void | Promise<void | FeedbackSubmitResult>;
16
+ onUrgencyChange?: (urgency: FeedbackUrgency) => void;
17
+ onSubmit: (values: FeedbackSubmitValues) => void | Promise<void>;
18
+ urgencyEnabled?: boolean;
15
19
  };
16
20
  declare function FeedbackComposer(props: FeedbackComposerProps): React.JSX.Element;
17
- export { FeedbackComposer, type FeedbackComposerProps, type FeedbackSubmitResult, type FeedbackSubmitValues, type FeedbackType, };
21
+ export { FeedbackComposer, type FeedbackComposerProps, type FeedbackSubmitValues, type FeedbackType, };