anton-bakker-feedback 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/react/admin/FeedbackDetailPage.d.ts +12 -0
- package/dist/react/admin/FeedbackDetailPage.d.ts.map +1 -0
- package/dist/react/admin/FeedbackListPage.d.ts +20 -0
- package/dist/react/admin/FeedbackListPage.d.ts.map +1 -0
- package/dist/react/admin/FeedbackUpdatePage.d.ts +12 -0
- package/dist/react/admin/FeedbackUpdatePage.d.ts.map +1 -0
- package/dist/react/admin/__tests__/FeedbackDetailPage.test.d.ts +2 -0
- package/dist/react/admin/__tests__/FeedbackDetailPage.test.d.ts.map +1 -0
- package/dist/react/admin/__tests__/FeedbackListPage.test.d.ts +2 -0
- package/dist/react/admin/__tests__/FeedbackListPage.test.d.ts.map +1 -0
- package/dist/react/admin/__tests__/FeedbackUpdatePage.test.d.ts +2 -0
- package/dist/react/admin/__tests__/FeedbackUpdatePage.test.d.ts.map +1 -0
- package/dist/react/admin/hooks/useStatusTransition.d.ts +18 -0
- package/dist/react/admin/hooks/useStatusTransition.d.ts.map +1 -0
- package/dist/react/admin/index.d.ts +7 -0
- package/dist/react/admin/index.d.ts.map +1 -0
- package/dist/react/admin/index.js +406 -0
- package/dist/react/admin/index.js.map +1 -0
- package/dist/react/admin/types.d.ts +44 -0
- package/dist/react/admin/types.d.ts.map +1 -0
- package/dist/react.js +6 -6
- package/dist/react.js.map +1 -1
- package/package.json +14 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/react/admin/FeedbackListPage.tsx","../../../src/react/admin/FeedbackDetailPage.tsx","../../../src/react/admin/hooks/useStatusTransition.ts","../../../src/react/admin/FeedbackUpdatePage.tsx"],"sourcesContent":["/**\n * FeedbackListPage.tsx\n * Location: src/react/admin/FeedbackListPage.tsx\n *\n * Admin list page for browsing and filtering feedback items.\n * Thin wrapper around ListPage from anton-bakker-tailwind-components\n * for visual consistency with sagrosoft20.\n */\nimport * as React from 'react';\nimport { ListPage, Badge } from 'anton-bakker-tailwind-components';\nimport type { SimpleColumn, FilterBarProps } from 'anton-bakker-tailwind-components';\nimport type { StoredFeedbackItem, FeedbackStatus, FeedbackType } from '../../core/types.js';\nimport type { FeedbackListPageProps } from './types.js';\n\nconst STATUS_VARIANT: Record<FeedbackStatus, 'default' | 'success' | 'warning' | 'destructive' | 'secondary'> = {\n open: 'warning',\n approved: 'default',\n deferred: 'secondary',\n resolved: 'success',\n wontfix: 'destructive',\n};\n\nconst TYPE_VARIANT: Record<FeedbackType, 'default' | 'success' | 'warning' | 'destructive' | 'secondary'> = {\n bug: 'destructive',\n feature: 'default',\n ux: 'warning',\n other: 'secondary',\n};\n\nfunction StatusBadge({ status }: { status: FeedbackStatus }) {\n return <Badge variant={STATUS_VARIANT[status] ?? 'secondary'}>{status}</Badge>;\n}\n\nfunction TypeBadge({ type }: { type: FeedbackType }) {\n return <Badge variant={TYPE_VARIANT[type] ?? 'secondary'}>{type}</Badge>;\n}\n\nconst FeedbackListPage = React.forwardRef<HTMLDivElement, FeedbackListPageProps>(\n ({ items, loading = false, onNavigate, className, emptyStateMessage, currentUser, ...rest }, ref) => {\n const [search, setSearch] = React.useState('');\n const [statusFilter, setStatusFilter] = React.useState('');\n const [typeFilter, setTypeFilter] = React.useState('');\n\n const filteredItems = React.useMemo(() => {\n let result = items;\n if (statusFilter) result = result.filter((i) => i.status === statusFilter);\n if (typeFilter) result = result.filter((i) => i.type === typeFilter);\n if (search) {\n const q = search.toLowerCase();\n result = result.filter((i) => i.content?.toLowerCase().includes(q) || i.id.toLowerCase().includes(q));\n }\n return result;\n }, [items, statusFilter, typeFilter, search]);\n\n const columns: SimpleColumn<StoredFeedbackItem>[] = React.useMemo(() => [\n { header: 'Content', accessor: (row) => row.content?.slice(0, 60) || row.id, sortable: true },\n { header: 'Type', accessor: (row) => <TypeBadge type={row.type} />, sortable: true },\n { header: 'Status', accessor: (row) => <StatusBadge status={row.status} />, sortable: true },\n { header: 'Priority', accessor: 'priority', sortable: true },\n { header: 'Reporters', accessor: 'reporterCount', sortable: true },\n {\n header: 'Date',\n accessor: (row) => row.submittedAt ? new Date(row.submittedAt).toLocaleDateString() : '—',\n sortable: true,\n },\n ], []);\n\n const filterBar: FilterBarProps | undefined = (statusFilter || typeFilter) ? {\n filters: [\n ...(statusFilter ? [{ field: 'status', label: `Status: ${statusFilter}`, value: statusFilter }] : []),\n ...(typeFilter ? [{ field: 'type', label: `Type: ${typeFilter}`, value: typeFilter }] : []),\n ],\n onFilterRemove: (field: string) => {\n if (field === 'status') setStatusFilter('');\n if (field === 'type') setTypeFilter('');\n },\n onFiltersClear: () => { setStatusFilter(''); setTypeFilter(''); },\n } : undefined;\n\n const headerActions = (\n <div className=\"flex items-center gap-2\">\n <select\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n className=\"h-8 rounded-md border border-border bg-transparent px-2 text-sm\"\n aria-label=\"Filter by status\"\n >\n <option value=\"\">All statuses</option>\n <option value=\"open\">Open</option>\n <option value=\"approved\">Approved</option>\n <option value=\"deferred\">Deferred</option>\n <option value=\"resolved\">Resolved</option>\n <option value=\"wontfix\">Won't fix</option>\n </select>\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value)}\n className=\"h-8 rounded-md border border-border bg-transparent px-2 text-sm\"\n aria-label=\"Filter by type\"\n >\n <option value=\"\">All types</option>\n <option value=\"bug\">Bug</option>\n <option value=\"feature\">Feature</option>\n <option value=\"ux\">UX</option>\n <option value=\"other\">Other</option>\n </select>\n </div>\n );\n\n const listProps = {\n title: 'Feedback' as const,\n columns,\n items: filteredItems,\n loading,\n onRowClick: (row: StoredFeedbackItem) => onNavigate(`/feedback/${row.id}`),\n search,\n onSearchChange: setSearch,\n searchPlaceholder: 'Search feedback...',\n headerActions,\n testId: 'feedback-list-page',\n ...(filterBar ? { filterBar } : {}),\n ...(emptyStateMessage ? { emptyState: emptyStateMessage } : {}),\n ...(className ? { className } : {}),\n };\n\n return <ListPage<StoredFeedbackItem> ref={ref} {...listProps} />;\n },\n);\nFeedbackListPage.displayName = 'FeedbackListPage';\n\nexport { FeedbackListPage, StatusBadge, TypeBadge };\n","/**\n * FeedbackDetailPage.tsx\n * Location: src/react/admin/FeedbackDetailPage.tsx\n *\n * Admin detail page for viewing a single feedback item.\n * Uses DetailPage + DetailSection + DetailRow from anton-bakker-tailwind-components.\n */\nimport * as React from 'react';\nimport { DetailPage, DetailSection, DetailRow, Badge, Button } from 'anton-bakker-tailwind-components';\nimport type { StoredFeedbackItem } from '../../core/types.js';\nimport type { FeedbackDetailPageProps } from './types.js';\nimport { StatusBadge, TypeBadge } from './FeedbackListPage.js';\n\nconst FeedbackDetailPage = React.forwardRef<HTMLDivElement, FeedbackDetailPageProps>(\n ({ feedbackId, fetchItem, onNavigate, currentUser, className }, ref) => {\n const [item, setItem] = React.useState<StoredFeedbackItem | null>(null);\n const [loading, setLoading] = React.useState(true);\n const [error, setError] = React.useState<string | null>(null);\n\n React.useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(null);\n fetchItem(feedbackId)\n .then((data) => { if (!cancelled) setItem(data); })\n .catch((err) => { if (!cancelled) setError(err?.message ?? 'Failed to load item'); })\n .finally(() => { if (!cancelled) setLoading(false); });\n return () => { cancelled = true; };\n }, [feedbackId, fetchItem]);\n\n if (error) {\n return (\n <div ref={ref} className={className}>\n <DetailPage title=\"Feedback\" headerActions={<Button variant=\"outline\" onClick={() => onNavigate('/feedback')}>Back to list</Button>}>\n <div className=\"p-6 text-destructive\">{error}</div>\n </DetailPage>\n </div>\n );\n }\n\n const headerActions = (\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" onClick={() => onNavigate('/feedback')}>Back to list</Button>\n <Button onClick={() => onNavigate(`/feedback/${feedbackId}/edit`)}>Edit</Button>\n </div>\n );\n\n return (\n <div ref={ref} className={className}>\n <DetailPage\n title={item?.content?.slice(0, 50) || `Feedback ${feedbackId.slice(0, 12)}…`}\n headerActions={headerActions}\n loading={loading}\n testId=\"feedback-detail-page\"\n >\n {item && (\n <>\n <DetailSection title=\"Overview\">\n <DetailRow label=\"Status\"><StatusBadge status={item.status} /></DetailRow>\n <DetailRow label=\"Type\"><TypeBadge type={item.type} /></DetailRow>\n <DetailRow label=\"Priority\">{item.priority}</DetailRow>\n <DetailRow label=\"Environment\">{item.environment || '—'}</DetailRow>\n <DetailRow label=\"Submitted\">{item.submittedAt ? new Date(item.submittedAt).toLocaleString() : '—'}</DetailRow>\n <DetailRow label=\"Reporters\">{item.reporterCount ?? 1}</DetailRow>\n {item.assignedTo && <DetailRow label=\"Assigned to\">{item.assignedTo}</DetailRow>}\n {item.scope && <DetailRow label=\"Scope\"><Badge variant=\"outline\">{item.scope}</Badge></DetailRow>}\n {item.size && <DetailRow label=\"Size\"><Badge variant=\"outline\">{item.size}</Badge></DetailRow>}\n </DetailSection>\n\n <DetailSection title=\"Content\">\n <DetailRow label=\"Description\">\n <p className=\"whitespace-pre-wrap\">{item.content}</p>\n </DetailRow>\n </DetailSection>\n\n {item.dialogueHistory && item.dialogueHistory.length > 0 && (\n <DetailSection title=\"Dialogue History\" collapsible defaultOpen={false}>\n {item.dialogueHistory.map((entry, i) => (\n <DetailRow key={i} label={entry.role === 'user' ? 'User' : 'Assistant'}>\n <p className=\"whitespace-pre-wrap text-sm\">{entry.content}</p>\n {entry.timestamp && (\n <span className=\"text-xs text-muted-foreground\">\n {new Date(entry.timestamp).toLocaleString()}\n </span>\n )}\n </DetailRow>\n ))}\n </DetailSection>\n )}\n\n {item.context && (\n <DetailSection title=\"Browser Context\" collapsible defaultOpen={false}>\n {item.context.url && <DetailRow label=\"URL\">{item.context.url}</DetailRow>}\n {item.context.userAgent && <DetailRow label=\"User Agent\">{item.context.userAgent}</DetailRow>}\n {item.context.viewport && <DetailRow label=\"Viewport\">{`${item.context.viewport.width}×${item.context.viewport.height}`}</DetailRow>}\n </DetailSection>\n )}\n\n {item.tags && item.tags.length > 0 && (\n <DetailSection title=\"Tags\">\n <DetailRow label=\"Tags\">\n <div className=\"flex flex-wrap gap-1\">\n {item.tags.map((tag) => <Badge key={tag} variant=\"outline\">{tag}</Badge>)}\n </div>\n </DetailRow>\n </DetailSection>\n )}\n </>\n )}\n </DetailPage>\n </div>\n );\n },\n);\nFeedbackDetailPage.displayName = 'FeedbackDetailPage';\n\nexport { FeedbackDetailPage };\n","/**\n * useStatusTransition — pure hook encoding the FeedbackStatus transition table.\n *\n * Returns the set of valid target statuses reachable from the current status.\n * The current status is excluded from allowedTargets (can't transition to self).\n *\n * No side effects — same inputs always produce same outputs.\n */\nimport type { FeedbackStatus } from '../../../core/types.js';\n\n/**\n * Valid transitions for each FeedbackStatus.\n * open → resolved is deliberately excluded (must go through approved first).\n */\nconst ALLOWED_TRANSITIONS: Record<FeedbackStatus, FeedbackStatus[]> = {\n open: ['approved', 'deferred', 'wontfix'],\n approved: ['open', 'resolved', 'deferred'],\n deferred: ['open', 'approved', 'wontfix'],\n resolved: ['open', 'approved'],\n wontfix: ['open', 'deferred'],\n};\n\nexport interface UseStatusTransitionResult {\n /** Statuses reachable from the current status (current status excluded) */\n allowedTargets: FeedbackStatus[];\n}\n\n/**\n * Returns the allowed target statuses from the given current status.\n *\n * @param currentStatus - The status the item currently has\n * @returns Object with `allowedTargets` — statuses the item can transition to\n *\n * @example\n * const { allowedTargets } = useStatusTransition('open');\n * // allowedTargets → ['approved', 'deferred', 'wontfix']\n */\nexport function useStatusTransition(currentStatus: FeedbackStatus): UseStatusTransitionResult {\n const allowedTargets = ALLOWED_TRANSITIONS[currentStatus] ?? [];\n return { allowedTargets };\n}\n","/**\n * FeedbackUpdatePage.tsx\n * Location: src/react/admin/FeedbackUpdatePage.tsx\n *\n * Admin update page for editing mutable fields of a feedback item.\n * Uses UpdatePage + EnumField + TextField + TextareaField from anton-bakker-tailwind-components.\n */\nimport * as React from 'react';\nimport { UpdatePage, EnumField, TextField, TextareaField, Button, SaveFooter, Badge } from 'anton-bakker-tailwind-components';\nimport type { StoredFeedbackItem, FeedbackStatus, FeedbackType, FeedbackScope, FeedbackSize } from '../../core/types.js';\nimport type { FeedbackUpdatePageProps } from './types.js';\nimport { useStatusTransition } from './hooks/useStatusTransition.js';\n\nconst STATUS_OPTIONS = [\n { value: 'open', label: 'Open' },\n { value: 'approved', label: 'Approved' },\n { value: 'deferred', label: 'Deferred' },\n { value: 'resolved', label: 'Resolved' },\n { value: 'wontfix', label: \"Won't Fix\" },\n];\n\nconst TYPE_OPTIONS = [\n { value: 'bug', label: 'Bug' },\n { value: 'feature', label: 'Feature' },\n { value: 'ux', label: 'UX Issue' },\n { value: 'other', label: 'Other' },\n];\n\nconst SCOPE_OPTIONS = [\n { value: '', label: '— not set —' },\n { value: 'component', label: 'Component' },\n { value: 'page', label: 'Page' },\n { value: 'workflow', label: 'Workflow' },\n];\n\nconst SIZE_OPTIONS = [\n { value: '', label: '— not set —' },\n { value: 'trivial', label: 'Trivial' },\n { value: 'small', label: 'Small' },\n { value: 'medium', label: 'Medium' },\n { value: 'large', label: 'Large' },\n];\n\nconst FeedbackUpdatePage = React.forwardRef<HTMLDivElement, FeedbackUpdatePageProps>(\n ({ feedbackId, fetchItem, updateItem, onNavigate, currentUser, className }, ref) => {\n const [item, setItem] = React.useState<StoredFeedbackItem | null>(null);\n const [loading, setLoading] = React.useState(true);\n const [saving, setSaving] = React.useState(false);\n\n // Form state\n const [status, setStatus] = React.useState<FeedbackStatus>('open');\n const [type, setType] = React.useState<FeedbackType>('bug');\n const [scope, setScope] = React.useState<string>('');\n const [size, setSize] = React.useState<string>('');\n const [assignedTo, setAssignedTo] = React.useState('');\n const [tags, setTags] = React.useState('');\n const [correctionNote, setCorrectionNote] = React.useState('');\n\n // Load item\n React.useEffect(() => {\n let cancelled = false;\n setLoading(true);\n fetchItem(feedbackId)\n .then((data) => {\n if (cancelled) return;\n setItem(data);\n setStatus(data.status);\n setType(data.type);\n setScope(data.scope ?? '');\n setSize(data.size ?? '');\n setAssignedTo(data.assignedTo ?? '');\n setTags(data.tags?.join(', ') ?? '');\n })\n .finally(() => { if (!cancelled) setLoading(false); });\n return () => { cancelled = true; };\n }, [feedbackId, fetchItem]);\n\n const { allowedTargets } = useStatusTransition(status);\n const statusOptions = STATUS_OPTIONS.filter(\n (opt) => opt.value === status || allowedTargets.includes(opt.value as FeedbackStatus),\n );\n\n // Dirty detection\n const classificationChanged = item && (type !== item.type || scope !== (item.scope ?? '') || size !== (item.size ?? ''));\n const isDirty = item && (\n status !== item.status ||\n type !== item.type ||\n scope !== (item.scope ?? '') ||\n size !== (item.size ?? '') ||\n assignedTo !== (item.assignedTo ?? '') ||\n tags !== (item.tags?.join(', ') ?? '')\n );\n\n const handleSave = async () => {\n if (!item) return;\n setSaving(true);\n try {\n const patch: Partial<StoredFeedbackItem> = { lastModifiedAt: new Date().toISOString() };\n if (status !== item.status) patch.status = status;\n if (type !== item.type) patch.type = type;\n if (scope !== (item.scope ?? '')) (patch as Record<string, unknown>).scope = scope || undefined;\n if (size !== (item.size ?? '')) (patch as Record<string, unknown>).size = size || undefined;\n if (assignedTo !== (item.assignedTo ?? '')) (patch as Record<string, unknown>).assignedTo = assignedTo || undefined;\n const newTags = tags.split(',').map((t) => t.trim()).filter(Boolean);\n if (JSON.stringify(newTags) !== JSON.stringify(item.tags ?? [])) patch.tags = newTags;\n\n if (status === 'resolved' && item.status !== 'resolved') {\n patch.resolvedAt = new Date().toISOString();\n patch.resolvedBy = currentUser?.userId ?? 'unknown';\n }\n\n if (classificationChanged && correctionNote) {\n patch.corrections = [\n ...(item.corrections ?? []),\n {\n field: [type !== item.type && 'type', scope !== (item.scope ?? '') && 'scope', size !== (item.size ?? '') && 'size'].filter(Boolean).join(', '),\n before: { type: item.type, scope: item.scope, size: item.size },\n after: { type, scope: scope || undefined, size: size || undefined },\n correctedBy: currentUser?.userId ?? 'unknown',\n correctedAt: new Date().toISOString(),\n reason: correctionNote,\n },\n ];\n }\n\n await updateItem(feedbackId, patch);\n onNavigate(`/feedback/${feedbackId}`);\n } finally {\n setSaving(false);\n }\n };\n\n const headerActions = (\n <Button variant=\"outline\" onClick={() => onNavigate(`/feedback/${feedbackId}`)}>\n Cancel\n </Button>\n );\n\n return (\n <div ref={ref} className={className}>\n <UpdatePage\n title={`Edit: ${item?.content?.slice(0, 40) || feedbackId.slice(0, 12)}…`}\n headerActions={headerActions}\n dataLoading={loading}\n actions={\n <SaveFooter dirty={!!isDirty} saving={saving}>\n <Button variant=\"outline\" onClick={() => onNavigate(`/feedback/${feedbackId}`)}>Cancel</Button>\n <Button\n onClick={handleSave}\n disabled={saving || !isDirty || (!!classificationChanged && !correctionNote)}\n >\n {saving ? 'Saving…' : 'Save changes'}\n </Button>\n </SaveFooter>\n }\n testId=\"feedback-update-page\"\n >\n {item && (\n <div className=\"space-y-6\">\n {/* Read-only context */}\n <div className=\"rounded-lg border border-border p-4 bg-muted/30\">\n <p className=\"text-sm text-muted-foreground mb-2\">Original submission</p>\n <p className=\"whitespace-pre-wrap text-sm\">{item.content}</p>\n <div className=\"flex gap-3 mt-3 text-xs text-muted-foreground\">\n <span>{item.submittedAt ? new Date(item.submittedAt).toLocaleString() : ''}</span>\n <span>{item.environment}</span>\n <span>{item.reporterCount ?? 1} reporter(s)</span>\n </div>\n </div>\n\n {/* Editable fields */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <EnumField\n label=\"Status\"\n options={statusOptions}\n value={status}\n onChange={(v) => setStatus(v as FeedbackStatus)}\n />\n <EnumField\n label=\"Type\"\n options={TYPE_OPTIONS}\n value={type}\n onChange={(v) => setType(v as FeedbackType)}\n />\n <EnumField\n label=\"Scope\"\n options={SCOPE_OPTIONS}\n value={scope}\n onChange={setScope}\n />\n <EnumField\n label=\"Size\"\n options={SIZE_OPTIONS}\n value={size}\n onChange={setSize}\n />\n <TextField\n label=\"Assigned to\"\n value={assignedTo}\n onChange={(e) => setAssignedTo(e.target.value)}\n placeholder={currentUser?.name ?? currentUser?.userId ?? ''}\n {...(currentUser ? { description: `You: ${currentUser.name ?? currentUser.userId}` } : {})}\n />\n <TextField\n label=\"Tags\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n placeholder=\"Comma-separated tags\"\n description=\"Separate multiple tags with commas\"\n />\n </div>\n\n {/* Correction note (required when classification changes) */}\n {classificationChanged && (\n <TextareaField\n label=\"Correction note\"\n value={correctionNote}\n onChange={(e) => setCorrectionNote(e.target.value)}\n required\n {...(!correctionNote ? { error: 'Required when changing type, scope, or size' } : {})}\n placeholder=\"Explain why this classification is being changed...\"\n />\n )}\n\n {/* Existing tags display */}\n {item.tags && item.tags.length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {item.tags.map((tag) => <Badge key={tag} variant=\"outline\">{tag}</Badge>)}\n </div>\n )}\n </div>\n )}\n </UpdatePage>\n </div>\n );\n },\n);\nFeedbackUpdatePage.displayName = 'FeedbackUpdatePage';\n\nexport { FeedbackUpdatePage };\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { StoredFeedbackItem } from '../../core/types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base props shared by all admin page components.
|
|
5
|
+
* Follows Inversion of Control — consumer provides navigation and user context.
|
|
6
|
+
*/
|
|
7
|
+
export interface AdminPageProps {
|
|
8
|
+
className?: string;
|
|
9
|
+
currentUser?: {
|
|
10
|
+
userId: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
};
|
|
13
|
+
onNavigate: (path: string) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Props for the FeedbackListPage component.
|
|
17
|
+
* Consumer provides items (already fetched); component handles filtering/sorting via
|
|
18
|
+
* anton-bakker-tailwind-components ListPage template.
|
|
19
|
+
*/
|
|
20
|
+
export interface FeedbackListPageProps extends AdminPageProps {
|
|
21
|
+
items: StoredFeedbackItem[];
|
|
22
|
+
loading?: boolean;
|
|
23
|
+
emptyStateMessage?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Props for the FeedbackDetailPage component.
|
|
27
|
+
* Consumer provides fetchItem; component handles loading/display via
|
|
28
|
+
* anton-bakker-tailwind-components DetailPage template.
|
|
29
|
+
*/
|
|
30
|
+
export interface FeedbackDetailPageProps extends AdminPageProps {
|
|
31
|
+
feedbackId: string;
|
|
32
|
+
fetchItem: (id: string) => Promise<StoredFeedbackItem>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Props for the FeedbackUpdatePage component.
|
|
36
|
+
* Consumer provides fetchItem + updateItem; component handles form state via
|
|
37
|
+
* anton-bakker-tailwind-components UpdatePage template.
|
|
38
|
+
*/
|
|
39
|
+
export interface FeedbackUpdatePageProps extends AdminPageProps {
|
|
40
|
+
feedbackId: string;
|
|
41
|
+
fetchItem: (id: string) => Promise<StoredFeedbackItem>;
|
|
42
|
+
updateItem: (id: string, patch: Partial<StoredFeedbackItem>) => Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/admin/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,cAAc;IAC3D,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACxD;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E"}
|
package/dist/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import React__default, { createContext, lazy, useMemo, useReducer, useEffect, useCallback, Suspense, useContext, useState, useRef } from "react";
|
|
3
3
|
import { v as validateConfig, b as resolveConfig, d as detectLocale, t, w as withSpan } from "./chunks/i18n-RpKqSWAT.js";
|
|
4
4
|
const FeedbackContext = createContext(null);
|
|
5
5
|
FeedbackContext.displayName = "FeedbackContext";
|
|
@@ -313,7 +313,7 @@ function FeedbackProvider({
|
|
|
313
313
|
};
|
|
314
314
|
}, [resolvedConfig, internalState, locale, open, close]);
|
|
315
315
|
if (!contextValue) {
|
|
316
|
-
return
|
|
316
|
+
return React__default.createElement(React__default.Fragment, null, children);
|
|
317
317
|
}
|
|
318
318
|
return /* @__PURE__ */ jsxs(FeedbackContext.Provider, { value: contextValue, children: [
|
|
319
319
|
children,
|
|
@@ -405,7 +405,7 @@ function injectStyles() {
|
|
|
405
405
|
function FeedbackButton({ className, label }) {
|
|
406
406
|
const ctx = useContext(FeedbackContext);
|
|
407
407
|
const { state, open } = useFeedback();
|
|
408
|
-
|
|
408
|
+
React__default.useEffect(() => {
|
|
409
409
|
injectStyles();
|
|
410
410
|
}, []);
|
|
411
411
|
if (state.status !== "idle") return null;
|
|
@@ -1165,10 +1165,10 @@ function Portal({ children }) {
|
|
|
1165
1165
|
setIsClient(true);
|
|
1166
1166
|
}, []);
|
|
1167
1167
|
if (!isClient || typeof document === "undefined") {
|
|
1168
|
-
return
|
|
1168
|
+
return React__default.createElement(React__default.Fragment, null, children);
|
|
1169
1169
|
}
|
|
1170
|
-
return
|
|
1171
|
-
|
|
1170
|
+
return React__default.createElement(
|
|
1171
|
+
React__default.Fragment,
|
|
1172
1172
|
null,
|
|
1173
1173
|
children
|
|
1174
1174
|
);
|