@burdenoff/microfe-bigconsole 2026.608.1 → 2026.612.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +13 -8
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -1
- package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetRegistry.js +2 -1
- package/dist/bigconsole/components/widgets/WidgetRegistry.js.map +1 -1
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +4 -4
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js.map +1 -1
- package/dist/bigconsole/components/widgets/form/FormWidget.js +20 -20
- package/dist/bigconsole/components/widgets/form/FormWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +2 -2
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +2 -2
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +37 -37
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js.map +1 -1
- package/dist/bigconsole/hooks/useDashboardOperations.js +92 -70
- package/dist/bigconsole/hooks/useDashboardOperations.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +120 -77
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/InsightsPage.js +787 -392
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/generated/wspace-operations.js +105 -107
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -25,7 +25,7 @@ var w = t(function({ isOpen: t, isCreating: w = !1, onClose: T, onCreate: E, sho
|
|
|
25
25
|
P.length
|
|
26
26
|
]), K = n((e) => {
|
|
27
27
|
z(e), j(e.name), N(e.description || "");
|
|
28
|
-
}, []), q = n((e) => {
|
|
28
|
+
}, []), q = n(async (e) => {
|
|
29
29
|
if (e.preventDefault(), O === "template" && !R) {
|
|
30
30
|
V("Please select a template");
|
|
31
31
|
return;
|
|
@@ -43,13 +43,18 @@ var w = t(function({ isOpen: t, isCreating: w = !1, onClose: T, onCreate: E, sho
|
|
|
43
43
|
V("Dashboard name must be less than 100 characters"), H.current?.focus();
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
|
-
V(null)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
V(null);
|
|
47
|
+
try {
|
|
48
|
+
await E({
|
|
49
|
+
name: t,
|
|
50
|
+
description: M.trim(),
|
|
51
|
+
category: "CUSTOM",
|
|
52
|
+
tags: P.length > 0 ? P : void 0,
|
|
53
|
+
templateId: R?.id
|
|
54
|
+
});
|
|
55
|
+
} catch (e) {
|
|
56
|
+
V(e instanceof Error ? e.message : "Failed to create dashboard. Please try again.");
|
|
57
|
+
}
|
|
53
58
|
}, [
|
|
54
59
|
A,
|
|
55
60
|
M,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateDashboardDialog.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/CreateDashboardDialog.tsx"],"sourcesContent":["/**\n * CreateDashboardDialog Component\n *\n * Modal dialog for creating a new dashboard with name and description.\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n * Phase 2: Added TemplatePicker integration for creating from templates.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef, type KeyboardEvent } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Plus, LayoutDashboard, Copy, X, Tag } from 'lucide-react';\nimport { TemplatePicker } from './TemplatePicker';\nimport type { DashboardTemplateListItem } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CreateDashboardDialogProps {\n isOpen: boolean;\n isCreating?: boolean;\n onClose: () => void;\n onCreate: (data: {\n name: string;\n description: string;\n category: string;\n tags?: string[];\n templateId?: string;\n }) => void;\n /** Whether to show the template picker option */\n showTemplates?: boolean;\n}\n\ntype CreateMode = 'blank' | 'template';\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const CreateDashboardDialog: FC<CreateDashboardDialogProps> = memo(function CreateDashboardDialog({\n isOpen,\n isCreating = false,\n onClose,\n onCreate,\n showTemplates = true,\n}) {\n const [mode, setMode] = useState<CreateMode>('blank');\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [tags, setTags] = useState<string[]>([]);\n const [tagInput, setTagInput] = useState('');\n const [selectedTemplate, setSelectedTemplate] = useState<DashboardTemplateListItem | null>(null);\n const [error, setError] = useState<string | null>(null);\n const nameInputRef = useRef<HTMLInputElement>(null);\n\n // Reset form when dialog opens\n useEffect(() => {\n if (isOpen) {\n setMode('blank');\n setName('');\n setDescription('');\n setTags([]);\n setTagInput('');\n setSelectedTemplate(null);\n setError(null);\n setTimeout(() => {\n nameInputRef.current?.focus();\n }, 100);\n }\n }, [isOpen]);\n\n // Handle adding a tag\n const handleAddTag = useCallback(() => {\n const trimmedTag = tagInput.trim().toLowerCase();\n if (trimmedTag && !tags.includes(trimmedTag) && tags.length < 10) {\n setTags((prev) => [...prev, trimmedTag]);\n setTagInput('');\n }\n }, [tagInput, tags]);\n\n // Handle removing a tag\n const handleRemoveTag = useCallback((tagToRemove: string) => {\n setTags((prev) => prev.filter((tag) => tag !== tagToRemove));\n }, []);\n\n // Handle tag input key press\n const handleTagKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleAddTag();\n } else if (e.key === 'Backspace' && !tagInput && tags.length > 0) {\n // Remove last tag when backspace is pressed on empty input\n setTags((prev) => prev.slice(0, -1));\n }\n },\n [handleAddTag, tagInput, tags.length]\n );\n\n // Handle template selection\n const handleTemplateSelect = useCallback((template: DashboardTemplateListItem) => {\n setSelectedTemplate(template);\n // Pre-fill name and description from template\n setName(template.name);\n setDescription(template.description || '');\n }, []);\n\n // Handle form submission\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n\n // For template mode without selection, show error\n if (mode === 'template' && !selectedTemplate) {\n setError('Please select a template');\n return;\n }\n\n const trimmedName = name.trim();\n if (!trimmedName) {\n setError('Dashboard name is required');\n nameInputRef.current?.focus();\n return;\n }\n\n if (trimmedName.length < 3) {\n setError('Dashboard name must be at least 3 characters');\n nameInputRef.current?.focus();\n return;\n }\n\n if (trimmedName.length > 100) {\n setError('Dashboard name must be less than 100 characters');\n nameInputRef.current?.focus();\n return;\n }\n\n setError(null);\n onCreate({\n name: trimmedName,\n description: description.trim(),\n category: 'CUSTOM',\n tags: tags.length > 0 ? tags : undefined,\n templateId: selectedTemplate?.id,\n });\n },\n [name, description, tags, mode, selectedTemplate, onCreate]\n );\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && !isCreating && onClose()}>\n <DialogContent\n style={{ maxWidth: mode === 'template' ? '900px' : '480px', padding: 0, transition: 'max-width 0.2s ease' }}\n >\n {/* Header */}\n <DialogHeader style={{ padding: 'var(--space-pagePadding) var(--space-pagePadding) 0' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-action-primary-bg)',\n boxShadow: 'var(--shadow-elevation-2)',\n }}\n >\n <LayoutDashboard\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n <div>\n <DialogTitle\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n margin: 0,\n }}\n >\n Create New Dashboard\n </DialogTitle>\n <DialogDescription\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n margin: 'var(--space-controlGap) 0 0 0',\n }}\n >\n Create a new dashboard to visualize your data\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <form onSubmit={handleSubmit}>\n {/* Mode Selection Tabs */}\n {showTemplates && (\n <div style={{ padding: 'var(--space-pagePadding) var(--space-pagePadding) 0' }}>\n <div\n style={{\n display: 'flex',\n gap: 'var(--space-controlGap)',\n padding: 'var(--space-controlGap)',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n <button\n type=\"button\"\n onClick={() => {\n setMode('blank');\n setSelectedTemplate(null);\n }}\n style={{\n flex: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-controlGap) var(--space-formGap)',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: mode === 'blank' ? 'var(--color-bg-surface)' : 'transparent',\n color: mode === 'blank' ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',\n fontWeight: mode === 'blank' ? 'var(--font-weight-semibold)' : 'var(--font-weight-regular)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n boxShadow: mode === 'blank' ? 'var(--shadow-elevation-1)' : 'none',\n transition: 'all 0.15s ease',\n }}\n >\n <Plus style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Blank Dashboard\n </button>\n <button\n type=\"button\"\n onClick={() => setMode('template')}\n style={{\n flex: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-controlGap) var(--space-formGap)',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: mode === 'template' ? 'var(--color-bg-surface)' : 'transparent',\n color: mode === 'template' ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',\n fontWeight: mode === 'template' ? 'var(--font-weight-semibold)' : 'var(--font-weight-regular)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n boxShadow: mode === 'template' ? 'var(--shadow-elevation-1)' : 'none',\n transition: 'all 0.15s ease',\n }}\n >\n <Copy style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n From Template\n </button>\n </div>\n </div>\n )}\n\n {/* Template Picker (when in template mode) */}\n {mode === 'template' && (\n <div style={{ padding: 'var(--space-pagePadding)', maxHeight: '400px', overflowY: 'auto' }}>\n <TemplatePicker selectedTemplateId={selectedTemplate?.id} onSelect={handleTemplateSelect} compact />\n </div>\n )}\n\n {/* Form Content */}\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n {/* Name Field */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label\n htmlFor=\"dashboard-name\"\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Dashboard Name\n <span style={{ color: 'var(--color-status-error-text)', marginLeft: 'var(--space-controlGap)' }}>\n *\n </span>\n </label>\n <Input\n ref={nameInputRef}\n id=\"dashboard-name\"\n type=\"text\"\n value={name}\n onChange={(e) => {\n setName(e.target.value);\n if (error) setError(null);\n }}\n placeholder=\"Enter dashboard name\"\n disabled={isCreating}\n style={{\n height: 'var(--size-controlHeight-md)',\n fontSize: 'var(--font-size-body-sm)',\n borderColor: error ? 'var(--color-status-error-border)' : undefined,\n }}\n />\n {error && (\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-status-error-text)',\n marginTop: 'var(--space-controlGap)',\n fontWeight: 'var(--font-weight-medium)',\n }}\n >\n {error}\n </p>\n )}\n </div>\n\n {/* Description Field */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label\n htmlFor=\"dashboard-description\"\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Description\n <span\n style={{\n color: 'var(--color-text-muted)',\n fontWeight: 'var(--font-weight-regular)',\n marginLeft: 'var(--space-controlGap)',\n }}\n >\n (optional)\n </span>\n </label>\n <Textarea\n id=\"dashboard-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Enter a brief description for this dashboard\"\n rows={3}\n disabled={isCreating}\n style={{\n fontSize: 'var(--font-size-body-sm)',\n resize: 'none',\n }}\n />\n </div>\n\n {/* Tags Field */}\n <div>\n <label\n htmlFor=\"dashboard-tags\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n <Tag style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Tags\n <span\n style={{\n color: 'var(--color-text-muted)',\n fontWeight: 'var(--font-weight-regular)',\n }}\n >\n (optional, max 10)\n </span>\n </label>\n {/* Tag chips display */}\n {tags.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n {tags.map((tag) => (\n <Badge\n key={tag}\n variant=\"secondary\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n paddingRight: '4px',\n }}\n >\n {tag}\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(tag)}\n disabled={isCreating}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '2px',\n borderRadius: '50%',\n color: 'inherit',\n opacity: 0.7,\n }}\n onMouseOver={(e) => (e.currentTarget.style.opacity = '1')}\n onMouseOut={(e) => (e.currentTarget.style.opacity = '0.7')}\n >\n <X style={{ height: '12px', width: '12px' }} />\n </button>\n </Badge>\n ))}\n </div>\n )}\n {/* Tag input */}\n <div style={{ display: 'flex', gap: 'var(--space-controlGap)' }}>\n <Input\n id=\"dashboard-tags\"\n type=\"text\"\n value={tagInput}\n onChange={(e) => setTagInput(e.target.value)}\n onKeyDown={handleTagKeyDown}\n placeholder={tags.length >= 10 ? 'Max tags reached' : 'Type a tag and press Enter'}\n disabled={isCreating || tags.length >= 10}\n style={{\n flex: 1,\n height: 'var(--size-controlHeight-md)',\n fontSize: 'var(--font-size-body-sm)',\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleAddTag}\n disabled={isCreating || !tagInput.trim() || tags.length >= 10}\n style={{ height: 'var(--size-controlHeight-md)' }}\n >\n Add\n </Button>\n </div>\n <p\n style={{\n fontSize: 'var(--font-size-body-xs)',\n color: 'var(--color-text-muted)',\n marginTop: 'var(--space-controlGap)',\n }}\n >\n Press Enter to add a tag, or click Add\n </p>\n </div>\n </div>\n\n {/* Footer */}\n <DialogFooter\n style={{\n padding: 'var(--space-formGap) var(--space-pagePadding)',\n borderTop: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-sunken)',\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 'var(--space-controlGap)',\n }}\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n disabled={isCreating}\n style={{ height: 'var(--size-controlHeight-md)' }}\n >\n Cancel\n </Button>\n <Button\n type=\"submit\"\n disabled={isCreating || !name.trim()}\n style={{\n height: 'var(--size-controlHeight-md)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n {isCreating ? (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Spinner style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Creating...\n </span>\n ) : (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Plus style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Create Dashboard\n </span>\n )}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default CreateDashboardDialog;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateDashboardDialog.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/CreateDashboardDialog.tsx"],"sourcesContent":["/**\n * CreateDashboardDialog Component\n *\n * Modal dialog for creating a new dashboard with name and description.\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n * Phase 2: Added TemplatePicker integration for creating from templates.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef, type KeyboardEvent } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Plus, LayoutDashboard, Copy, X, Tag } from 'lucide-react';\nimport { TemplatePicker } from './TemplatePicker';\nimport type { DashboardTemplateListItem } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CreateDashboardDialogProps {\n isOpen: boolean;\n isCreating?: boolean;\n onClose: () => void;\n onCreate: (data: {\n name: string;\n description: string;\n category: string;\n tags?: string[];\n templateId?: string;\n }) => void | Promise<void>;\n /** Whether to show the template picker option */\n showTemplates?: boolean;\n}\n\ntype CreateMode = 'blank' | 'template';\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const CreateDashboardDialog: FC<CreateDashboardDialogProps> = memo(function CreateDashboardDialog({\n isOpen,\n isCreating = false,\n onClose,\n onCreate,\n showTemplates = true,\n}) {\n const [mode, setMode] = useState<CreateMode>('blank');\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [tags, setTags] = useState<string[]>([]);\n const [tagInput, setTagInput] = useState('');\n const [selectedTemplate, setSelectedTemplate] = useState<DashboardTemplateListItem | null>(null);\n const [error, setError] = useState<string | null>(null);\n const nameInputRef = useRef<HTMLInputElement>(null);\n\n // Reset form when dialog opens\n useEffect(() => {\n if (isOpen) {\n setMode('blank');\n setName('');\n setDescription('');\n setTags([]);\n setTagInput('');\n setSelectedTemplate(null);\n setError(null);\n setTimeout(() => {\n nameInputRef.current?.focus();\n }, 100);\n }\n }, [isOpen]);\n\n // Handle adding a tag\n const handleAddTag = useCallback(() => {\n const trimmedTag = tagInput.trim().toLowerCase();\n if (trimmedTag && !tags.includes(trimmedTag) && tags.length < 10) {\n setTags((prev) => [...prev, trimmedTag]);\n setTagInput('');\n }\n }, [tagInput, tags]);\n\n // Handle removing a tag\n const handleRemoveTag = useCallback((tagToRemove: string) => {\n setTags((prev) => prev.filter((tag) => tag !== tagToRemove));\n }, []);\n\n // Handle tag input key press\n const handleTagKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleAddTag();\n } else if (e.key === 'Backspace' && !tagInput && tags.length > 0) {\n // Remove last tag when backspace is pressed on empty input\n setTags((prev) => prev.slice(0, -1));\n }\n },\n [handleAddTag, tagInput, tags.length]\n );\n\n // Handle template selection\n const handleTemplateSelect = useCallback((template: DashboardTemplateListItem) => {\n setSelectedTemplate(template);\n // Pre-fill name and description from template\n setName(template.name);\n setDescription(template.description || '');\n }, []);\n\n // Handle form submission\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n\n // For template mode without selection, show error\n if (mode === 'template' && !selectedTemplate) {\n setError('Please select a template');\n return;\n }\n\n const trimmedName = name.trim();\n if (!trimmedName) {\n setError('Dashboard name is required');\n nameInputRef.current?.focus();\n return;\n }\n\n if (trimmedName.length < 3) {\n setError('Dashboard name must be at least 3 characters');\n nameInputRef.current?.focus();\n return;\n }\n\n if (trimmedName.length > 100) {\n setError('Dashboard name must be less than 100 characters');\n nameInputRef.current?.focus();\n return;\n }\n\n setError(null);\n try {\n await onCreate({\n name: trimmedName,\n description: description.trim(),\n category: 'CUSTOM',\n tags: tags.length > 0 ? tags : undefined,\n templateId: selectedTemplate?.id,\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create dashboard. Please try again.');\n }\n },\n [name, description, tags, mode, selectedTemplate, onCreate]\n );\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && !isCreating && onClose()}>\n <DialogContent\n style={{ maxWidth: mode === 'template' ? '900px' : '480px', padding: 0, transition: 'max-width 0.2s ease' }}\n >\n {/* Header */}\n <DialogHeader style={{ padding: 'var(--space-pagePadding) var(--space-pagePadding) 0' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-action-primary-bg)',\n boxShadow: 'var(--shadow-elevation-2)',\n }}\n >\n <LayoutDashboard\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n </div>\n <div>\n <DialogTitle\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n margin: 0,\n }}\n >\n Create New Dashboard\n </DialogTitle>\n <DialogDescription\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n margin: 'var(--space-controlGap) 0 0 0',\n }}\n >\n Create a new dashboard to visualize your data\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <form onSubmit={handleSubmit}>\n {/* Mode Selection Tabs */}\n {showTemplates && (\n <div style={{ padding: 'var(--space-pagePadding) var(--space-pagePadding) 0' }}>\n <div\n style={{\n display: 'flex',\n gap: 'var(--space-controlGap)',\n padding: 'var(--space-controlGap)',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n <button\n type=\"button\"\n onClick={() => {\n setMode('blank');\n setSelectedTemplate(null);\n }}\n style={{\n flex: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-controlGap) var(--space-formGap)',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: mode === 'blank' ? 'var(--color-bg-surface)' : 'transparent',\n color: mode === 'blank' ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',\n fontWeight: mode === 'blank' ? 'var(--font-weight-semibold)' : 'var(--font-weight-regular)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n boxShadow: mode === 'blank' ? 'var(--shadow-elevation-1)' : 'none',\n transition: 'all 0.15s ease',\n }}\n >\n <Plus style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Blank Dashboard\n </button>\n <button\n type=\"button\"\n onClick={() => setMode('template')}\n style={{\n flex: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-controlGap) var(--space-formGap)',\n borderRadius: 'var(--radius-button)',\n border: 'none',\n backgroundColor: mode === 'template' ? 'var(--color-bg-surface)' : 'transparent',\n color: mode === 'template' ? 'var(--color-text-primary)' : 'var(--color-text-secondary)',\n fontWeight: mode === 'template' ? 'var(--font-weight-semibold)' : 'var(--font-weight-regular)',\n fontSize: 'var(--font-size-body-sm)',\n cursor: 'pointer',\n boxShadow: mode === 'template' ? 'var(--shadow-elevation-1)' : 'none',\n transition: 'all 0.15s ease',\n }}\n >\n <Copy style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n From Template\n </button>\n </div>\n </div>\n )}\n\n {/* Template Picker (when in template mode) */}\n {mode === 'template' && (\n <div style={{ padding: 'var(--space-pagePadding)', maxHeight: '400px', overflowY: 'auto' }}>\n <TemplatePicker selectedTemplateId={selectedTemplate?.id} onSelect={handleTemplateSelect} compact />\n </div>\n )}\n\n {/* Form Content */}\n <div style={{ padding: 'var(--space-pagePadding)' }}>\n {/* Name Field */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label\n htmlFor=\"dashboard-name\"\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Dashboard Name\n <span style={{ color: 'var(--color-status-error-text)', marginLeft: 'var(--space-controlGap)' }}>\n *\n </span>\n </label>\n <Input\n ref={nameInputRef}\n id=\"dashboard-name\"\n type=\"text\"\n value={name}\n onChange={(e) => {\n setName(e.target.value);\n if (error) setError(null);\n }}\n placeholder=\"Enter dashboard name\"\n disabled={isCreating}\n style={{\n height: 'var(--size-controlHeight-md)',\n fontSize: 'var(--font-size-body-sm)',\n borderColor: error ? 'var(--color-status-error-border)' : undefined,\n }}\n />\n {error && (\n <p\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-status-error-text)',\n marginTop: 'var(--space-controlGap)',\n fontWeight: 'var(--font-weight-medium)',\n }}\n >\n {error}\n </p>\n )}\n </div>\n\n {/* Description Field */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label\n htmlFor=\"dashboard-description\"\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Description\n <span\n style={{\n color: 'var(--color-text-muted)',\n fontWeight: 'var(--font-weight-regular)',\n marginLeft: 'var(--space-controlGap)',\n }}\n >\n (optional)\n </span>\n </label>\n <Textarea\n id=\"dashboard-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Enter a brief description for this dashboard\"\n rows={3}\n disabled={isCreating}\n style={{\n fontSize: 'var(--font-size-body-sm)',\n resize: 'none',\n }}\n />\n </div>\n\n {/* Tags Field */}\n <div>\n <label\n htmlFor=\"dashboard-tags\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n <Tag style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Tags\n <span\n style={{\n color: 'var(--color-text-muted)',\n fontWeight: 'var(--font-weight-regular)',\n }}\n >\n (optional, max 10)\n </span>\n </label>\n {/* Tag chips display */}\n {tags.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n {tags.map((tag) => (\n <Badge\n key={tag}\n variant=\"secondary\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n paddingRight: '4px',\n }}\n >\n {tag}\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(tag)}\n disabled={isCreating}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '2px',\n borderRadius: '50%',\n color: 'inherit',\n opacity: 0.7,\n }}\n onMouseOver={(e) => (e.currentTarget.style.opacity = '1')}\n onMouseOut={(e) => (e.currentTarget.style.opacity = '0.7')}\n >\n <X style={{ height: '12px', width: '12px' }} />\n </button>\n </Badge>\n ))}\n </div>\n )}\n {/* Tag input */}\n <div style={{ display: 'flex', gap: 'var(--space-controlGap)' }}>\n <Input\n id=\"dashboard-tags\"\n type=\"text\"\n value={tagInput}\n onChange={(e) => setTagInput(e.target.value)}\n onKeyDown={handleTagKeyDown}\n placeholder={tags.length >= 10 ? 'Max tags reached' : 'Type a tag and press Enter'}\n disabled={isCreating || tags.length >= 10}\n style={{\n flex: 1,\n height: 'var(--size-controlHeight-md)',\n fontSize: 'var(--font-size-body-sm)',\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleAddTag}\n disabled={isCreating || !tagInput.trim() || tags.length >= 10}\n style={{ height: 'var(--size-controlHeight-md)' }}\n >\n Add\n </Button>\n </div>\n <p\n style={{\n fontSize: 'var(--font-size-body-xs)',\n color: 'var(--color-text-muted)',\n marginTop: 'var(--space-controlGap)',\n }}\n >\n Press Enter to add a tag, or click Add\n </p>\n </div>\n </div>\n\n {/* Footer */}\n <DialogFooter\n style={{\n padding: 'var(--space-formGap) var(--space-pagePadding)',\n borderTop: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-sunken)',\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 'var(--space-controlGap)',\n }}\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n disabled={isCreating}\n style={{ height: 'var(--size-controlHeight-md)' }}\n >\n Cancel\n </Button>\n <Button\n type=\"submit\"\n disabled={isCreating || !name.trim()}\n style={{\n height: 'var(--size-controlHeight-md)',\n backgroundColor: 'var(--color-action-primary-bg)',\n }}\n >\n {isCreating ? (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Spinner style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Creating...\n </span>\n ) : (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Plus style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Create Dashboard\n </span>\n )}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default CreateDashboardDialog;\n"],"mappings":"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"}
|
|
@@ -136,7 +136,7 @@ function c({ isOpen: c, onClose: f, onSelectTemplate: p, selectedTemplateId: m,
|
|
|
136
136
|
}) : /* @__PURE__ */ i("div", {
|
|
137
137
|
style: {
|
|
138
138
|
display: "grid",
|
|
139
|
-
gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
|
|
139
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(min(200px, 100%), 1fr))",
|
|
140
140
|
gap: "12px"
|
|
141
141
|
},
|
|
142
142
|
children: W.map((e) => /* @__PURE__ */ i(d, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplatePicker.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.tsx"],"sourcesContent":["/**\n * TemplatePicker Component\n *\n * Grid/List view for browsing and selecting dashboard templates.\n */\n\nimport React, { useState, useEffect, useCallback } from 'react';\nimport {\n useDashboardTemplates,\n type DashboardTemplateListItem,\n type DashboardCategory,\n type TemplateComplexity,\n} from '../../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TemplatePickerProps {\n /** Modal mode: whether the modal is open */\n isOpen?: boolean;\n /** Modal mode: callback to close the modal */\n onClose?: () => void;\n /** Modal mode: callback when template is selected with name and placeholders */\n onSelectTemplate?: (templateId: string, name: string, placeholderValues: Record<string, unknown>) => void;\n /** Embedded mode: currently selected template ID */\n selectedTemplateId?: string;\n /** Embedded mode: callback when template is selected */\n onSelect?: (template: DashboardTemplateListItem) => void;\n /** Embedded mode: compact display without modal wrapper */\n compact?: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst CATEGORY_LABELS: Record<DashboardCategory, string> = {\n ANALYTICS: 'Analytics',\n OPERATIONS: 'Operations',\n SALES: 'Sales',\n MARKETING: 'Marketing',\n FINANCE: 'Finance',\n HR: 'Human Resources',\n CUSTOM: 'Custom',\n};\n\nconst COMPLEXITY_COLORS: Record<TemplateComplexity, string> = {\n BEGINNER: '#22c55e',\n INTERMEDIATE: '#f59e0b',\n ADVANCED: '#ef4444',\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function TemplatePicker({\n isOpen,\n onClose,\n onSelectTemplate,\n selectedTemplateId,\n onSelect,\n compact = false,\n}: TemplatePickerProps) {\n const { templates, featuredTemplates, categories, loading, fetchTemplates, fetchFeaturedTemplates, searchTemplates } =\n useDashboardTemplates();\n\n const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedCategory, setSelectedCategory] = useState<DashboardCategory | ''>('');\n const [selectedComplexity, setSelectedComplexity] = useState<TemplateComplexity | ''>('');\n const [selectedTemplate, setSelectedTemplate] = useState<DashboardTemplateListItem | null>(null);\n const [showDetailsModal, setShowDetailsModal] = useState(false);\n const [dashboardName, setDashboardName] = useState('');\n const [placeholderValues, setPlaceholderValues] = useState<Record<string, unknown>>({});\n\n // Determine if we should be active (modal mode uses isOpen, compact mode is always active)\n const isActive = compact || isOpen;\n\n // Fetch templates on mount/activation\n useEffect(() => {\n if (isActive) {\n fetchTemplates();\n fetchFeaturedTemplates();\n }\n }, [isActive, fetchTemplates, fetchFeaturedTemplates]);\n\n // Handle search\n useEffect(() => {\n if (searchQuery) {\n const debounce = setTimeout(() => {\n searchTemplates(searchQuery, selectedCategory || undefined, selectedComplexity || undefined);\n }, 300);\n return () => clearTimeout(debounce);\n } else if (isActive) {\n fetchTemplates({\n category: selectedCategory || undefined,\n complexity: selectedComplexity || undefined,\n });\n }\n }, [searchQuery, selectedCategory, selectedComplexity, isActive, searchTemplates, fetchTemplates]);\n\n // Handle template selection\n const handleSelectTemplate = useCallback(\n (template: DashboardTemplateListItem) => {\n // In compact/embedded mode, just call onSelect\n if (compact && onSelect) {\n onSelect(template);\n return;\n }\n\n // In modal mode, show details modal\n setSelectedTemplate(template);\n setDashboardName(`${template.name} Dashboard`);\n setShowDetailsModal(true);\n },\n [compact, onSelect]\n );\n\n // Handle create from template\n const handleCreateFromTemplate = useCallback(async () => {\n if (!selectedTemplate || !dashboardName.trim()) return;\n\n onSelectTemplate?.(selectedTemplate.id, dashboardName.trim(), placeholderValues);\n setShowDetailsModal(false);\n setSelectedTemplate(null);\n setDashboardName('');\n setPlaceholderValues({});\n onClose?.();\n }, [selectedTemplate, dashboardName, placeholderValues, onSelectTemplate, onClose]);\n\n // Render star rating\n const renderRating = (rating: number | undefined, count: number) => {\n if (!rating) return null;\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>\n <span style={{ color: '#f59e0b' }}>★</span>\n <span style={{ fontSize: '12px', color: 'var(--text-secondary)' }}>\n {rating.toFixed(1)} ({count})\n </span>\n </div>\n );\n };\n\n if (!isActive) return null;\n\n const displayTemplates = searchQuery || selectedCategory || selectedComplexity ? templates : featuredTemplates;\n\n // Compact/Embedded mode - render just the content without modal wrapper\n if (compact) {\n return (\n <div>\n {/* Compact Filters Bar */}\n <div style={{ display: 'flex', gap: '8px', marginBottom: '12px', flexWrap: 'wrap' }}>\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder=\"Search templates...\"\n style={{\n flex: '1 1 150px',\n padding: '6px 10px',\n border: '1px solid var(--border-color)',\n borderRadius: '4px',\n fontSize: '12px',\n }}\n />\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value as DashboardCategory | '')}\n style={{\n padding: '6px 10px',\n border: '1px solid var(--border-color)',\n borderRadius: '4px',\n fontSize: '12px',\n backgroundColor: 'var(--bg-primary)',\n }}\n >\n <option value=\"\">All Categories</option>\n {categories.map((cat) => (\n <option key={cat} value={cat}>\n {CATEGORY_LABELS[cat]}\n </option>\n ))}\n </select>\n </div>\n\n {/* Compact Template Grid */}\n {loading && displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '20px', color: 'var(--text-secondary)', fontSize: '13px' }}>\n Loading templates...\n </div>\n ) : displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '20px', color: 'var(--text-secondary)', fontSize: '13px' }}>\n No templates found\n </div>\n ) : (\n <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '12px' }}>\n {displayTemplates.map((template) => (\n <CompactTemplateCard\n key={template.id}\n template={template}\n isSelected={selectedTemplateId === template.id}\n onSelect={() => handleSelectTemplate(template)}\n />\n ))}\n </div>\n )}\n </div>\n );\n }\n\n // Modal mode - full modal wrapper\n return (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n }}\n onClick={onClose}\n >\n <div\n style={{\n width: '90%',\n maxWidth: '1000px',\n maxHeight: '85vh',\n backgroundColor: 'var(--bg-primary)',\n borderRadius: '12px',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.2)',\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div\n style={{\n padding: '20px 24px',\n borderBottom: '1px solid var(--border-color)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <div>\n <h2 style={{ margin: 0, fontSize: '20px', fontWeight: 600 }}>Choose a Template</h2>\n <p style={{ margin: '4px 0 0', fontSize: '13px', color: 'var(--text-secondary)' }}>\n Start with a pre-built dashboard template\n </p>\n </div>\n <button\n onClick={onClose}\n style={{\n background: 'none',\n border: 'none',\n fontSize: '24px',\n cursor: 'pointer',\n color: 'var(--text-secondary)',\n }}\n >\n x\n </button>\n </div>\n\n {/* Filters Bar */}\n <div\n style={{\n padding: '16px 24px',\n borderBottom: '1px solid var(--border-color)',\n display: 'flex',\n gap: '12px',\n alignItems: 'center',\n flexWrap: 'wrap',\n }}\n >\n {/* Search */}\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder=\"Search templates...\"\n style={{\n flex: '1 1 200px',\n padding: '8px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '13px',\n }}\n />\n\n {/* Category Filter */}\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value as DashboardCategory | '')}\n style={{\n padding: '8px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: 'var(--bg-primary)',\n }}\n >\n <option value=\"\">All Categories</option>\n {categories.map((cat) => (\n <option key={cat} value={cat}>\n {CATEGORY_LABELS[cat]}\n </option>\n ))}\n </select>\n\n {/* Complexity Filter */}\n <select\n value={selectedComplexity}\n onChange={(e) => setSelectedComplexity(e.target.value as TemplateComplexity | '')}\n style={{\n padding: '8px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: 'var(--bg-primary)',\n }}\n >\n <option value=\"\">All Levels</option>\n <option value=\"BEGINNER\">Beginner</option>\n <option value=\"INTERMEDIATE\">Intermediate</option>\n <option value=\"ADVANCED\">Advanced</option>\n </select>\n\n {/* View Toggle */}\n <div style={{ display: 'flex', border: '1px solid var(--border-color)', borderRadius: '6px' }}>\n <button\n onClick={() => setViewMode('grid')}\n style={{\n padding: '8px 12px',\n backgroundColor: viewMode === 'grid' ? 'var(--bg-secondary)' : 'transparent',\n border: 'none',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n Grid\n </button>\n <button\n onClick={() => setViewMode('list')}\n style={{\n padding: '8px 12px',\n backgroundColor: viewMode === 'list' ? 'var(--bg-secondary)' : 'transparent',\n border: 'none',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n List\n </button>\n </div>\n </div>\n\n {/* Templates Content */}\n <div style={{ flex: 1, overflowY: 'auto', padding: '24px' }}>\n {loading && displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '40px', color: 'var(--text-secondary)' }}>\n Loading templates...\n </div>\n ) : displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '40px', color: 'var(--text-secondary)' }}>\n No templates found. Try adjusting your filters.\n </div>\n ) : viewMode === 'grid' ? (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',\n gap: '20px',\n }}\n >\n {displayTemplates.map((template) => (\n <TemplateCard\n key={template.id}\n template={template}\n onSelect={() => handleSelectTemplate(template)}\n renderRating={renderRating}\n />\n ))}\n </div>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>\n {displayTemplates.map((template) => (\n <TemplateListItem\n key={template.id}\n template={template}\n onSelect={() => handleSelectTemplate(template)}\n renderRating={renderRating}\n />\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div\n style={{\n padding: '16px 24px',\n borderTop: '1px solid var(--border-color)',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span style={{ fontSize: '13px', color: 'var(--text-secondary)' }}>\n {displayTemplates.length} templates available\n </span>\n <button\n onClick={onClose}\n style={{\n padding: '10px 20px',\n backgroundColor: 'transparent',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n }}\n >\n Cancel\n </button>\n </div>\n </div>\n\n {/* Template Details Modal */}\n {showDetailsModal && selectedTemplate && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 10000,\n }}\n onClick={() => setShowDetailsModal(false)}\n >\n <div\n style={{\n width: '500px',\n backgroundColor: 'var(--bg-primary)',\n borderRadius: '12px',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.2)',\n overflow: 'hidden',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n padding: '20px 24px',\n borderBottom: '1px solid var(--border-color)',\n }}\n >\n <h3 style={{ margin: 0, fontSize: '18px', fontWeight: 600 }}>Create from Template</h3>\n <p style={{ margin: '4px 0 0', fontSize: '13px', color: 'var(--text-secondary)' }}>\n {selectedTemplate.name}\n </p>\n </div>\n <div style={{ padding: '24px' }}>\n <div style={{ marginBottom: '20px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '13px',\n fontWeight: 500,\n }}\n >\n Dashboard Name *\n </label>\n <input\n type=\"text\"\n value={dashboardName}\n onChange={(e) => setDashboardName(e.target.value)}\n placeholder=\"Enter dashboard name...\"\n style={{\n width: '100%',\n padding: '10px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '14px',\n }}\n />\n </div>\n\n {/* Template Info */}\n <div\n style={{\n padding: '16px',\n backgroundColor: 'var(--bg-secondary)',\n borderRadius: '8px',\n }}\n >\n <div style={{ fontSize: '13px', color: 'var(--text-secondary)', marginBottom: '8px' }}>\n Template Details\n </div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: '16px' }}>\n <div>\n <div style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>Category</div>\n <div style={{ fontSize: '13px' }}>{CATEGORY_LABELS[selectedTemplate.category]}</div>\n </div>\n <div>\n <div style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>Complexity</div>\n <div\n style={{\n fontSize: '13px',\n color: COMPLEXITY_COLORS[selectedTemplate.complexity],\n }}\n >\n {selectedTemplate.complexity}\n </div>\n </div>\n <div>\n <div style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>Used</div>\n <div style={{ fontSize: '13px' }}>{selectedTemplate.usageCount} times</div>\n </div>\n </div>\n </div>\n </div>\n <div\n style={{\n padding: '16px 24px',\n borderTop: '1px solid var(--border-color)',\n display: 'flex',\n justifyContent: 'flex-end',\n gap: '12px',\n }}\n >\n <button\n onClick={() => setShowDetailsModal(false)}\n style={{\n padding: '10px 20px',\n backgroundColor: 'transparent',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n }}\n >\n Cancel\n </button>\n <button\n onClick={handleCreateFromTemplate}\n disabled={!dashboardName.trim()}\n style={{\n padding: '10px 20px',\n backgroundColor: dashboardName.trim() ? 'var(--primary)' : 'var(--bg-tertiary)',\n color: dashboardName.trim() ? 'white' : 'var(--text-secondary)',\n border: 'none',\n borderRadius: '6px',\n cursor: dashboardName.trim() ? 'pointer' : 'not-allowed',\n fontSize: '14px',\n fontWeight: 500,\n }}\n >\n Create Dashboard\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\ninterface TemplateCardProps {\n template: DashboardTemplateListItem;\n onSelect: () => void;\n renderRating: (rating: number | undefined, count: number) => React.ReactNode;\n}\n\nfunction TemplateCard({ template, onSelect, renderRating }: TemplateCardProps) {\n return (\n <div\n onClick={onSelect}\n style={{\n backgroundColor: 'var(--bg-secondary)',\n border: '1px solid var(--border-color)',\n borderRadius: '10px',\n overflow: 'hidden',\n cursor: 'pointer',\n transition: 'transform 0.15s, box-shadow 0.15s',\n }}\n onMouseOver={(e) => {\n (e.currentTarget as HTMLDivElement).style.transform = 'translateY(-2px)';\n (e.currentTarget as HTMLDivElement).style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)';\n }}\n onMouseOut={(e) => {\n (e.currentTarget as HTMLDivElement).style.transform = 'none';\n (e.currentTarget as HTMLDivElement).style.boxShadow = 'none';\n }}\n >\n {/* Thumbnail */}\n <div\n style={{\n height: '140px',\n backgroundColor: 'var(--bg-tertiary)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderBottom: '1px solid var(--border-color)',\n }}\n >\n {template.previewUrl ? (\n <img\n src={template.previewUrl}\n alt={template.name}\n style={{ width: '100%', height: '100%', objectFit: 'cover' }}\n />\n ) : (\n <span style={{ fontSize: '40px', color: 'var(--text-tertiary)' }}>📊</span>\n )}\n </div>\n\n {/* Content */}\n <div style={{ padding: '16px' }}>\n <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: '8px' }}>\n <h3 style={{ margin: 0, fontSize: '14px', fontWeight: 600, lineHeight: '1.3' }}>{template.name}</h3>\n {template.isFeatured && (\n <span\n style={{\n fontSize: '9px',\n padding: '2px 6px',\n backgroundColor: '#fef3c7',\n color: '#92400e',\n borderRadius: '4px',\n fontWeight: 600,\n flexShrink: 0,\n }}\n >\n FEATURED\n </span>\n )}\n </div>\n\n {template.description && (\n <p\n style={{\n margin: '8px 0 0',\n fontSize: '12px',\n color: 'var(--text-secondary)',\n lineHeight: '1.4',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {template.description}\n </p>\n )}\n\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '12px', flexWrap: 'wrap' }}>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '4px',\n color: 'var(--text-secondary)',\n }}\n >\n {CATEGORY_LABELS[template.category]}\n </span>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: `${COMPLEXITY_COLORS[template.complexity]}20`,\n color: COMPLEXITY_COLORS[template.complexity],\n borderRadius: '4px',\n fontWeight: 500,\n }}\n >\n {template.complexity}\n </span>\n {renderRating(template.rating, template.ratingCount)}\n </div>\n </div>\n </div>\n );\n}\n\ninterface TemplateListItemProps {\n template: DashboardTemplateListItem;\n onSelect: () => void;\n renderRating: (rating: number | undefined, count: number) => React.ReactNode;\n}\n\nfunction TemplateListItem({ template, onSelect, renderRating }: TemplateListItemProps) {\n return (\n <div\n onClick={onSelect}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '16px',\n padding: '16px',\n backgroundColor: 'var(--bg-secondary)',\n border: '1px solid var(--border-color)',\n borderRadius: '8px',\n cursor: 'pointer',\n transition: 'background-color 0.15s',\n }}\n >\n {/* Thumbnail */}\n <div\n style={{\n width: '80px',\n height: '60px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '6px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n }}\n >\n {template.previewUrl ? (\n <img\n src={template.previewUrl}\n alt={template.name}\n style={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: '6px' }}\n />\n ) : (\n <span style={{ fontSize: '24px', color: 'var(--text-tertiary)' }}>📊</span>\n )}\n </div>\n\n {/* Content */}\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <h3 style={{ margin: 0, fontSize: '14px', fontWeight: 600 }}>{template.name}</h3>\n {template.isFeatured && (\n <span\n style={{\n fontSize: '9px',\n padding: '2px 6px',\n backgroundColor: '#fef3c7',\n color: '#92400e',\n borderRadius: '4px',\n fontWeight: 600,\n }}\n >\n FEATURED\n </span>\n )}\n </div>\n {template.description && (\n <p\n style={{\n margin: '4px 0 0',\n fontSize: '12px',\n color: 'var(--text-secondary)',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {template.description}\n </p>\n )}\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '8px' }}>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '4px',\n }}\n >\n {CATEGORY_LABELS[template.category]}\n </span>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: `${COMPLEXITY_COLORS[template.complexity]}20`,\n color: COMPLEXITY_COLORS[template.complexity],\n borderRadius: '4px',\n }}\n >\n {template.complexity}\n </span>\n {renderRating(template.rating, template.ratingCount)}\n <span style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>{template.usageCount} uses</span>\n </div>\n </div>\n\n {/* Action */}\n <button\n style={{\n padding: '8px 16px',\n backgroundColor: 'var(--primary)',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 500,\n flexShrink: 0,\n }}\n >\n Use Template\n </button>\n </div>\n );\n}\n\n// Compact template card for embedded mode\ninterface CompactTemplateCardProps {\n template: DashboardTemplateListItem;\n isSelected: boolean;\n onSelect: () => void;\n}\n\nfunction CompactTemplateCard({ template, isSelected, onSelect }: CompactTemplateCardProps) {\n return (\n <div\n onClick={onSelect}\n style={{\n backgroundColor: isSelected ? 'var(--color-action-primary-bg-subtle, #e0f2fe)' : 'var(--bg-secondary)',\n border: `2px solid ${isSelected ? 'var(--color-action-primary-border, #3b82f6)' : 'var(--border-color)'}`,\n borderRadius: '8px',\n overflow: 'hidden',\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n >\n {/* Thumbnail */}\n <div\n style={{\n height: '80px',\n backgroundColor: 'var(--bg-tertiary)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderBottom: '1px solid var(--border-color)',\n }}\n >\n {template.previewUrl ? (\n <img\n src={template.previewUrl}\n alt={template.name}\n style={{ width: '100%', height: '100%', objectFit: 'cover' }}\n />\n ) : (\n <span style={{ fontSize: '28px', color: 'var(--text-tertiary)' }}>📊</span>\n )}\n </div>\n\n {/* Content */}\n <div style={{ padding: '10px' }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '4px' }}>\n <h4\n style={{\n margin: 0,\n fontSize: '12px',\n fontWeight: 600,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {template.name}\n </h4>\n {isSelected && (\n <span style={{ color: 'var(--color-action-primary-text, #3b82f6)', fontSize: '14px' }}>✓</span>\n )}\n </div>\n <div style={{ display: 'flex', gap: '4px', marginTop: '6px', flexWrap: 'wrap' }}>\n <span\n style={{\n fontSize: '9px',\n padding: '1px 4px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '3px',\n color: 'var(--text-secondary)',\n }}\n >\n {CATEGORY_LABELS[template.category]}\n </span>\n <span\n style={{\n fontSize: '9px',\n padding: '1px 4px',\n backgroundColor: `${COMPLEXITY_COLORS[template.complexity]}20`,\n color: COMPLEXITY_COLORS[template.complexity],\n borderRadius: '3px',\n }}\n >\n {template.complexity}\n </span>\n </div>\n </div>\n </div>\n );\n}\n\nexport default TemplatePicker;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TemplatePicker.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.tsx"],"sourcesContent":["/**\n * TemplatePicker Component\n *\n * Grid/List view for browsing and selecting dashboard templates.\n */\n\nimport React, { useState, useEffect, useCallback } from 'react';\nimport {\n useDashboardTemplates,\n type DashboardTemplateListItem,\n type DashboardCategory,\n type TemplateComplexity,\n} from '../../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TemplatePickerProps {\n /** Modal mode: whether the modal is open */\n isOpen?: boolean;\n /** Modal mode: callback to close the modal */\n onClose?: () => void;\n /** Modal mode: callback when template is selected with name and placeholders */\n onSelectTemplate?: (templateId: string, name: string, placeholderValues: Record<string, unknown>) => void;\n /** Embedded mode: currently selected template ID */\n selectedTemplateId?: string;\n /** Embedded mode: callback when template is selected */\n onSelect?: (template: DashboardTemplateListItem) => void;\n /** Embedded mode: compact display without modal wrapper */\n compact?: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst CATEGORY_LABELS: Record<DashboardCategory, string> = {\n ANALYTICS: 'Analytics',\n OPERATIONS: 'Operations',\n SALES: 'Sales',\n MARKETING: 'Marketing',\n FINANCE: 'Finance',\n HR: 'Human Resources',\n CUSTOM: 'Custom',\n};\n\nconst COMPLEXITY_COLORS: Record<TemplateComplexity, string> = {\n BEGINNER: '#22c55e',\n INTERMEDIATE: '#f59e0b',\n ADVANCED: '#ef4444',\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function TemplatePicker({\n isOpen,\n onClose,\n onSelectTemplate,\n selectedTemplateId,\n onSelect,\n compact = false,\n}: TemplatePickerProps) {\n const { templates, featuredTemplates, categories, loading, fetchTemplates, fetchFeaturedTemplates, searchTemplates } =\n useDashboardTemplates();\n\n const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedCategory, setSelectedCategory] = useState<DashboardCategory | ''>('');\n const [selectedComplexity, setSelectedComplexity] = useState<TemplateComplexity | ''>('');\n const [selectedTemplate, setSelectedTemplate] = useState<DashboardTemplateListItem | null>(null);\n const [showDetailsModal, setShowDetailsModal] = useState(false);\n const [dashboardName, setDashboardName] = useState('');\n const [placeholderValues, setPlaceholderValues] = useState<Record<string, unknown>>({});\n\n // Determine if we should be active (modal mode uses isOpen, compact mode is always active)\n const isActive = compact || isOpen;\n\n // Fetch templates on mount/activation\n useEffect(() => {\n if (isActive) {\n fetchTemplates();\n fetchFeaturedTemplates();\n }\n }, [isActive, fetchTemplates, fetchFeaturedTemplates]);\n\n // Handle search\n useEffect(() => {\n if (searchQuery) {\n const debounce = setTimeout(() => {\n searchTemplates(searchQuery, selectedCategory || undefined, selectedComplexity || undefined);\n }, 300);\n return () => clearTimeout(debounce);\n } else if (isActive) {\n fetchTemplates({\n category: selectedCategory || undefined,\n complexity: selectedComplexity || undefined,\n });\n }\n }, [searchQuery, selectedCategory, selectedComplexity, isActive, searchTemplates, fetchTemplates]);\n\n // Handle template selection\n const handleSelectTemplate = useCallback(\n (template: DashboardTemplateListItem) => {\n // In compact/embedded mode, just call onSelect\n if (compact && onSelect) {\n onSelect(template);\n return;\n }\n\n // In modal mode, show details modal\n setSelectedTemplate(template);\n setDashboardName(`${template.name} Dashboard`);\n setShowDetailsModal(true);\n },\n [compact, onSelect]\n );\n\n // Handle create from template\n const handleCreateFromTemplate = useCallback(async () => {\n if (!selectedTemplate || !dashboardName.trim()) return;\n\n onSelectTemplate?.(selectedTemplate.id, dashboardName.trim(), placeholderValues);\n setShowDetailsModal(false);\n setSelectedTemplate(null);\n setDashboardName('');\n setPlaceholderValues({});\n onClose?.();\n }, [selectedTemplate, dashboardName, placeholderValues, onSelectTemplate, onClose]);\n\n // Render star rating\n const renderRating = (rating: number | undefined, count: number) => {\n if (!rating) return null;\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>\n <span style={{ color: '#f59e0b' }}>★</span>\n <span style={{ fontSize: '12px', color: 'var(--text-secondary)' }}>\n {rating.toFixed(1)} ({count})\n </span>\n </div>\n );\n };\n\n if (!isActive) return null;\n\n const displayTemplates = searchQuery || selectedCategory || selectedComplexity ? templates : featuredTemplates;\n\n // Compact/Embedded mode - render just the content without modal wrapper\n if (compact) {\n return (\n <div>\n {/* Compact Filters Bar */}\n <div style={{ display: 'flex', gap: '8px', marginBottom: '12px', flexWrap: 'wrap' }}>\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder=\"Search templates...\"\n style={{\n flex: '1 1 150px',\n padding: '6px 10px',\n border: '1px solid var(--border-color)',\n borderRadius: '4px',\n fontSize: '12px',\n }}\n />\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value as DashboardCategory | '')}\n style={{\n padding: '6px 10px',\n border: '1px solid var(--border-color)',\n borderRadius: '4px',\n fontSize: '12px',\n backgroundColor: 'var(--bg-primary)',\n }}\n >\n <option value=\"\">All Categories</option>\n {categories.map((cat) => (\n <option key={cat} value={cat}>\n {CATEGORY_LABELS[cat]}\n </option>\n ))}\n </select>\n </div>\n\n {/* Compact Template Grid */}\n {loading && displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '20px', color: 'var(--text-secondary)', fontSize: '13px' }}>\n Loading templates...\n </div>\n ) : displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '20px', color: 'var(--text-secondary)', fontSize: '13px' }}>\n No templates found\n </div>\n ) : (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(min(200px, 100%), 1fr))',\n gap: '12px',\n }}\n >\n {displayTemplates.map((template) => (\n <CompactTemplateCard\n key={template.id}\n template={template}\n isSelected={selectedTemplateId === template.id}\n onSelect={() => handleSelectTemplate(template)}\n />\n ))}\n </div>\n )}\n </div>\n );\n }\n\n // Modal mode - full modal wrapper\n return (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n }}\n onClick={onClose}\n >\n <div\n style={{\n width: '90%',\n maxWidth: '1000px',\n maxHeight: '85vh',\n backgroundColor: 'var(--bg-primary)',\n borderRadius: '12px',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.2)',\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div\n style={{\n padding: '20px 24px',\n borderBottom: '1px solid var(--border-color)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <div>\n <h2 style={{ margin: 0, fontSize: '20px', fontWeight: 600 }}>Choose a Template</h2>\n <p style={{ margin: '4px 0 0', fontSize: '13px', color: 'var(--text-secondary)' }}>\n Start with a pre-built dashboard template\n </p>\n </div>\n <button\n onClick={onClose}\n style={{\n background: 'none',\n border: 'none',\n fontSize: '24px',\n cursor: 'pointer',\n color: 'var(--text-secondary)',\n }}\n >\n x\n </button>\n </div>\n\n {/* Filters Bar */}\n <div\n style={{\n padding: '16px 24px',\n borderBottom: '1px solid var(--border-color)',\n display: 'flex',\n gap: '12px',\n alignItems: 'center',\n flexWrap: 'wrap',\n }}\n >\n {/* Search */}\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder=\"Search templates...\"\n style={{\n flex: '1 1 200px',\n padding: '8px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '13px',\n }}\n />\n\n {/* Category Filter */}\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value as DashboardCategory | '')}\n style={{\n padding: '8px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: 'var(--bg-primary)',\n }}\n >\n <option value=\"\">All Categories</option>\n {categories.map((cat) => (\n <option key={cat} value={cat}>\n {CATEGORY_LABELS[cat]}\n </option>\n ))}\n </select>\n\n {/* Complexity Filter */}\n <select\n value={selectedComplexity}\n onChange={(e) => setSelectedComplexity(e.target.value as TemplateComplexity | '')}\n style={{\n padding: '8px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: 'var(--bg-primary)',\n }}\n >\n <option value=\"\">All Levels</option>\n <option value=\"BEGINNER\">Beginner</option>\n <option value=\"INTERMEDIATE\">Intermediate</option>\n <option value=\"ADVANCED\">Advanced</option>\n </select>\n\n {/* View Toggle */}\n <div style={{ display: 'flex', border: '1px solid var(--border-color)', borderRadius: '6px' }}>\n <button\n onClick={() => setViewMode('grid')}\n style={{\n padding: '8px 12px',\n backgroundColor: viewMode === 'grid' ? 'var(--bg-secondary)' : 'transparent',\n border: 'none',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n Grid\n </button>\n <button\n onClick={() => setViewMode('list')}\n style={{\n padding: '8px 12px',\n backgroundColor: viewMode === 'list' ? 'var(--bg-secondary)' : 'transparent',\n border: 'none',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n List\n </button>\n </div>\n </div>\n\n {/* Templates Content */}\n <div style={{ flex: 1, overflowY: 'auto', padding: '24px' }}>\n {loading && displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '40px', color: 'var(--text-secondary)' }}>\n Loading templates...\n </div>\n ) : displayTemplates.length === 0 ? (\n <div style={{ textAlign: 'center', padding: '40px', color: 'var(--text-secondary)' }}>\n No templates found. Try adjusting your filters.\n </div>\n ) : viewMode === 'grid' ? (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',\n gap: '20px',\n }}\n >\n {displayTemplates.map((template) => (\n <TemplateCard\n key={template.id}\n template={template}\n onSelect={() => handleSelectTemplate(template)}\n renderRating={renderRating}\n />\n ))}\n </div>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>\n {displayTemplates.map((template) => (\n <TemplateListItem\n key={template.id}\n template={template}\n onSelect={() => handleSelectTemplate(template)}\n renderRating={renderRating}\n />\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div\n style={{\n padding: '16px 24px',\n borderTop: '1px solid var(--border-color)',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span style={{ fontSize: '13px', color: 'var(--text-secondary)' }}>\n {displayTemplates.length} templates available\n </span>\n <button\n onClick={onClose}\n style={{\n padding: '10px 20px',\n backgroundColor: 'transparent',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n }}\n >\n Cancel\n </button>\n </div>\n </div>\n\n {/* Template Details Modal */}\n {showDetailsModal && selectedTemplate && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 10000,\n }}\n onClick={() => setShowDetailsModal(false)}\n >\n <div\n style={{\n width: '500px',\n backgroundColor: 'var(--bg-primary)',\n borderRadius: '12px',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.2)',\n overflow: 'hidden',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n padding: '20px 24px',\n borderBottom: '1px solid var(--border-color)',\n }}\n >\n <h3 style={{ margin: 0, fontSize: '18px', fontWeight: 600 }}>Create from Template</h3>\n <p style={{ margin: '4px 0 0', fontSize: '13px', color: 'var(--text-secondary)' }}>\n {selectedTemplate.name}\n </p>\n </div>\n <div style={{ padding: '24px' }}>\n <div style={{ marginBottom: '20px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '13px',\n fontWeight: 500,\n }}\n >\n Dashboard Name *\n </label>\n <input\n type=\"text\"\n value={dashboardName}\n onChange={(e) => setDashboardName(e.target.value)}\n placeholder=\"Enter dashboard name...\"\n style={{\n width: '100%',\n padding: '10px 12px',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n fontSize: '14px',\n }}\n />\n </div>\n\n {/* Template Info */}\n <div\n style={{\n padding: '16px',\n backgroundColor: 'var(--bg-secondary)',\n borderRadius: '8px',\n }}\n >\n <div style={{ fontSize: '13px', color: 'var(--text-secondary)', marginBottom: '8px' }}>\n Template Details\n </div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: '16px' }}>\n <div>\n <div style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>Category</div>\n <div style={{ fontSize: '13px' }}>{CATEGORY_LABELS[selectedTemplate.category]}</div>\n </div>\n <div>\n <div style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>Complexity</div>\n <div\n style={{\n fontSize: '13px',\n color: COMPLEXITY_COLORS[selectedTemplate.complexity],\n }}\n >\n {selectedTemplate.complexity}\n </div>\n </div>\n <div>\n <div style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>Used</div>\n <div style={{ fontSize: '13px' }}>{selectedTemplate.usageCount} times</div>\n </div>\n </div>\n </div>\n </div>\n <div\n style={{\n padding: '16px 24px',\n borderTop: '1px solid var(--border-color)',\n display: 'flex',\n justifyContent: 'flex-end',\n gap: '12px',\n }}\n >\n <button\n onClick={() => setShowDetailsModal(false)}\n style={{\n padding: '10px 20px',\n backgroundColor: 'transparent',\n border: '1px solid var(--border-color)',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n }}\n >\n Cancel\n </button>\n <button\n onClick={handleCreateFromTemplate}\n disabled={!dashboardName.trim()}\n style={{\n padding: '10px 20px',\n backgroundColor: dashboardName.trim() ? 'var(--primary)' : 'var(--bg-tertiary)',\n color: dashboardName.trim() ? 'white' : 'var(--text-secondary)',\n border: 'none',\n borderRadius: '6px',\n cursor: dashboardName.trim() ? 'pointer' : 'not-allowed',\n fontSize: '14px',\n fontWeight: 500,\n }}\n >\n Create Dashboard\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\ninterface TemplateCardProps {\n template: DashboardTemplateListItem;\n onSelect: () => void;\n renderRating: (rating: number | undefined, count: number) => React.ReactNode;\n}\n\nfunction TemplateCard({ template, onSelect, renderRating }: TemplateCardProps) {\n return (\n <div\n onClick={onSelect}\n style={{\n backgroundColor: 'var(--bg-secondary)',\n border: '1px solid var(--border-color)',\n borderRadius: '10px',\n overflow: 'hidden',\n cursor: 'pointer',\n transition: 'transform 0.15s, box-shadow 0.15s',\n }}\n onMouseOver={(e) => {\n (e.currentTarget as HTMLDivElement).style.transform = 'translateY(-2px)';\n (e.currentTarget as HTMLDivElement).style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)';\n }}\n onMouseOut={(e) => {\n (e.currentTarget as HTMLDivElement).style.transform = 'none';\n (e.currentTarget as HTMLDivElement).style.boxShadow = 'none';\n }}\n >\n {/* Thumbnail */}\n <div\n style={{\n height: '140px',\n backgroundColor: 'var(--bg-tertiary)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderBottom: '1px solid var(--border-color)',\n }}\n >\n {template.previewUrl ? (\n <img\n src={template.previewUrl}\n alt={template.name}\n style={{ width: '100%', height: '100%', objectFit: 'cover' }}\n />\n ) : (\n <span style={{ fontSize: '40px', color: 'var(--text-tertiary)' }}>📊</span>\n )}\n </div>\n\n {/* Content */}\n <div style={{ padding: '16px' }}>\n <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: '8px' }}>\n <h3 style={{ margin: 0, fontSize: '14px', fontWeight: 600, lineHeight: '1.3' }}>{template.name}</h3>\n {template.isFeatured && (\n <span\n style={{\n fontSize: '9px',\n padding: '2px 6px',\n backgroundColor: '#fef3c7',\n color: '#92400e',\n borderRadius: '4px',\n fontWeight: 600,\n flexShrink: 0,\n }}\n >\n FEATURED\n </span>\n )}\n </div>\n\n {template.description && (\n <p\n style={{\n margin: '8px 0 0',\n fontSize: '12px',\n color: 'var(--text-secondary)',\n lineHeight: '1.4',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {template.description}\n </p>\n )}\n\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '12px', flexWrap: 'wrap' }}>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '4px',\n color: 'var(--text-secondary)',\n }}\n >\n {CATEGORY_LABELS[template.category]}\n </span>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: `${COMPLEXITY_COLORS[template.complexity]}20`,\n color: COMPLEXITY_COLORS[template.complexity],\n borderRadius: '4px',\n fontWeight: 500,\n }}\n >\n {template.complexity}\n </span>\n {renderRating(template.rating, template.ratingCount)}\n </div>\n </div>\n </div>\n );\n}\n\ninterface TemplateListItemProps {\n template: DashboardTemplateListItem;\n onSelect: () => void;\n renderRating: (rating: number | undefined, count: number) => React.ReactNode;\n}\n\nfunction TemplateListItem({ template, onSelect, renderRating }: TemplateListItemProps) {\n return (\n <div\n onClick={onSelect}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '16px',\n padding: '16px',\n backgroundColor: 'var(--bg-secondary)',\n border: '1px solid var(--border-color)',\n borderRadius: '8px',\n cursor: 'pointer',\n transition: 'background-color 0.15s',\n }}\n >\n {/* Thumbnail */}\n <div\n style={{\n width: '80px',\n height: '60px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '6px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n }}\n >\n {template.previewUrl ? (\n <img\n src={template.previewUrl}\n alt={template.name}\n style={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: '6px' }}\n />\n ) : (\n <span style={{ fontSize: '24px', color: 'var(--text-tertiary)' }}>📊</span>\n )}\n </div>\n\n {/* Content */}\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <h3 style={{ margin: 0, fontSize: '14px', fontWeight: 600 }}>{template.name}</h3>\n {template.isFeatured && (\n <span\n style={{\n fontSize: '9px',\n padding: '2px 6px',\n backgroundColor: '#fef3c7',\n color: '#92400e',\n borderRadius: '4px',\n fontWeight: 600,\n }}\n >\n FEATURED\n </span>\n )}\n </div>\n {template.description && (\n <p\n style={{\n margin: '4px 0 0',\n fontSize: '12px',\n color: 'var(--text-secondary)',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {template.description}\n </p>\n )}\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '8px' }}>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '4px',\n }}\n >\n {CATEGORY_LABELS[template.category]}\n </span>\n <span\n style={{\n fontSize: '10px',\n padding: '2px 6px',\n backgroundColor: `${COMPLEXITY_COLORS[template.complexity]}20`,\n color: COMPLEXITY_COLORS[template.complexity],\n borderRadius: '4px',\n }}\n >\n {template.complexity}\n </span>\n {renderRating(template.rating, template.ratingCount)}\n <span style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>{template.usageCount} uses</span>\n </div>\n </div>\n\n {/* Action */}\n <button\n style={{\n padding: '8px 16px',\n backgroundColor: 'var(--primary)',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 500,\n flexShrink: 0,\n }}\n >\n Use Template\n </button>\n </div>\n );\n}\n\n// Compact template card for embedded mode\ninterface CompactTemplateCardProps {\n template: DashboardTemplateListItem;\n isSelected: boolean;\n onSelect: () => void;\n}\n\nfunction CompactTemplateCard({ template, isSelected, onSelect }: CompactTemplateCardProps) {\n return (\n <div\n onClick={onSelect}\n style={{\n backgroundColor: isSelected ? 'var(--color-action-primary-bg-subtle, #e0f2fe)' : 'var(--bg-secondary)',\n border: `2px solid ${isSelected ? 'var(--color-action-primary-border, #3b82f6)' : 'var(--border-color)'}`,\n borderRadius: '8px',\n overflow: 'hidden',\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n >\n {/* Thumbnail */}\n <div\n style={{\n height: '80px',\n backgroundColor: 'var(--bg-tertiary)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderBottom: '1px solid var(--border-color)',\n }}\n >\n {template.previewUrl ? (\n <img\n src={template.previewUrl}\n alt={template.name}\n style={{ width: '100%', height: '100%', objectFit: 'cover' }}\n />\n ) : (\n <span style={{ fontSize: '28px', color: 'var(--text-tertiary)' }}>📊</span>\n )}\n </div>\n\n {/* Content */}\n <div style={{ padding: '10px' }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '4px' }}>\n <h4\n style={{\n margin: 0,\n fontSize: '12px',\n fontWeight: 600,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {template.name}\n </h4>\n {isSelected && (\n <span style={{ color: 'var(--color-action-primary-text, #3b82f6)', fontSize: '14px' }}>✓</span>\n )}\n </div>\n <div style={{ display: 'flex', gap: '4px', marginTop: '6px', flexWrap: 'wrap' }}>\n <span\n style={{\n fontSize: '9px',\n padding: '1px 4px',\n backgroundColor: 'var(--bg-tertiary)',\n borderRadius: '3px',\n color: 'var(--text-secondary)',\n }}\n >\n {CATEGORY_LABELS[template.category]}\n </span>\n <span\n style={{\n fontSize: '9px',\n padding: '1px 4px',\n backgroundColor: `${COMPLEXITY_COLORS[template.complexity]}20`,\n color: COMPLEXITY_COLORS[template.complexity],\n borderRadius: '3px',\n }}\n >\n {template.complexity}\n </span>\n </div>\n </div>\n </div>\n );\n}\n\nexport default TemplatePicker;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetRegistry.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetRegistry.ts"],"sourcesContent":["/**\n * Widget Registry\n *\n * Central registry mapping widget types to their components,\n * metadata, and configuration schemas.\n */\n\nimport type { ComponentType } from 'react';\nimport type { WidgetType, WidgetCategory, Widget } from '../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetComponentProps {\n /** The widget data */\n widget: Widget;\n /** Widget data (processed) */\n data: Record<string, unknown>;\n /** Whether the widget is in edit mode */\n isEditMode?: boolean;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether data is loading */\n isLoading?: boolean;\n /** Error message if any */\n error?: string | null;\n /** Callback when widget is clicked */\n onClick?: () => void;\n /** Callback for drilldown navigation */\n onDrilldown?: (params: Record<string, unknown>) => void;\n}\n\nexport interface WidgetDefinition {\n /** Widget type */\n type: WidgetType;\n /** Human-readable name */\n name: string;\n /** Description */\n description: string;\n /** Lucide icon name */\n icon: string;\n /** Category for grouping */\n category: WidgetCategory;\n /** Component to render. Null for all current widget types because the render path\n * uses WidgetRendererFactory + RendererRegistry instead of this field. */\n component: ComponentType<WidgetComponentProps> | null;\n /** Default configuration */\n defaultConfig: Record<string, unknown>;\n /** Default size (fixed grid) */\n defaultSize: { width: number; height: number };\n /** Default responsive position (v1.0 spec) */\n defaultResponsive: { xs: number; sm: number; md: number; lg: number };\n /** Minimum size constraints */\n minSize: { width: number; height: number };\n /** Maximum size constraints */\n maxSize: { width: number; height: number };\n /** Whether the widget supports data binding */\n supportsDataBinding: boolean;\n /** Whether the widget supports drilldown */\n supportsDrilldown: boolean;\n /** Whether the widget supports auto-refresh */\n supportsAutoRefresh: boolean;\n /** Tags for search/filter */\n tags: string[];\n}\n\n// ============================================================================\n// Widget Definitions\n// ============================================================================\n\nexport const WIDGET_DEFINITIONS: Record<WidgetType, WidgetDefinition> = {\n // ============================================================================\n // Core Visualization (6 types)\n // ============================================================================\n\n kpi_card_comparison: {\n type: 'kpi_card_comparison',\n name: 'KPI Card Comparison',\n description: 'Compare multiple KPIs side by side with trends',\n icon: 'LayoutDashboard',\n category: 'KPI',\n component: null,\n defaultConfig: {\n comparisons: [],\n showSparklines: true,\n layout: 'horizontal',\n format: 'number',\n },\n defaultSize: { width: 6, height: 3 },\n defaultResponsive: { xs: 12, sm: 6, md: 6, lg: 4 },\n minSize: { width: 4, height: 2 },\n maxSize: { width: 12, height: 6 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['kpi', 'comparison', 'metrics', 'multi'],\n },\n\n metric_card: {\n type: 'metric_card',\n name: 'Metric Card',\n description: 'Display a single KPI value with trend indicator',\n icon: 'TrendingUp',\n category: 'KPI',\n component: null,\n defaultConfig: {\n format: 'number',\n decimalPlaces: 0,\n showTrend: true,\n showSparkline: false,\n },\n defaultSize: { width: 3, height: 2 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 3 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 6, height: 4 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['kpi', 'metric', 'number', 'trend'],\n },\n\n chart: {\n type: 'chart',\n name: 'Chart',\n description: 'Multi-type chart (Line, Bar, Area, Pie)',\n icon: 'BarChart3',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n chartType: 'line',\n showLegend: true,\n legendPosition: 'bottom',\n showGrid: true,\n showTooltip: true,\n },\n defaultSize: { width: 6, height: 4 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 3, height: 3 },\n maxSize: { width: 12, height: 8 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['chart', 'graph', 'visualization', 'line', 'bar', 'area', 'pie'],\n },\n\n funnel_chart: {\n type: 'funnel_chart',\n name: 'Funnel Chart',\n description: 'Visualize conversion or drop-off stages',\n icon: 'Filter',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n showLabels: true,\n showValues: true,\n showPercentages: true,\n orientation: 'vertical',\n },\n defaultSize: { width: 4, height: 5 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 3, height: 4 },\n maxSize: { width: 8, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['funnel', 'conversion', 'stages', 'pipeline'],\n },\n\n table: {\n type: 'table',\n name: 'Data Table',\n description: 'Display data in sortable, filterable rows and columns',\n icon: 'Table',\n category: 'Data',\n component: null,\n defaultConfig: {\n pageSize: 10,\n sortable: true,\n filterable: true,\n stickyHeader: true,\n columns: [],\n },\n defaultSize: { width: 8, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 8, lg: 8 },\n minSize: { width: 4, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['table', 'data', 'grid', 'rows'],\n },\n\n pivot_table: {\n type: 'pivot_table',\n name: 'Pivot Table',\n description: 'Interactive pivot table for data analysis',\n icon: 'Grid3x3',\n category: 'Data',\n component: null,\n defaultConfig: {\n rows: [],\n columns: [],\n values: [],\n aggregation: 'sum',\n showTotals: true,\n },\n defaultSize: { width: 10, height: 6 },\n defaultResponsive: { xs: 12, sm: 12, md: 12, lg: 10 },\n minSize: { width: 6, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['pivot', 'table', 'analysis', 'aggregation'],\n },\n\n // ============================================================================\n // Indicators (2 types)\n // ============================================================================\n\n gauge: {\n type: 'gauge',\n name: 'Gauge',\n description: 'Display a value within a range',\n icon: 'Gauge',\n category: 'KPI',\n component: null,\n defaultConfig: {\n variant: 'circular',\n min: 0,\n max: 100,\n showValue: true,\n showLabels: true,\n thresholds: [\n { value: 33, color: 'red' },\n { value: 66, color: 'yellow' },\n { value: 100, color: 'green' },\n ],\n },\n defaultSize: { width: 3, height: 3 },\n defaultResponsive: { xs: 6, sm: 4, md: 3, lg: 3 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 6, height: 6 },\n supportsDataBinding: true,\n supportsDrilldown: false,\n supportsAutoRefresh: true,\n tags: ['gauge', 'meter', 'dial', 'indicator'],\n },\n\n progress: {\n type: 'progress',\n name: 'Progress Bar',\n description: 'Show progress towards a goal with milestones',\n icon: 'Activity',\n category: 'KPI',\n component: null,\n defaultConfig: {\n showPercentage: true,\n showValue: false,\n target: null,\n milestones: [],\n },\n defaultSize: { width: 4, height: 2 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 3, height: 2 },\n maxSize: { width: 12, height: 3 },\n supportsDataBinding: true,\n supportsDrilldown: false,\n supportsAutoRefresh: true,\n tags: ['progress', 'bar', 'goal', 'completion'],\n },\n\n // ============================================================================\n // Data Display (2 types)\n // ============================================================================\n\n list: {\n type: 'list',\n name: 'List',\n description: 'Display items in a scrollable list',\n icon: 'List',\n category: 'Data',\n component: null,\n defaultConfig: {\n showMetadata: true,\n showActions: false,\n showBadges: true,\n maxItems: 10,\n itemClickable: true,\n },\n defaultSize: { width: 4, height: 5 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 3, height: 3 },\n maxSize: { width: 8, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['list', 'items', 'scroll'],\n },\n\n form: {\n type: 'form',\n name: 'Form',\n description: 'Interactive form widget for data input',\n icon: 'FileText',\n category: 'Data',\n component: null,\n defaultConfig: {\n fields: [],\n submitAction: null,\n layout: 'vertical',\n showLabels: true,\n },\n defaultSize: { width: 6, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 4, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: false,\n supportsAutoRefresh: false,\n tags: ['form', 'input', 'submit', 'fields'],\n },\n\n // ============================================================================\n // Content (2 types)\n // ============================================================================\n\n text: {\n type: 'text',\n name: 'Text',\n description: 'Rich text or markdown content widget',\n icon: 'Type',\n category: 'Content',\n component: null,\n defaultConfig: {\n content: '',\n format: 'markdown',\n alignment: 'left',\n },\n defaultSize: { width: 4, height: 3 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 2, height: 1 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: false,\n supportsDrilldown: false,\n supportsAutoRefresh: false,\n tags: ['text', 'markdown', 'content', 'rich text'],\n },\n\n iframe: {\n type: 'iframe',\n name: 'Iframe',\n description: 'Embed external content via iframe',\n icon: 'ExternalLink',\n category: 'Content',\n component: null,\n defaultConfig: {\n url: '',\n sandbox: 'allow-scripts allow-same-origin',\n allowFullscreen: false,\n },\n defaultSize: { width: 6, height: 4 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 3, height: 2 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: false,\n supportsDrilldown: false,\n supportsAutoRefresh: false,\n tags: ['iframe', 'embed', 'external', 'web'],\n },\n\n // ============================================================================\n // Spatial & Temporal (5 types)\n // ============================================================================\n\n map: {\n type: 'map',\n name: 'Map',\n description: 'Geographic map visualization with markers',\n icon: 'MapPin',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n provider: 'mapbox',\n center: { lat: 0, lng: 0 },\n zoom: 2,\n showMarkers: true,\n showRegions: false,\n },\n defaultSize: { width: 6, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 4, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['map', 'geo', 'location', 'markers'],\n },\n\n heatmap: {\n type: 'heatmap',\n name: 'Heatmap',\n description: 'Display values in a color-coded grid',\n icon: 'Grid3x3',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n xAxisField: '',\n yAxisField: '',\n valueField: '',\n colorScale: ['#f0f9ff', '#0369a1'],\n showValues: true,\n },\n defaultSize: { width: 6, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 4, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['heatmap', 'grid', 'density', 'color'],\n },\n\n calendar: {\n type: 'calendar',\n name: 'Calendar',\n description: 'Calendar view for events and schedules',\n icon: 'Calendar',\n category: 'Temporal',\n component: null,\n defaultConfig: {\n view: 'month',\n showWeekNumbers: false,\n firstDayOfWeek: 0,\n eventField: '',\n },\n defaultSize: { width: 8, height: 6 },\n defaultResponsive: { xs: 12, sm: 12, md: 8, lg: 8 },\n minSize: { width: 6, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['calendar', 'events', 'schedule', 'dates'],\n },\n\n kanban: {\n type: 'kanban',\n name: 'Kanban Board',\n description: 'Kanban board for task and workflow management',\n icon: 'Columns',\n category: 'Data',\n component: null,\n defaultConfig: {\n columns: [],\n cardTitleField: '',\n cardDescriptionField: '',\n columnField: '',\n allowDragDrop: true,\n },\n defaultSize: { width: 12, height: 6 },\n defaultResponsive: { xs: 12, sm: 12, md: 12, lg: 12 },\n minSize: { width: 8, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['kanban', 'board', 'tasks', 'workflow'],\n },\n\n timeline: {\n type: 'timeline',\n name: 'Timeline',\n description: 'Timeline or Gantt chart for project scheduling',\n icon: 'GitBranch',\n category: 'Temporal',\n component: null,\n defaultConfig: {\n startDateField: '',\n endDateField: '',\n titleField: '',\n groupField: '',\n showToday: true,\n },\n defaultSize: { width: 12, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 12, lg: 12 },\n minSize: { width: 8, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['timeline', 'gantt', 'project', 'schedule'],\n },\n\n // ============================================================================\n // Extensibility (1 type)\n // ============================================================================\n\n custom: {\n type: 'custom',\n name: 'Custom Widget',\n description: 'Custom widget with user-defined rendering',\n icon: 'Puzzle',\n category: 'Custom',\n component: null,\n defaultConfig: {\n componentId: '',\n props: {},\n },\n defaultSize: { width: 4, height: 4 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['custom', 'plugin', 'extension'],\n },\n};\n\n// ============================================================================\n// Registry Functions\n// ============================================================================\n\n/**\n * Default widget definition for unknown types\n */\nconst DEFAULT_WIDGET_DEFINITION: WidgetDefinition = {\n type: 'custom' as WidgetType,\n name: 'Unknown Widget',\n description: 'Unknown widget type',\n icon: 'Puzzle',\n category: 'Custom',\n component: null,\n defaultConfig: {},\n defaultSize: { width: 4, height: 4 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['unknown'],\n};\n\n/**\n * Get widget definition by type\n * Handles both lowercase (frontend) and uppercase (backend) types\n */\nexport function getWidgetDefinition(type: WidgetType | string): WidgetDefinition {\n // Try exact match first\n if (WIDGET_DEFINITIONS[type as WidgetType]) {\n return WIDGET_DEFINITIONS[type as WidgetType];\n }\n\n // Try lowercase version (backend returns uppercase for some types)\n const lowerType = type.toLowerCase() as WidgetType;\n if (WIDGET_DEFINITIONS[lowerType]) {\n return WIDGET_DEFINITIONS[lowerType];\n }\n\n // Return default definition for unknown types\n return { ...DEFAULT_WIDGET_DEFINITION, type: type as WidgetType };\n}\n\n/**\n * Get all widget definitions\n */\nexport function getAllWidgetDefinitions(): WidgetDefinition[] {\n return Object.values(WIDGET_DEFINITIONS);\n}\n\n/**\n * Get widget definitions by category\n */\nexport function getWidgetsByCategory(category: WidgetCategory): WidgetDefinition[] {\n return Object.values(WIDGET_DEFINITIONS).filter((def) => def.category === category);\n}\n\n/**\n * Search widget definitions by query\n */\nexport function searchWidgets(query: string): WidgetDefinition[] {\n const lowerQuery = query.toLowerCase();\n return Object.values(WIDGET_DEFINITIONS).filter(\n (def) =>\n def.name.toLowerCase().includes(lowerQuery) ||\n def.description.toLowerCase().includes(lowerQuery) ||\n def.tags.some((tag) => tag.includes(lowerQuery))\n );\n}\n\n/**\n * Canonical mapping from widget category to human-readable label.\n * Shared across WidgetTypeSelector, WidgetPalette, and other UI.\n */\nexport const WIDGET_CATEGORY_LABELS: Record<WidgetCategory, string> = {\n KPI: 'Metrics & KPIs',\n Visualization: 'Charts & Visualization',\n Data: 'Data Display',\n Content: 'Content',\n Temporal: 'Temporal & Scheduling',\n Custom: 'Custom',\n Installed: 'Installed from Store',\n};\n\n/**\n * Get widget categories with their definitions\n */\nexport function getWidgetCategories(): {\n category: WidgetCategory;\n label: string;\n widgets: WidgetDefinition[];\n}[] {\n const categories: WidgetCategory[] = ['KPI', 'Visualization', 'Data', 'Content', 'Temporal', 'Custom', 'Installed'];\n\n return categories.map((category) => ({\n category,\n label: WIDGET_CATEGORY_LABELS[category],\n widgets: getWidgetsByCategory(category),\n }));\n}\n\n/**\n * Validate widget size constraints\n */\nexport function validateWidgetSize(\n type: WidgetType,\n width: number,\n height: number\n): { valid: boolean; width: number; height: number } {\n const def = WIDGET_DEFINITIONS[type];\n\n const clampedWidth = Math.min(Math.max(width, def.minSize.width), def.maxSize.width);\n const clampedHeight = Math.min(Math.max(height, def.minSize.height), def.maxSize.height);\n\n return {\n valid: width === clampedWidth && height === clampedHeight,\n width: clampedWidth,\n height: clampedHeight,\n };\n}\n\n/**\n * Get a stable key for a widget definition (handles installed widgets with componentId)\n */\nexport function getWidgetKey(widget: WidgetDefinition): string {\n if (widget.defaultConfig?.componentId) {\n return `installed-${widget.defaultConfig.componentId}`;\n }\n if (widget.category === 'Installed') {\n return `installed-${widget.name}`;\n }\n return widget.type;\n}\n\nexport default WIDGET_DEFINITIONS;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WidgetRegistry.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetRegistry.ts"],"sourcesContent":["/**\n * Widget Registry\n *\n * Central registry mapping widget types to their components,\n * metadata, and configuration schemas.\n */\n\nimport type { ComponentType } from 'react';\nimport type { WidgetType, WidgetCategory, Widget } from '../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetComponentProps {\n /** The widget data */\n widget: Widget;\n /** Widget data (processed) */\n data: Record<string, unknown>;\n /** Whether the widget is in edit mode */\n isEditMode?: boolean;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether data is loading */\n isLoading?: boolean;\n /** Error message if any */\n error?: string | null;\n /** Callback when widget is clicked */\n onClick?: () => void;\n /** Callback for drilldown navigation */\n onDrilldown?: (params: Record<string, unknown>) => void;\n}\n\nexport interface WidgetDefinition {\n /** Widget type */\n type: WidgetType;\n /** Human-readable name */\n name: string;\n /** Description */\n description: string;\n /** Lucide icon name */\n icon: string;\n /** Category for grouping */\n category: WidgetCategory;\n /** Component to render. Null for all current widget types because the render path\n * uses WidgetRendererFactory + RendererRegistry instead of this field. */\n component: ComponentType<WidgetComponentProps> | null;\n /** Default configuration */\n defaultConfig: Record<string, unknown>;\n /** Default size (fixed grid) */\n defaultSize: { width: number; height: number };\n /** Default responsive position (v1.0 spec) */\n defaultResponsive: { xs: number; sm: number; md: number; lg: number };\n /** Minimum size constraints */\n minSize: { width: number; height: number };\n /** Maximum size constraints */\n maxSize: { width: number; height: number };\n /** Whether the widget supports data binding */\n supportsDataBinding: boolean;\n /** Whether the widget supports drilldown */\n supportsDrilldown: boolean;\n /** Whether the widget supports auto-refresh */\n supportsAutoRefresh: boolean;\n /** Tags for search/filter */\n tags: string[];\n}\n\n// ============================================================================\n// Widget Definitions\n// ============================================================================\n\nexport const WIDGET_DEFINITIONS: Record<WidgetType, WidgetDefinition> = {\n // ============================================================================\n // Core Visualization (6 types)\n // ============================================================================\n\n kpi_card_comparison: {\n type: 'kpi_card_comparison',\n name: 'KPI Card Comparison',\n description: 'Compare multiple KPIs side by side with trends',\n icon: 'LayoutDashboard',\n category: 'KPI',\n component: null,\n defaultConfig: {\n comparisons: [],\n showSparklines: true,\n layout: 'horizontal',\n format: 'number',\n },\n defaultSize: { width: 6, height: 3 },\n defaultResponsive: { xs: 12, sm: 6, md: 6, lg: 4 },\n minSize: { width: 4, height: 2 },\n maxSize: { width: 12, height: 6 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['kpi', 'comparison', 'metrics', 'multi'],\n },\n\n metric_card: {\n type: 'metric_card',\n name: 'Metric Card',\n description: 'Display a single KPI value with trend indicator',\n icon: 'TrendingUp',\n category: 'KPI',\n component: null,\n defaultConfig: {\n format: 'number',\n decimalPlaces: 0,\n showTrend: true,\n showSparkline: false,\n },\n defaultSize: { width: 3, height: 2 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 3 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 6, height: 4 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['kpi', 'metric', 'number', 'trend'],\n },\n\n chart: {\n type: 'chart',\n name: 'Chart',\n description: 'Multi-type chart (Line, Bar, Area, Pie)',\n icon: 'BarChart3',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n chartType: 'line',\n showLegend: true,\n legendPosition: 'bottom',\n showGrid: true,\n showTooltip: true,\n },\n defaultSize: { width: 6, height: 4 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 3, height: 3 },\n maxSize: { width: 12, height: 8 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['chart', 'graph', 'visualization', 'line', 'bar', 'area', 'pie'],\n },\n\n funnel_chart: {\n type: 'funnel_chart',\n name: 'Funnel Chart',\n description: 'Visualize conversion or drop-off stages',\n icon: 'Filter',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n showLabels: true,\n showValues: true,\n showPercentages: true,\n orientation: 'vertical',\n },\n defaultSize: { width: 4, height: 5 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 3, height: 4 },\n maxSize: { width: 8, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['funnel', 'conversion', 'stages', 'pipeline'],\n },\n\n table: {\n type: 'table',\n name: 'Data Table',\n description: 'Display data in sortable, filterable rows and columns',\n icon: 'Table',\n category: 'Data',\n component: null,\n defaultConfig: {\n pageSize: 10,\n sortable: true,\n filterable: true,\n stickyHeader: true,\n columns: [],\n },\n defaultSize: { width: 8, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 8, lg: 8 },\n minSize: { width: 4, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['table', 'data', 'grid', 'rows'],\n },\n\n pivot_table: {\n type: 'pivot_table',\n name: 'Pivot Table',\n description: 'Interactive pivot table for data analysis',\n icon: 'Grid3x3',\n category: 'Data',\n component: null,\n defaultConfig: {\n rows: [],\n columns: [],\n values: [],\n aggregation: 'sum',\n showTotals: true,\n },\n defaultSize: { width: 10, height: 6 },\n defaultResponsive: { xs: 12, sm: 12, md: 12, lg: 10 },\n minSize: { width: 6, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['pivot', 'table', 'analysis', 'aggregation'],\n },\n\n // ============================================================================\n // Indicators (2 types)\n // ============================================================================\n\n gauge: {\n type: 'gauge',\n name: 'Gauge',\n description: 'Display a value within a range',\n icon: 'Gauge',\n category: 'KPI',\n component: null,\n defaultConfig: {\n variant: 'circular',\n min: 0,\n max: 100,\n showValue: true,\n showLabels: true,\n thresholds: [\n { value: 33, color: 'red' },\n { value: 66, color: 'yellow' },\n { value: 100, color: 'green' },\n ],\n },\n defaultSize: { width: 3, height: 3 },\n defaultResponsive: { xs: 6, sm: 4, md: 3, lg: 3 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 6, height: 6 },\n supportsDataBinding: true,\n supportsDrilldown: false,\n supportsAutoRefresh: true,\n tags: ['gauge', 'meter', 'dial', 'indicator'],\n },\n\n progress: {\n type: 'progress',\n name: 'Progress Bar',\n description: 'Show progress towards a goal with milestones',\n icon: 'Activity',\n category: 'KPI',\n component: null,\n defaultConfig: {\n showPercentage: true,\n showValue: false,\n target: null,\n milestones: [],\n },\n defaultSize: { width: 4, height: 2 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 3, height: 2 },\n maxSize: { width: 12, height: 3 },\n supportsDataBinding: true,\n supportsDrilldown: false,\n supportsAutoRefresh: true,\n tags: ['progress', 'bar', 'goal', 'completion'],\n },\n\n // ============================================================================\n // Data Display (2 types)\n // ============================================================================\n\n list: {\n type: 'list',\n name: 'List',\n description: 'Display items in a scrollable list',\n icon: 'List',\n category: 'Data',\n component: null,\n defaultConfig: {\n showMetadata: true,\n showActions: false,\n showBadges: true,\n maxItems: 10,\n itemClickable: true,\n },\n defaultSize: { width: 4, height: 5 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 3, height: 3 },\n maxSize: { width: 8, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['list', 'items', 'scroll'],\n },\n\n form: {\n type: 'form',\n name: 'Form',\n description: 'Interactive form widget for data input',\n icon: 'FileText',\n category: 'Data',\n component: null,\n defaultConfig: {\n fields: [],\n submitAction: null,\n layout: 'vertical',\n showLabels: true,\n },\n defaultSize: { width: 6, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 4, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: false,\n supportsAutoRefresh: false,\n tags: ['form', 'input', 'submit', 'fields'],\n },\n\n // ============================================================================\n // Content (2 types)\n // ============================================================================\n\n text: {\n type: 'text',\n name: 'Text',\n description: 'Rich text or markdown content widget',\n icon: 'Type',\n category: 'Content',\n component: null,\n defaultConfig: {\n content: '',\n format: 'markdown',\n alignment: 'left',\n },\n defaultSize: { width: 4, height: 3 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 2, height: 1 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: false,\n supportsDrilldown: false,\n supportsAutoRefresh: false,\n tags: ['text', 'markdown', 'content', 'rich text'],\n },\n\n iframe: {\n type: 'iframe',\n name: 'Iframe',\n description: 'Embed external content via iframe',\n icon: 'ExternalLink',\n category: 'Content',\n component: null,\n defaultConfig: {\n url: '',\n sandbox: 'allow-scripts allow-same-origin',\n allowFullscreen: false,\n },\n defaultSize: { width: 6, height: 4 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 3, height: 2 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: false,\n supportsDrilldown: false,\n supportsAutoRefresh: false,\n tags: ['iframe', 'embed', 'external', 'web'],\n },\n\n // ============================================================================\n // Spatial & Temporal (5 types)\n // ============================================================================\n\n map: {\n type: 'map',\n name: 'Map',\n description: 'Geographic map visualization with markers',\n icon: 'MapPin',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n provider: 'mapbox',\n center: { lat: 0, lng: 0 },\n zoom: 2,\n showMarkers: true,\n showRegions: false,\n },\n defaultSize: { width: 6, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 4, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['map', 'geo', 'location', 'markers'],\n },\n\n heatmap: {\n type: 'heatmap',\n name: 'Heatmap',\n description: 'Display values in a color-coded grid',\n icon: 'Grid3x3',\n category: 'Visualization',\n component: null,\n defaultConfig: {\n xAxisField: '',\n yAxisField: '',\n valueField: '',\n // HeatmapWidget renders intensity from `colorScheme` (token-based Tailwind\n // classes), not `colorScale`; keep the scale empty rather than seeding it\n // with CSS var() strings that a data array can never resolve.\n colorScale: [],\n colorScheme: 'blue',\n showValues: true,\n },\n defaultSize: { width: 6, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 6, lg: 6 },\n minSize: { width: 4, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['heatmap', 'grid', 'density', 'color'],\n },\n\n calendar: {\n type: 'calendar',\n name: 'Calendar',\n description: 'Calendar view for events and schedules',\n icon: 'Calendar',\n category: 'Temporal',\n component: null,\n defaultConfig: {\n view: 'month',\n showWeekNumbers: false,\n firstDayOfWeek: 0,\n eventField: '',\n },\n defaultSize: { width: 8, height: 6 },\n defaultResponsive: { xs: 12, sm: 12, md: 8, lg: 8 },\n minSize: { width: 6, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['calendar', 'events', 'schedule', 'dates'],\n },\n\n kanban: {\n type: 'kanban',\n name: 'Kanban Board',\n description: 'Kanban board for task and workflow management',\n icon: 'Columns',\n category: 'Data',\n component: null,\n defaultConfig: {\n columns: [],\n cardTitleField: '',\n cardDescriptionField: '',\n columnField: '',\n allowDragDrop: true,\n },\n defaultSize: { width: 12, height: 6 },\n defaultResponsive: { xs: 12, sm: 12, md: 12, lg: 12 },\n minSize: { width: 8, height: 4 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['kanban', 'board', 'tasks', 'workflow'],\n },\n\n timeline: {\n type: 'timeline',\n name: 'Timeline',\n description: 'Timeline or Gantt chart for project scheduling',\n icon: 'GitBranch',\n category: 'Temporal',\n component: null,\n defaultConfig: {\n startDateField: '',\n endDateField: '',\n titleField: '',\n groupField: '',\n showToday: true,\n },\n defaultSize: { width: 12, height: 5 },\n defaultResponsive: { xs: 12, sm: 12, md: 12, lg: 12 },\n minSize: { width: 8, height: 3 },\n maxSize: { width: 12, height: 10 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['timeline', 'gantt', 'project', 'schedule'],\n },\n\n // ============================================================================\n // Extensibility (1 type)\n // ============================================================================\n\n custom: {\n type: 'custom',\n name: 'Custom Widget',\n description: 'Custom widget with user-defined rendering',\n icon: 'Puzzle',\n category: 'Custom',\n component: null,\n defaultConfig: {\n componentId: '',\n props: {},\n },\n defaultSize: { width: 4, height: 4 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['custom', 'plugin', 'extension'],\n },\n};\n\n// ============================================================================\n// Registry Functions\n// ============================================================================\n\n/**\n * Default widget definition for unknown types\n */\nconst DEFAULT_WIDGET_DEFINITION: WidgetDefinition = {\n type: 'custom' as WidgetType,\n name: 'Unknown Widget',\n description: 'Unknown widget type',\n icon: 'Puzzle',\n category: 'Custom',\n component: null,\n defaultConfig: {},\n defaultSize: { width: 4, height: 4 },\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n minSize: { width: 2, height: 2 },\n maxSize: { width: 12, height: 12 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['unknown'],\n};\n\n/**\n * Get widget definition by type\n * Handles both lowercase (frontend) and uppercase (backend) types\n */\nexport function getWidgetDefinition(type: WidgetType | string): WidgetDefinition {\n // Try exact match first\n if (WIDGET_DEFINITIONS[type as WidgetType]) {\n return WIDGET_DEFINITIONS[type as WidgetType];\n }\n\n // Try lowercase version (backend returns uppercase for some types)\n const lowerType = type.toLowerCase() as WidgetType;\n if (WIDGET_DEFINITIONS[lowerType]) {\n return WIDGET_DEFINITIONS[lowerType];\n }\n\n // Return default definition for unknown types\n return { ...DEFAULT_WIDGET_DEFINITION, type: type as WidgetType };\n}\n\n/**\n * Get all widget definitions\n */\nexport function getAllWidgetDefinitions(): WidgetDefinition[] {\n return Object.values(WIDGET_DEFINITIONS);\n}\n\n/**\n * Get widget definitions by category\n */\nexport function getWidgetsByCategory(category: WidgetCategory): WidgetDefinition[] {\n return Object.values(WIDGET_DEFINITIONS).filter((def) => def.category === category);\n}\n\n/**\n * Search widget definitions by query\n */\nexport function searchWidgets(query: string): WidgetDefinition[] {\n const lowerQuery = query.toLowerCase();\n return Object.values(WIDGET_DEFINITIONS).filter(\n (def) =>\n def.name.toLowerCase().includes(lowerQuery) ||\n def.description.toLowerCase().includes(lowerQuery) ||\n def.tags.some((tag) => tag.includes(lowerQuery))\n );\n}\n\n/**\n * Canonical mapping from widget category to human-readable label.\n * Shared across WidgetTypeSelector, WidgetPalette, and other UI.\n */\nexport const WIDGET_CATEGORY_LABELS: Record<WidgetCategory, string> = {\n KPI: 'Metrics & KPIs',\n Visualization: 'Charts & Visualization',\n Data: 'Data Display',\n Content: 'Content',\n Temporal: 'Temporal & Scheduling',\n Custom: 'Custom',\n Installed: 'Installed from Store',\n};\n\n/**\n * Get widget categories with their definitions\n */\nexport function getWidgetCategories(): {\n category: WidgetCategory;\n label: string;\n widgets: WidgetDefinition[];\n}[] {\n const categories: WidgetCategory[] = ['KPI', 'Visualization', 'Data', 'Content', 'Temporal', 'Custom', 'Installed'];\n\n return categories.map((category) => ({\n category,\n label: WIDGET_CATEGORY_LABELS[category],\n widgets: getWidgetsByCategory(category),\n }));\n}\n\n/**\n * Validate widget size constraints\n */\nexport function validateWidgetSize(\n type: WidgetType,\n width: number,\n height: number\n): { valid: boolean; width: number; height: number } {\n const def = WIDGET_DEFINITIONS[type];\n\n const clampedWidth = Math.min(Math.max(width, def.minSize.width), def.maxSize.width);\n const clampedHeight = Math.min(Math.max(height, def.minSize.height), def.maxSize.height);\n\n return {\n valid: width === clampedWidth && height === clampedHeight,\n width: clampedWidth,\n height: clampedHeight,\n };\n}\n\n/**\n * Get a stable key for a widget definition (handles installed widgets with componentId)\n */\nexport function getWidgetKey(widget: WidgetDefinition): string {\n if (widget.defaultConfig?.componentId) {\n return `installed-${widget.defaultConfig.componentId}`;\n }\n if (widget.category === 'Installed') {\n return `installed-${widget.name}`;\n }\n return widget.type;\n}\n\nexport default WIDGET_DEFINITIONS;\n"],"mappings":"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"}
|