@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.
- package/dist/components/blocks/comment-composer.d.ts +3 -1
- package/dist/components/blocks/comment-composer.js +13 -7
- package/dist/components/blocks/comment-thread.d.ts +2 -1
- package/dist/components/blocks/comment-thread.js +2 -2
- package/dist/components/blocks/feedback-center-comments.d.ts +3 -1
- package/dist/components/blocks/feedback-center-comments.js +9 -3
- package/dist/components/blocks/feedback-center-footer.d.ts +12 -0
- package/dist/components/blocks/feedback-center-footer.js +10 -0
- package/dist/components/blocks/feedback-center-sheet-parts.d.ts +14 -11
- package/dist/components/blocks/feedback-center-sheet-parts.js +17 -22
- package/dist/components/blocks/feedback-center-sheet.d.ts +8 -2
- package/dist/components/blocks/feedback-center-sheet.js +72 -15
- package/dist/components/blocks/feedback-composer-parts.d.ts +4 -4
- package/dist/components/blocks/feedback-composer-parts.js +4 -4
- package/dist/components/blocks/feedback-composer-types.d.ts +5 -4
- package/dist/components/blocks/feedback-composer.d.ts +7 -3
- package/dist/components/blocks/feedback-composer.js +49 -79
- package/dist/components/blocks/feedback-detail-sheet.d.ts +9 -3
- package/dist/components/blocks/feedback-detail-sheet.js +27 -9
- package/dist/components/blocks/feedback-emergency-fields.d.ts +17 -0
- package/dist/components/blocks/feedback-emergency-fields.js +32 -0
- package/dist/components/blocks/feedback-metrics.js +1 -1
- package/dist/components/blocks/feedback-sheet.d.ts +1 -1
- package/dist/components/blocks/feedback-sheet.js +17 -4
- package/dist/components/blocks/feedback-status-badge.js +3 -3
- package/dist/components/blocks/feedback-table-row.js +8 -18
- package/dist/components/blocks/feedback-table-sort.js +4 -4
- package/dist/components/blocks/feedback-table.js +2 -2
- package/dist/components/blocks/feedback-tracking-types.d.ts +20 -11
- package/dist/components/blocks/feedback-tracking-types.js +15 -7
- package/dist/components/blocks/feedback-type-badge.js +1 -1
- package/dist/components/blocks/feedback-user-detail.d.ts +2 -1
- package/dist/components/blocks/feedback-user-detail.js +13 -6
- package/dist/components/blocks/feedback-user-list.d.ts +3 -1
- package/dist/components/blocks/feedback-user-list.js +11 -3
- package/dist/components/blocks/submit-feedback-composer.d.ts +5 -0
- package/dist/components/blocks/submit-feedback-composer.js +40 -0
- package/dist/components/blocks/use-feedback-composer-state.d.ts +27 -0
- package/dist/components/blocks/use-feedback-composer-state.js +60 -0
- package/dist/components/ui/form-sheet.d.ts +15 -0
- package/dist/components/ui/form-sheet.js +106 -0
- package/dist/entries/feedback-center-footer.d.ts +1 -0
- package/dist/entries/feedback-center-footer.js +1 -0
- package/dist/entries/feedback-emergency-fields.d.ts +1 -0
- package/dist/entries/feedback-emergency-fields.js +1 -0
- package/dist/entries/form-sheet.d.ts +1 -0
- package/dist/entries/form-sheet.js +1 -0
- package/dist/entries/submit-feedback-composer.d.ts +1 -0
- package/dist/entries/submit-feedback-composer.js +1 -0
- package/dist/entries/use-feedback-composer-state.d.ts +1 -0
- package/dist/entries/use-feedback-composer-state.js +1 -0
- 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
|
-
|
|
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:
|
|
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
|
-
|
|
4
|
-
|
|
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(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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 {
|
|
8
|
-
import { SheetDescription,
|
|
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" }),
|
|
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
|
-
|
|
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: "
|
|
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 {
|
|
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(
|
|
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
|
|
4
|
-
import {
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
setSelectedId(undefined);
|
|
30
|
-
setView("list");
|
|
46
|
+
if (props.open) {
|
|
47
|
+
wasOpenRef.current = true;
|
|
48
|
+
return;
|
|
31
49
|
}
|
|
32
|
-
|
|
33
|
-
|
|
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,
|
|
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,
|
|
35
|
-
return (_jsxs("div", { className: "grid gap-(--panel-gap)", children: [_jsxs(Alert, { children: [_jsx(CheckCircle2Icon, {}), _jsx(AlertTitle, { children: "Feedback
|
|
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
|
|
9
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
21
|
+
export { FeedbackComposer, type FeedbackComposerProps, type FeedbackSubmitValues, type FeedbackType, };
|