@burdenoff/microfe-vibecontrols 2026.528.2 → 2026.528.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/components/audit/AuditLogDetail.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/settings/WorkspaceDefaultsSection.js.map +1 -1
- package/dist/components/shared/ConfirmDialog.js.map +1 -1
- package/dist/components/shared/EmptyState.js.map +1 -1
- package/dist/components/targets/TargetCard.js +82 -74
- package/dist/components/targets/TargetCard.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/generated/wspace-operations.js +1 -0
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +10 -1
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +274 -259
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeAuditPage.js","names":[],"sources":["../../src/pages/VibeAuditPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Shield } from 'lucide-react';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuditDeleteDialog,\n AuditExportButton,\n AuditLogDetail,\n AuditLogFilters,\n AuditLogStats,\n AuditLogTable,\n} from '@/components/audit';\nimport {\n EmptyState,\n ErrorState,\n GetHelpButton,\n LoadingState,\n PageHeader,\n} from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n useBulkDeleteVibeAuditLogsMutation,\n useDeleteVibeAuditLogMutation,\n useGetVibeAuditLogQuery,\n useVibeAuditDashboardQuery,\n} from '@/generated/wspace-operations';\nimport type {\n VibeAuditAction,\n VibeAuditLogFilterInput,\n VibeAuditResourceType,\n VibeAuditStatus,\n} from '@/generated/wspace-types';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nconst PAGE_SIZE = 50;\n\nexport function VibeAuditPage() {\n useVibeControls();\n\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');\n const [actionFilter, setActionFilter] = useState<string | null>('all');\n const [resourceTypeFilter, setResourceTypeFilter] = useState<string | null>('all');\n const [statusFilter, setStatusFilter] = useState<string | null>('all');\n const [hasCommandFilter, setHasCommandFilter] = useState(false);\n const [page, setPage] = useState(0);\n const [selectedLogId, setSelectedLogId] = useState<string | null>(null);\n const [selectedLogIds, setSelectedLogIds] = useState<Set<string>>(new Set());\n const [deleteTargetIds, setDeleteTargetIds] = useState<string[]>([]);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n const [deleteSuccess, setDeleteSuccess] = useState<string | null>(null);\n\n const deleteErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const canListAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_LIST);\n const canDeleteAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_DELETE);\n const canExportAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_EXPORT);\n const shouldSkipQueries = permissionsLoading || !canListAudit;\n\n const filter: VibeAuditLogFilterInput = {};\n if (debouncedSearchQuery) filter.search = debouncedSearchQuery;\n if (actionFilter && actionFilter !== 'all') filter.action = actionFilter as VibeAuditAction;\n if (resourceTypeFilter && resourceTypeFilter !== 'all')\n filter.resourceType = resourceTypeFilter as VibeAuditResourceType;\n if (statusFilter && statusFilter !== 'all') filter.status = statusFilter as VibeAuditStatus;\n if (hasCommandFilter) filter.hasCommand = true;\n\n const { data, loading, error, refetch } = useVibeAuditDashboardQuery({\n variables: {\n filter,\n pagination: { limit: Math.min(Math.max((page + 1) * PAGE_SIZE, PAGE_SIZE), 100) },\n },\n skip: shouldSkipQueries,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: detailData } = useGetVibeAuditLogQuery({\n variables: { id: selectedLogId! },\n skip: !selectedLogId,\n });\n\n const [deleteAuditLog, { loading: deleteLoading }] = useDeleteVibeAuditLogMutation();\n const [bulkDeleteAuditLogs] = useBulkDeleteVibeAuditLogsMutation();\n\n useEffect(() => {\n const timeoutId = window.setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n setPage(0);\n }, 300);\n\n return () => window.clearTimeout(timeoutId);\n }, [searchQuery]);\n\n useEffect(() => {\n return () => {\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n }\n };\n }, []);\n\n const clearDeleteError = useCallback(() => {\n setDeleteError(null);\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n deleteErrorTimer.current = null;\n }\n }, []);\n\n const clearDeleteSuccess = useCallback(() => {\n setDeleteSuccess(null);\n }, []);\n\n const allItems = data?.vibeAuditLogs.items ?? [];\n const items = allItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n const totalAuditItems = data?.vibeAuditLogs.total ?? 0;\n const visibleLogIds = items.map((item: (typeof items)[number]) => item.id);\n const allVisibleSelected =\n visibleLogIds.length > 0 && visibleLogIds.every((id: string) => selectedLogIds.has(id));\n const partiallySelected =\n visibleLogIds.some((id: string) => selectedLogIds.has(id)) && !allVisibleSelected;\n\n const handleDelete = async (reason: string) => {\n if (deleteTargetIds.length === 0) return;\n\n clearDeleteError();\n clearDeleteSuccess();\n\n try {\n if (deleteTargetIds.length === 1) {\n await deleteAuditLog({ variables: { id: deleteTargetIds[0], input: { reason } } });\n setDeleteSuccess('Deleted 1 audit log');\n } else {\n const result = await bulkDeleteAuditLogs({\n variables: { ids: deleteTargetIds, input: { reason } },\n });\n const deletedCount = result.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0;\n const failedCount = result.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;\n setDeleteSuccess(\n `Deleted ${deletedCount} audit logs${failedCount > 0 ? ` • ${failedCount} failed` : ''}`\n );\n }\n\n setDeleteTargetIds([]);\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n deleteTargetIds.forEach((id) => next.delete(id));\n return next;\n });\n if (selectedLogId && deleteTargetIds.includes(selectedLogId)) {\n setSelectedLogId(null);\n }\n await refetch();\n if (isNative()) void nativeNotify('success');\n } catch (deleteAuditError) {\n console.error('Failed to delete audit log:', deleteAuditError);\n setDeleteError(tr('audit.deleteFailed', 'Failed to delete audit log. Please try again.'));\n if (isNative()) void nativeNotify('error');\n deleteErrorTimer.current = setTimeout(() => {\n setDeleteError(null);\n deleteErrorTimer.current = null;\n }, 30000);\n }\n };\n\n const handleOpenDelete = useCallback(\n (id: string) => {\n if (!canDeleteAudit) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds([id]);\n },\n [canDeleteAudit, clearDeleteError, clearDeleteSuccess]\n );\n\n const handleToggleLogSelection = useCallback((id: string, checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n }, []);\n\n const handleToggleAllVisible = useCallback(\n (checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n visibleLogIds.forEach((id: string) => {\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n return next;\n });\n },\n [visibleLogIds]\n );\n\n const handleOpenBulkDelete = useCallback(() => {\n if (!canDeleteAudit || selectedLogIds.size === 0) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds(Array.from(selectedLogIds));\n }, [canDeleteAudit, clearDeleteError, clearDeleteSuccess, selectedLogIds]);\n\n if (permissionsLoading) {\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <LoadingState message={tr('audit.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (\n permissions &&\n permissions.permissionStrings?.length > 0 &&\n !canListAudit\n ) {\n return (\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'common.accessDeniedDescription',\n \"You don't have permission to access this resource.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.vibeAudit', 'Audit')}\n description={tr(\n 'audit.description',\n 'Comprehensive audit trail of all VibeControls operations'\n )}\n actions={canExportAudit ? <AuditExportButton filter={filter} /> : undefined}\n />\n\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n\n <AuditLogFilters\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n actionFilter={actionFilter}\n onActionChange={(value) => {\n setActionFilter(value);\n setPage(0);\n }}\n resourceTypeFilter={resourceTypeFilter}\n onResourceTypeChange={(value) => {\n setResourceTypeFilter(value);\n setPage(0);\n }}\n statusFilter={statusFilter}\n onStatusChange={(value) => {\n setStatusFilter(value);\n setPage(0);\n }}\n hasCommandFilter={hasCommandFilter}\n onHasCommandChange={(value) => {\n setHasCommandFilter(value);\n setPage(0);\n }}\n />\n\n {items.length > 0 ? (\n <div className=\"rounded-lg border border-border-primary bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedLogIds.size} selected\n </span>\n {selectedLogIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedLogIds(new Set())}\n className=\"text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n Clear selection\n </button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleOpenBulkDelete}\n disabled={!canDeleteAudit || selectedLogIds.size === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n Delete Selected{selectedLogIds.size > 0 ? ` (${selectedLogIds.size})` : ''}\n </button>\n </div>\n </div>\n </div>\n ) : null}\n\n {loading && !data ? (\n <LoadingState message={tr('audit.loadingLogs', 'Loading audit logs...')} />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => refetch()} />\n ) : totalAuditItems === 0 ? (\n <EmptyState\n icon={<Shield size={48} className=\"text-text-secondary\" />}\n title={tr('audit.noLogs', 'No audit logs')}\n description={tr(\n 'audit.emptyDescription',\n 'Audit logs will appear here as operations are performed in VibeControls.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Audit Log in VibeControls and how do I use it?\" />\n }\n />\n ) : (\n <AuditLogTable\n items={items}\n total={totalAuditItems}\n hasMore={data?.vibeAuditLogs.hasMore ?? false}\n page={page}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n onSelectLog={setSelectedLogId}\n onDeleteLog={handleOpenDelete}\n selectedLogId={selectedLogId}\n selectedLogIds={selectedLogIds}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n onToggleLogSelection={handleToggleLogSelection}\n onToggleAllVisible={handleToggleAllVisible}\n />\n )}\n\n {selectedLogId && detailData?.vibeAuditLog ? (\n <AuditLogDetail log={detailData.vibeAuditLog} onClose={() => setSelectedLogId(null)} />\n ) : null}\n\n {deleteError ? (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text shadow-lg\"\n >\n {deleteError}\n <button\n type=\"button\"\n onClick={clearDeleteError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n {deleteSuccess ? (\n <div\n role=\"status\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-success-bg px-4 py-3 text-sm text-status-success-text shadow-lg\"\n >\n {deleteSuccess}\n <button\n type=\"button\"\n onClick={clearDeleteSuccess}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n <AuditDeleteDialog\n isOpen={deleteTargetIds.length > 0}\n onClose={() => setDeleteTargetIds([])}\n onConfirm={handleDelete}\n loading={deleteLoading}\n title={deleteTargetIds.length > 1 ? 'Delete Audit Logs' : 'Delete Audit Log'}\n description={\n deleteTargetIds.length > 1\n ? `This will permanently delete ${deleteTargetIds.length} audit logs and record the reason in the audit trail.`\n : 'This action will be recorded in the audit trail.'\n }\n confirmLabel={deleteTargetIds.length > 1 ? 'Delete Logs' : 'Delete'}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeAuditPage.js","names":[],"sources":["../../src/pages/VibeAuditPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Shield } from 'lucide-react';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuditDeleteDialog,\n AuditExportButton,\n AuditLogDetail,\n AuditLogFilters,\n AuditLogStats,\n AuditLogTable,\n} from '@/components/audit';\nimport {\n EmptyState,\n ErrorState,\n GetHelpButton,\n LoadingState,\n PageHeader,\n} from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n useBulkDeleteVibeAuditLogsMutation,\n useDeleteVibeAuditLogMutation,\n useGetVibeAuditLogQuery,\n useVibeAuditDashboardQuery,\n} from '@/generated/wspace-operations';\nimport type {\n VibeAuditAction,\n VibeAuditLogFilterInput,\n VibeAuditResourceType,\n VibeAuditStatus,\n} from '@/generated/wspace-types';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nconst PAGE_SIZE = 50;\n\nexport function VibeAuditPage() {\n useVibeControls();\n\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');\n const [actionFilter, setActionFilter] = useState<string | null>('all');\n const [resourceTypeFilter, setResourceTypeFilter] = useState<string | null>('all');\n const [statusFilter, setStatusFilter] = useState<string | null>('all');\n const [hasCommandFilter, setHasCommandFilter] = useState(false);\n const [page, setPage] = useState(0);\n const [selectedLogId, setSelectedLogId] = useState<string | null>(null);\n const [selectedLogIds, setSelectedLogIds] = useState<Set<string>>(new Set());\n const [deleteTargetIds, setDeleteTargetIds] = useState<string[]>([]);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n const [deleteSuccess, setDeleteSuccess] = useState<string | null>(null);\n\n const deleteErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const canListAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_LIST);\n const canDeleteAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_DELETE);\n const canExportAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_EXPORT);\n const shouldSkipQueries = permissionsLoading || !canListAudit;\n\n const filter: VibeAuditLogFilterInput = {};\n if (debouncedSearchQuery) filter.search = debouncedSearchQuery;\n if (actionFilter && actionFilter !== 'all') filter.action = actionFilter as VibeAuditAction;\n if (resourceTypeFilter && resourceTypeFilter !== 'all')\n filter.resourceType = resourceTypeFilter as VibeAuditResourceType;\n if (statusFilter && statusFilter !== 'all') filter.status = statusFilter as VibeAuditStatus;\n if (hasCommandFilter) filter.hasCommand = true;\n\n const { data, loading, error, refetch } = useVibeAuditDashboardQuery({\n variables: {\n filter,\n pagination: { limit: Math.min(Math.max((page + 1) * PAGE_SIZE, PAGE_SIZE), 100) },\n },\n skip: shouldSkipQueries,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: detailData } = useGetVibeAuditLogQuery({\n variables: { id: selectedLogId! },\n skip: !selectedLogId,\n });\n\n const [deleteAuditLog, { loading: deleteLoading }] = useDeleteVibeAuditLogMutation();\n const [bulkDeleteAuditLogs] = useBulkDeleteVibeAuditLogsMutation();\n\n useEffect(() => {\n const timeoutId = window.setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n setPage(0);\n }, 300);\n\n return () => window.clearTimeout(timeoutId);\n }, [searchQuery]);\n\n useEffect(() => {\n return () => {\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n }\n };\n }, []);\n\n const clearDeleteError = useCallback(() => {\n setDeleteError(null);\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n deleteErrorTimer.current = null;\n }\n }, []);\n\n const clearDeleteSuccess = useCallback(() => {\n setDeleteSuccess(null);\n }, []);\n\n const allItems = data?.vibeAuditLogs.items ?? [];\n const items = allItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n const totalAuditItems = data?.vibeAuditLogs.total ?? 0;\n const visibleLogIds = items.map((item: (typeof items)[number]) => item.id);\n const allVisibleSelected =\n visibleLogIds.length > 0 && visibleLogIds.every((id: string) => selectedLogIds.has(id));\n const partiallySelected =\n visibleLogIds.some((id: string) => selectedLogIds.has(id)) && !allVisibleSelected;\n\n const handleDelete = async (reason: string) => {\n if (deleteTargetIds.length === 0) return;\n\n clearDeleteError();\n clearDeleteSuccess();\n\n try {\n if (deleteTargetIds.length === 1) {\n await deleteAuditLog({ variables: { id: deleteTargetIds[0], input: { reason } } });\n setDeleteSuccess('Deleted 1 audit log');\n } else {\n const result = await bulkDeleteAuditLogs({\n variables: { ids: deleteTargetIds, input: { reason } },\n });\n const deletedCount = result.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0;\n const failedCount = result.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;\n setDeleteSuccess(\n `Deleted ${deletedCount} audit logs${failedCount > 0 ? ` • ${failedCount} failed` : ''}`\n );\n }\n\n setDeleteTargetIds([]);\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n deleteTargetIds.forEach((id) => next.delete(id));\n return next;\n });\n if (selectedLogId && deleteTargetIds.includes(selectedLogId)) {\n setSelectedLogId(null);\n }\n await refetch();\n if (isNative()) void nativeNotify('success');\n } catch (deleteAuditError) {\n console.error('Failed to delete audit log:', deleteAuditError);\n setDeleteError(tr('audit.deleteFailed', 'Failed to delete audit log. Please try again.'));\n if (isNative()) void nativeNotify('error');\n deleteErrorTimer.current = setTimeout(() => {\n setDeleteError(null);\n deleteErrorTimer.current = null;\n }, 30000);\n }\n };\n\n const handleOpenDelete = useCallback(\n (id: string) => {\n if (!canDeleteAudit) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds([id]);\n },\n [canDeleteAudit, clearDeleteError, clearDeleteSuccess]\n );\n\n const handleToggleLogSelection = useCallback((id: string, checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n }, []);\n\n const handleToggleAllVisible = useCallback(\n (checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n visibleLogIds.forEach((id: string) => {\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n return next;\n });\n },\n [visibleLogIds]\n );\n\n const handleOpenBulkDelete = useCallback(() => {\n if (!canDeleteAudit || selectedLogIds.size === 0) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds(Array.from(selectedLogIds));\n }, [canDeleteAudit, clearDeleteError, clearDeleteSuccess, selectedLogIds]);\n\n if (permissionsLoading) {\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <LoadingState message={tr('audit.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canListAudit) {\n return (\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'common.accessDeniedDescription',\n \"You don't have permission to access this resource.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.vibeAudit', 'Audit')}\n description={tr(\n 'audit.description',\n 'Comprehensive audit trail of all VibeControls operations'\n )}\n actions={canExportAudit ? <AuditExportButton filter={filter} /> : undefined}\n />\n\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n\n <AuditLogFilters\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n actionFilter={actionFilter}\n onActionChange={(value) => {\n setActionFilter(value);\n setPage(0);\n }}\n resourceTypeFilter={resourceTypeFilter}\n onResourceTypeChange={(value) => {\n setResourceTypeFilter(value);\n setPage(0);\n }}\n statusFilter={statusFilter}\n onStatusChange={(value) => {\n setStatusFilter(value);\n setPage(0);\n }}\n hasCommandFilter={hasCommandFilter}\n onHasCommandChange={(value) => {\n setHasCommandFilter(value);\n setPage(0);\n }}\n />\n\n {items.length > 0 ? (\n <div className=\"rounded-lg border border-border-primary bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedLogIds.size} selected\n </span>\n {selectedLogIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedLogIds(new Set())}\n className=\"text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n Clear selection\n </button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleOpenBulkDelete}\n disabled={!canDeleteAudit || selectedLogIds.size === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n Delete Selected{selectedLogIds.size > 0 ? ` (${selectedLogIds.size})` : ''}\n </button>\n </div>\n </div>\n </div>\n ) : null}\n\n {loading && !data ? (\n <LoadingState message={tr('audit.loadingLogs', 'Loading audit logs...')} />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => refetch()} />\n ) : totalAuditItems === 0 ? (\n <EmptyState\n icon={<Shield size={48} className=\"text-text-secondary\" />}\n title={tr('audit.noLogs', 'No audit logs')}\n description={tr(\n 'audit.emptyDescription',\n 'Audit logs will appear here as operations are performed in VibeControls.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Audit Log in VibeControls and how do I use it?\" />\n }\n />\n ) : (\n <AuditLogTable\n items={items}\n total={totalAuditItems}\n hasMore={data?.vibeAuditLogs.hasMore ?? false}\n page={page}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n onSelectLog={setSelectedLogId}\n onDeleteLog={handleOpenDelete}\n selectedLogId={selectedLogId}\n selectedLogIds={selectedLogIds}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n onToggleLogSelection={handleToggleLogSelection}\n onToggleAllVisible={handleToggleAllVisible}\n />\n )}\n\n {selectedLogId && detailData?.vibeAuditLog ? (\n <AuditLogDetail log={detailData.vibeAuditLog} onClose={() => setSelectedLogId(null)} />\n ) : null}\n\n {deleteError ? (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text shadow-lg\"\n >\n {deleteError}\n <button\n type=\"button\"\n onClick={clearDeleteError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n {deleteSuccess ? (\n <div\n role=\"status\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-success-bg px-4 py-3 text-sm text-status-success-text shadow-lg\"\n >\n {deleteSuccess}\n <button\n type=\"button\"\n onClick={clearDeleteSuccess}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n <AuditDeleteDialog\n isOpen={deleteTargetIds.length > 0}\n onClose={() => setDeleteTargetIds([])}\n onConfirm={handleDelete}\n loading={deleteLoading}\n title={deleteTargetIds.length > 1 ? 'Delete Audit Logs' : 'Delete Audit Log'}\n description={\n deleteTargetIds.length > 1\n ? `This will permanently delete ${deleteTargetIds.length} audit logs and record the reason in the audit trail.`\n : 'This action will be recorded in the audit trail.'\n }\n confirmLabel={deleteTargetIds.length > 1 ? 'Delete Logs' : 'Delete'}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeDeckPage.js","names":[],"sources":["../../src/pages/VibeDeckPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo, useRef, useEffect } from 'react';\nimport { Copy, Eye, Pencil, Play, RefreshCw, Share2, Star, Trash2 } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { AnimatePresence } from 'framer-motion';\nimport {\n PageHeader,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport {\n VibeDeckGrid,\n VibeDeckSwitcher,\n VibeDeckForm,\n VibeDeckButtonForm,\n VibeDeckShareDialog,\n VibeDeckExecutionLog,\n} from '@/components/vibedeck';\nimport type { ExecutionLogEntry } from '@/components/vibedeck';\nimport {\n useListVibeDecksQuery,\n useGetVibeDeckQuery,\n useListAgentsQuery,\n useCreateVibeDeckMutation,\n useUpdateVibeDeckMutation,\n useDeleteVibeDeckMutation,\n useSetDefaultVibeDeckMutation,\n useDuplicateVibeDeckMutation,\n useCreateVibeDeckButtonMutation,\n useUpdateVibeDeckButtonMutation,\n useDeleteVibeDeckButtonMutation,\n useMoveVibeDeckButtonMutation,\n useExecuteVibeDeckButtonMutation,\n} from '@/generated/wspace-operations';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\n\ntype ModalState =\n | { type: 'none' }\n | { type: 'createDeck' }\n | { type: 'editDeck'; deckId: string }\n | { type: 'createButton'; row: number; column: number }\n | { type: 'editButton'; buttonId: string }\n | { type: 'confirmDelete'; target: 'deck' | 'button'; id: string; name: string }\n | { type: 'confirmExecute'; buttonId: string; label: string; detail: string }\n | { type: 'duplicateDeck'; deckId: string }\n | { type: 'shareDeck'; deckId: string; deckName: string };\n\ninterface ErrorToast {\n message: string;\n variant: 'success' | 'error';\n detail?: string | null;\n id: number;\n}\n\nexport function VibeDeckPage() {\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const tr = (\n (i18n) =>\n (key: string, fallback: string): string => {\n const v = i18n.t(key);\n return v === key ? fallback : v;\n }\n )(useI18n());\n\n const deckPermsRegistered = (\n Array.isArray(permissions) ? permissions : permissions?.permissionStrings\n )?.some?.((p: string) => p.startsWith('vibedeck:'));\n const canList = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_LIST) : true;\n const canCreate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_CREATE) : true;\n const canUpdate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_UPDATE) : true;\n const canDelete = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_DELETE) : true;\n const canExecute = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_EXECUTE) : true;\n const shouldSkipQueries = permissionsLoading;\n\n const { activeVibeDeckId, setActiveVibeDeckId, vibeDeckMode, setVibeDeckMode } =\n useVibeControlsStore();\n const isPreview = vibeDeckMode === 'preview';\n const effectiveCanCreate = isPreview ? false : canCreate;\n const effectiveCanUpdate = isPreview ? false : canUpdate;\n const effectiveCanDelete = isPreview ? false : canDelete;\n\n const [modal, setModal] = useState<ModalState>({ type: 'none' });\n const [executingButtonId, setExecutingButtonId] = useState<string | null>(null);\n const [selectedDeckIds, setSelectedDeckIds] = useState<Set<string>>(new Set());\n const [selectedButtonIds, setSelectedButtonIds] = useState<Set<string>>(new Set());\n const [bulkDeckAction, setBulkDeckAction] = useState<'delete' | null>(null);\n const [bulkButtonAction, setBulkButtonAction] = useState<'execute' | 'delete' | null>(null);\n const [bulkBusy, setBulkBusy] = useState<\n 'duplicateDeck' | 'deleteDeck' | 'setDefaultDeck' | 'executeButton' | 'deleteButton' | null\n >(null);\n const [toasts, setToasts] = useState<ErrorToast[]>([]);\n const toastTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n // Execution log state\n const [executionLog, setExecutionLog] = useState<ExecutionLogEntry[]>([]);\n const [executionLogExpanded, setExecutionLogExpanded] = useState(true);\n const [buttonStatuses, setButtonStatuses] = useState<\n Map<string, 'idle' | 'executing' | 'success' | 'error'>\n >(new Map());\n const [quotaDialog, setQuotaDialog] = useState<{ title: string; message: string } | null>(null);\n const statusTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = statusTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const addLogEntry = useCallback((entry: Omit<ExecutionLogEntry, 'id'>) => {\n const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const newEntry = { ...entry, id };\n setExecutionLog((prev) => [...prev.slice(-49), newEntry]);\n return id;\n }, []);\n\n const updateLogEntry = useCallback((id: string, updates: Partial<ExecutionLogEntry>) => {\n setExecutionLog((prev) =>\n prev.map((entry) => (entry.id === id ? { ...entry, ...updates } : entry))\n );\n }, []);\n\n const setButtonStatus = useCallback(\n (buttonId: string, status: 'idle' | 'executing' | 'success' | 'error') => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n next.set(buttonId, status);\n return next;\n });\n if (status === 'success' || status === 'error') {\n const timer = setTimeout(() => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n if (next.get(buttonId) === status) next.delete(buttonId);\n return next;\n });\n statusTimers.current.delete(buttonId);\n }, 3000);\n const existing = statusTimers.current.get(buttonId);\n if (existing) clearTimeout(existing);\n statusTimers.current.set(buttonId, timer);\n }\n },\n []\n );\n\n useEffect(() => {\n const timers = toastTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const showToast = useCallback(\n (message: string, variant: 'success' | 'error', detail?: string | null, duration?: number) => {\n const id = Date.now() + Math.random();\n setToasts((prev) => [...prev, { message, variant, detail, id }]);\n const autoDismiss = duration ?? (variant === 'success' ? 10000 : 30000);\n const timer = setTimeout(() => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n toastTimers.current.delete(id);\n }, autoDismiss);\n toastTimers.current.set(id, timer);\n },\n []\n );\n\n const dismissToast = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n const timer = toastTimers.current.get(id);\n if (timer) {\n clearTimeout(timer);\n toastTimers.current.delete(id);\n }\n }, []);\n\n const showQuotaDialog = useCallback((title: string, message: string) => {\n // Coordinate with the global QuotaExhaustedProvider so it skips its\n // own dialog when this page renders a local one (BOFF-2625 review).\n markQuotaHandledLocally();\n setQuotaDialog({ title, message });\n }, []);\n\n const {\n data: decksData,\n loading: decksLoading,\n error: decksError,\n refetch: refetchDecks,\n } = useListVibeDecksQuery({\n variables: { pagination: { limit: 50 } },\n skip: shouldSkipQueries,\n });\n\n const decks = useMemo(() => decksData?.vibeDecks?.items || [], [decksData]);\n\n const effectiveActiveDeckId =\n activeVibeDeckId ||\n decks.find((d: { isDefault: boolean }) => d.isDefault)?.id ||\n decks[0]?.id ||\n null;\n\n const {\n data: deckData,\n loading: deckLoading,\n refetch: refetchDeck,\n } = useGetVibeDeckQuery({\n variables: { id: effectiveActiveDeckId! },\n skip: !effectiveActiveDeckId || shouldSkipQueries,\n });\n\n const activeDeck = deckData?.vibeDeck;\n\n const selectedDecks = useMemo(\n () => decks.filter((deck: { id: string }) => selectedDeckIds.has(deck.id)),\n [decks, selectedDeckIds]\n );\n\n const selectedButtons = useMemo(\n () =>\n activeDeck?.buttons?.filter((button: { id: string }) => selectedButtonIds.has(button.id)) ??\n [],\n [activeDeck?.buttons, selectedButtonIds]\n );\n\n const allDecksSelected =\n decks.length > 0 && decks.every((deck: { id: string }) => selectedDeckIds.has(deck.id));\n const partiallySelectedDecks =\n decks.some((deck: { id: string }) => selectedDeckIds.has(deck.id)) && !allDecksSelected;\n const allButtonsSelected =\n (activeDeck?.buttons?.length ?? 0) > 0 &&\n (activeDeck?.buttons ?? []).every((button: { id: string }) => selectedButtonIds.has(button.id));\n const partiallySelectedButtons =\n (activeDeck?.buttons ?? []).some((button: { id: string }) =>\n selectedButtonIds.has(button.id)\n ) && !allButtonsSelected;\n\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n const agents = (agentsData?.vibecontrolsAgents ?? []).map(\n (a: { id: string; name: string; isActive: boolean }) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n })\n );\n\n const [createDeck, { loading: createDeckLoading }] = useCreateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const { setDraftTagIds: setDeckDraftTagIds, flushTags: flushDeckTags } =\n useDraftEntityTags('VIBECONTROLS_VIBEDECK');\n const [updateDeck, { loading: updateDeckLoading }] = useUpdateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [deleteDeck] = useDeleteVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [setDefaultDeck] = useSetDefaultVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [duplicateDeck] = useDuplicateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [createButton, { loading: createButtonLoading }] = useCreateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [updateButton, { loading: updateButtonLoading }] = useUpdateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [deleteButton] = useDeleteVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [moveButton] = useMoveVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [executeButton] = useExecuteVibeDeckButtonMutation();\n\n useEffect(() => {\n setSelectedButtonIds(new Set());\n }, [effectiveActiveDeckId]);\n\n const toggleDeckSelection = useCallback((deckId: string, checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(deckId);\n } else {\n next.delete(deckId);\n }\n return next;\n });\n }, []);\n\n const toggleAllDecks = useCallback(\n (checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n decks.forEach((deck: { id: string }) => {\n if (checked) {\n next.add(deck.id);\n } else {\n next.delete(deck.id);\n }\n });\n return next;\n });\n },\n [decks]\n );\n\n const clearDeckSelection = useCallback(() => {\n setSelectedDeckIds(new Set());\n }, []);\n\n const toggleButtonSelection = useCallback((buttonId: string, checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(buttonId);\n } else {\n next.delete(buttonId);\n }\n return next;\n });\n }, []);\n\n const toggleAllButtons = useCallback(\n (checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n (activeDeck?.buttons ?? []).forEach((button: { id: string }) => {\n if (checked) {\n next.add(button.id);\n } else {\n next.delete(button.id);\n }\n });\n return next;\n });\n },\n [activeDeck?.buttons]\n );\n\n const clearButtonSelection = useCallback(() => {\n setSelectedButtonIds(new Set());\n }, []);\n\n const handleCreateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n try {\n const res = await createDeck({\n variables: {\n input: {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n },\n },\n });\n const newDeckId = res.data?.createVibeDeck?.id;\n setModal({ type: 'none' });\n showToast('Deck created', 'success');\n if (newDeckId) await flushDeckTags(newDeckId);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setModal({ type: 'none' });\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to create more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to create deck', 'error');\n }\n },\n [createDeck, flushDeckTags, showQuotaDialog, showToast]\n );\n\n const handleUpdateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n if (modal.type !== 'editDeck') return;\n try {\n await updateDeck({\n variables: {\n id: modal.deckId,\n input: {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Deck updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update deck', 'error');\n }\n },\n [updateDeck, modal, showToast]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (modal.type !== 'confirmDelete') return;\n try {\n if (modal.target === 'deck') {\n await deleteDeck({ variables: { id: modal.id } });\n if (activeVibeDeckId === modal.id) {\n setActiveVibeDeckId(null);\n }\n showToast('Deck deleted', 'success');\n } else {\n await deleteButton({ variables: { id: modal.id } });\n showToast('Button deleted', 'success');\n }\n setModal({ type: 'none' });\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete', 'error');\n if (isNative()) void nativeNotify('error');\n }\n }, [modal, deleteDeck, deleteButton, activeVibeDeckId, setActiveVibeDeckId, showToast]);\n\n const doExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button || !button.action) return;\n\n const action = button.action;\n\n // Add log entry\n const logId = addLogEntry({\n buttonId,\n buttonLabel: button.label,\n actionType: action.actionType as any,\n command:\n action.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action.command || undefined,\n status: 'running',\n startedAt: new Date(),\n });\n\n setExecutingButtonId(buttonId);\n setButtonStatus(buttonId, 'executing');\n\n try {\n const result = await executeButton({ variables: { id: buttonId } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec) {\n const isSuccess = exec.status === 'success';\n setButtonStatus(buttonId, isSuccess ? 'success' : 'error');\n updateLogEntry(logId, {\n status: isSuccess ? 'success' : 'error',\n output: exec.output,\n exitCode: exec.exitCode,\n webhookStatusCode: exec.webhookStatusCode,\n completedAt: new Date(),\n });\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: 'Usage quota exhausted',\n completedAt: new Date(),\n });\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n const errorMsg = err instanceof Error ? err.message : 'Execution failed';\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: errorMsg,\n completedAt: new Date(),\n });\n } finally {\n setExecutingButtonId(null);\n }\n },\n [activeDeck, executeButton, addLogEntry, updateLogEntry, setButtonStatus, showQuotaDialog]\n );\n\n const handleExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button) return;\n\n if (button.confirmationRequired) {\n const action = button.action;\n const detail =\n action?.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action?.command || '';\n setModal({ type: 'confirmExecute', buttonId, label: button.label, detail });\n return;\n }\n\n await doExecuteButton(buttonId);\n },\n [activeDeck, doExecuteButton]\n );\n\n const handleConfirmExecute = useCallback(async () => {\n if (modal.type !== 'confirmExecute') return;\n const buttonId = modal.buttonId;\n setModal({ type: 'none' });\n await doExecuteButton(buttonId);\n }, [modal, doExecuteButton]);\n\n const handleCreateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'createButton' || !effectiveActiveDeckId) return;\n\n try {\n await createButton({\n variables: {\n input: {\n vibeDeckId: effectiveActiveDeckId,\n label: data.label,\n actionId: data.actionId,\n row: modal.row,\n column: modal.column,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button created', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to create button', 'error');\n }\n },\n [modal, createButton, effectiveActiveDeckId, showToast]\n );\n\n const handleUpdateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'editButton') return;\n\n try {\n await updateButton({\n variables: {\n id: modal.buttonId,\n input: {\n label: data.label,\n actionId: data.actionId,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update button', 'error');\n }\n },\n [modal, updateButton, showToast]\n );\n\n const handleDropButton = useCallback(\n async (buttonId: string, targetRow: number, targetColumn: number) => {\n try {\n await moveButton({\n variables: {\n id: buttonId,\n input: { row: targetRow, column: targetColumn },\n },\n });\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to move button', 'error');\n }\n },\n [moveButton, showToast]\n );\n\n const handleDuplicateDeck = useCallback(\n async (deckId: string) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === deckId);\n if (!deck) return;\n try {\n const newName = `${deck.name} (Copy)`;\n await duplicateDeck({ variables: { id: deckId, name: newName } });\n showToast('Deck duplicated', 'success');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate deck', 'error');\n }\n },\n [decks, duplicateDeck, showQuotaDialog, showToast]\n );\n\n const handleSetDefault = useCallback(\n async (id: string) => {\n try {\n await setDefaultDeck({ variables: { id } });\n showToast('Default deck set', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default', 'error');\n }\n },\n [setDefaultDeck, showToast]\n );\n\n const handleSetSelectedDefault = useCallback(async () => {\n if (selectedDecks.length !== 1) return;\n setBulkBusy('setDefaultDeck');\n try {\n await setDefaultDeck({ variables: { id: selectedDecks[0].id } });\n showToast('Default deck set', 'success');\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default deck', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, setDefaultDeck, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDuplicateDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('duplicateDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string; name: string }) =>\n duplicateDeck({ variables: { id: deck.id, name: `${deck.name} (Copy)` } })\n )\n );\n\n const quotaRejected = results.some(\n (result: PromiseSettledResult<unknown>): result is PromiseRejectedResult =>\n result.status === 'rejected' && isQuotaExhaustedError(result.reason)\n );\n\n if (quotaRejected) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Duplicated ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, duplicateDeck, showQuotaDialog, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDeleteDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('deleteDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string }) => deleteDeck({ variables: { id: deck.id } }))\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n if (selectedDecks.some((deck: { id: string }) => deck.id === activeVibeDeckId)) {\n setActiveVibeDeckId(null);\n }\n showToast(\n `Deleted ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n setBulkDeckAction(null);\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [\n selectedDecks,\n deleteDeck,\n activeVibeDeckId,\n setActiveVibeDeckId,\n showToast,\n clearDeckSelection,\n refetchDecks,\n ]);\n\n const handleBulkExecuteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('executeButton');\n let succeeded = 0;\n let failed = 0;\n try {\n for (const button of selectedButtons) {\n try {\n const result = await executeButton({ variables: { id: button.id } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec?.status === 'success') {\n succeeded += 1;\n } else {\n failed += 1;\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setBulkButtonAction(null);\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n failed += 1;\n }\n }\n showToast(\n `Executed ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to execute buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, executeButton, showQuotaDialog, showToast, clearButtonSelection]);\n\n const handleBulkDeleteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('deleteButton');\n try {\n const results = await Promise.allSettled(\n selectedButtons.map((button: { id: string }) =>\n deleteButton({ variables: { id: button.id } })\n )\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Deleted ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n await refetchDeck();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, deleteButton, showToast, clearButtonSelection, refetchDeck]);\n\n if (permissionsLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading permissions...\" />\n </MotionPage>\n );\n }\n\n if (\n permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !canList\n ) {\n return (\n <MotionPage>\n <AccessDenied />\n </MotionPage>\n );\n }\n\n if (decksLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading Vibe Decks...\" />\n </MotionPage>\n );\n }\n\n if (decksError) {\n return (\n <MotionPage>\n <ErrorState message={decksError.message} onRetry={() => refetchDecks()} />\n </MotionPage>\n );\n }\n\n const editingButton =\n modal.type === 'editButton'\n ? activeDeck?.buttons?.find((b: { id: string }) => b.id === modal.buttonId)\n : null;\n const editingDeck =\n modal.type === 'editDeck' ? decks.find((d: { id: string }) => d.id === modal.deckId) : null;\n\n return (\n <MotionPage className=\"p-4 sm:p-6 space-y-6\">\n <PageHeader\n title={tr('nav.vibeDeck', 'Vibe Deck')}\n description=\"Your digital command board — execute commands or trigger webhooks with one click\"\n actions={\n <div className=\"flex items-center gap-2\">\n <div className=\"flex items-center rounded-lg border border-border-default bg-bg-secondary p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('preview')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title=\"Preview mode — execute only\"\n >\n <Eye size={14} />\n Preview\n </button>\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('edit')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n !isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title=\"Edit mode — full editing\"\n >\n <Pencil size={14} />\n Edit\n </button>\n </div>\n {effectiveActiveDeckId && activeDeck && (\n <button\n type=\"button\"\n onClick={() =>\n setModal({\n type: 'shareDeck',\n deckId: effectiveActiveDeckId,\n deckName: activeDeck.name,\n })\n }\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Share deck\"\n >\n <Share2 size={16} />\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => {\n refetchDecks();\n if (effectiveActiveDeckId) refetchDeck();\n }}\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw size={16} />\n </button>\n </div>\n }\n />\n\n {!isPreview && decks.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allDecksSelected}\n indeterminate={partiallySelectedDecks}\n onChange={toggleAllDecks}\n ariaLabel=\"Select all vibe decks\"\n />\n <span className=\"text-sm font-medium text-text-primary\">Select all decks</span>\n </div>\n {selectedDeckIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearDeckSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedDeckIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={handleSetSelectedDefault}\n disabled={\n selectedDecks.length !== 1 || selectedDecks[0]?.isDefault || bulkBusy !== null\n }\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Star size={14} />\n Set Selected Default\n </span>\n </button>\n <button\n type=\"button\"\n onClick={handleBulkDuplicateDecks}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Copy size={14} />\n Duplicate Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkDeckAction('delete')}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <VibeDeckSwitcher\n decks={decks.map((d: any) => ({\n id: d.id,\n name: d.name,\n icon: d.icon || null,\n colorTheme: d.colorTheme || null,\n isDefault: d.isDefault,\n }))}\n activeDeckId={effectiveActiveDeckId}\n selectedDeckIds={selectedDeckIds}\n onSelectDeck={(id) => setActiveVibeDeckId(id)}\n onToggleSelectDeck={toggleDeckSelection}\n onCreateDeck={() => setModal({ type: 'createDeck' })}\n onSetDefault={handleSetDefault}\n onDuplicate={(id) => handleDuplicateDeck(id)}\n onDelete={(id) => {\n const deck = decks.find((d: { id: string }) => d.id === id);\n setModal({ type: 'confirmDelete', target: 'deck', id, name: deck?.name || 'this deck' });\n }}\n onEdit={(id) => setModal({ type: 'editDeck', deckId: id })}\n onShare={(id) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === id);\n if (deck) setModal({ type: 'shareDeck', deckId: id, deckName: deck.name });\n }}\n // Create button is ungated by preview mode so users can always add a\n // new deck. Update/delete stay gated because they mutate selected decks.\n canCreate={canCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n />\n\n <AnimatePresence mode=\"wait\">\n {!activeDeck && !deckLoading && decks.length === 0 && (\n <EmptyState\n title=\"No Vibe Decks yet\"\n description=\"Create your first Vibe Deck to start organizing your commands and webhooks\"\n action={\n <div className=\"flex items-center gap-2\">\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setModal({ type: 'createDeck' })}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:opacity-90 text-sm font-medium\"\n >\n Create Vibe Deck\n </button>\n )}\n <GetHelpButton prompt=\"What is a VibeDeck and how do I use it to organise controls?\" />\n </div>\n }\n />\n )}\n\n {deckLoading && <LoadingState message=\"Loading deck...\" />}\n </AnimatePresence>\n\n {activeDeck && (\n <div className=\"space-y-4\">\n {!isPreview && (activeDeck.buttons?.length ?? 0) > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allButtonsSelected}\n indeterminate={partiallySelectedButtons}\n onChange={toggleAllButtons}\n ariaLabel=\"Select all buttons in active deck\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all buttons in this deck\n </span>\n </div>\n {selectedButtonIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearButtonSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedButtonIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('execute')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play size={14} />\n Execute Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('delete')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <VibeDeckGrid\n gridColumns={activeDeck.gridColumns}\n gridRows={activeDeck.gridRows}\n buttons={(activeDeck.buttons || []).map((b: any) => ({\n id: b.id,\n label: b.label,\n icon: b.icon || null,\n color: b.color || null,\n category: b.category || null,\n confirmationRequired: b.confirmationRequired,\n keyboardShortcut: b.keyboardShortcut || null,\n actionId: b.actionId || null,\n action: b.action\n ? {\n id: b.action.id,\n name: b.action.name,\n displayName: b.action.displayName || null,\n actionType: b.action.actionType,\n command: b.action.command || null,\n script: b.action.script || null,\n webhookUrl: b.action.webhookUrl || null,\n targetAgentId: b.action.targetAgentId || null,\n }\n : null,\n row: b.row,\n column: b.column,\n rowSpan: b.rowSpan,\n colSpan: b.colSpan,\n }))}\n onExecuteButton={handleExecuteButton}\n onEditButton={(id) => setModal({ type: 'editButton', buttonId: id })}\n onDeleteButton={(id) => {\n const btn = activeDeck.buttons?.find((b: { id: string }) => b.id === id);\n setModal({\n type: 'confirmDelete',\n target: 'button',\n id,\n name: btn?.label || 'this button',\n });\n }}\n onDropButton={handleDropButton}\n onAddButton={(row, column) => setModal({ type: 'createButton', row, column })}\n executingButtonId={executingButtonId}\n canCreate={effectiveCanCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n canExecute={canExecute}\n />\n </div>\n )}\n\n {executionLog.length > 0 && (\n <VibeDeckExecutionLog\n entries={executionLog}\n expanded={executionLogExpanded}\n onToggleExpanded={() => setExecutionLogExpanded((prev) => !prev)}\n onClear={() => setExecutionLog([])}\n />\n )}\n\n <AnimatePresence>\n {toasts.length > 0 && (\n <div className=\"fixed bottom-4 left-4 right-4 sm:left-auto sm:bottom-6 sm:right-6 z-50 flex flex-col gap-2 max-w-[calc(100vw-2rem)] sm:max-w-md\">\n {toasts.slice(-3).map((toast) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => dismissToast(toast.id))();\n }\n }}\n key={toast.id}\n className={`p-4 rounded-xl shadow-lg border cursor-pointer ${\n toast.variant === 'success'\n ? 'bg-status-success-bg/10 border-status-success-border/30 text-status-success-text'\n : 'bg-status-error-bg/10 border-status-error-border/30 text-status-error-text'\n }`}\n onClick={() => dismissToast(toast.id)}\n >\n <div className=\"font-medium text-sm\">{toast.message}</div>\n {toast.detail && (\n <pre className=\"text-xs font-mono opacity-80 mt-1 max-h-20 overflow-y-auto whitespace-pre-wrap\">\n {toast.detail}\n </pre>\n )}\n </div>\n ))}\n </div>\n )}\n </AnimatePresence>\n\n {modal.type === 'createDeck' && (\n <VibeDeckForm\n title=\"Create Vibe Deck\"\n onSubmit={handleCreateDeck}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createDeckLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker\n entityType=\"VIBECONTROLS_VIBEDECK\"\n onDraftChange={setDeckDraftTagIds}\n />\n </div>\n }\n />\n )}\n\n {modal.type === 'editDeck' && editingDeck && (\n <VibeDeckForm\n title=\"Edit Vibe Deck\"\n initialData={{\n name: editingDeck.name,\n description: editingDeck.description || '',\n colorTheme: editingDeck.colorTheme || '',\n gridColumns: editingDeck.gridColumns,\n gridRows: editingDeck.gridRows,\n }}\n onSubmit={handleUpdateDeck}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateDeckLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBEDECK\" entityId={editingDeck.id} />\n </div>\n }\n />\n )}\n\n {modal.type === 'createButton' && (\n <VibeDeckButtonForm\n title=\"Add Button\"\n agents={agents}\n onSubmit={handleCreateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={modal.row}\n buttonColumn={modal.column}\n />\n )}\n\n {modal.type === 'editButton' && editingButton && (\n <VibeDeckButtonForm\n title=\"Edit Button\"\n initialData={\n {\n label: editingButton.label,\n actionId: (editingButton as any).actionId || '',\n color: editingButton.color || '',\n category: editingButton.category || '',\n keyboardShortcut: editingButton.keyboardShortcut || '',\n confirmationRequired: editingButton.confirmationRequired,\n rowSpan: editingButton.rowSpan,\n colSpan: editingButton.colSpan,\n } as any\n }\n agents={agents}\n onSubmit={handleUpdateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={editingButton.row}\n buttonColumn={editingButton.column}\n />\n )}\n\n {modal.type === 'confirmDelete' && (\n <ConfirmDialog\n isOpen={true}\n title={`Delete ${modal.target === 'deck' ? 'Vibe Deck' : 'Button'}`}\n message={`Are you sure you want to delete \"${modal.name}\"?${modal.target === 'deck' ? ' All buttons in this deck will also be deleted.' : ''}`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDeleteConfirm}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n {modal.type === 'confirmExecute' && (\n <ConfirmDialog\n isOpen={true}\n title={`Execute: ${modal.label}`}\n message={`Are you sure you want to execute this action?\\n\\n${modal.detail}`}\n confirmLabel=\"Execute\"\n variant=\"warning\"\n onConfirm={handleConfirmExecute}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <ConfirmDialog\n isOpen={bulkDeckAction === 'delete'}\n title=\"Delete Vibe Decks\"\n message={`Are you sure you want to delete ${selectedDecks.length} selected deck${selectedDecks.length === 1 ? '' : 's'}? All buttons in those decks will also be deleted.`}\n confirmLabel=\"Delete Decks\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteDecks}\n onClose={() => setBulkDeckAction(null)}\n isLoading={bulkBusy === 'deleteDeck'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'execute'}\n title=\"Execute Vibe Deck Buttons\"\n message={`Execute ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This may trigger commands, scripts, or webhooks immediately.`}\n confirmLabel=\"Execute Buttons\"\n variant=\"warning\"\n onConfirm={handleBulkExecuteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'executeButton'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'delete'}\n title=\"Delete Vibe Deck Buttons\"\n message={`Delete ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel=\"Delete Buttons\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'deleteButton'}\n />\n\n {modal.type === 'shareDeck' && (\n <VibeDeckShareDialog\n vibeDeckId={modal.deckId}\n vibeDeckName={modal.deckName}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={quotaDialog !== null}\n onClose={() => setQuotaDialog(null)}\n onConfirm={() => {\n setQuotaDialog(null);\n navigate('/billing');\n }}\n title={quotaDialog?.title || 'Quota Reached'}\n message={quotaDialog?.message || 'Upgrade your plan to continue.'}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeDeckPage.js","names":[],"sources":["../../src/pages/VibeDeckPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo, useRef, useEffect } from 'react';\nimport { Copy, Eye, Pencil, Play, RefreshCw, Share2, Star, Trash2 } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { AnimatePresence } from 'framer-motion';\nimport {\n PageHeader,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport {\n VibeDeckGrid,\n VibeDeckSwitcher,\n VibeDeckForm,\n VibeDeckButtonForm,\n VibeDeckShareDialog,\n VibeDeckExecutionLog,\n} from '@/components/vibedeck';\nimport type { ExecutionLogEntry } from '@/components/vibedeck';\nimport {\n useListVibeDecksQuery,\n useGetVibeDeckQuery,\n useListAgentsQuery,\n useCreateVibeDeckMutation,\n useUpdateVibeDeckMutation,\n useDeleteVibeDeckMutation,\n useSetDefaultVibeDeckMutation,\n useDuplicateVibeDeckMutation,\n useCreateVibeDeckButtonMutation,\n useUpdateVibeDeckButtonMutation,\n useDeleteVibeDeckButtonMutation,\n useMoveVibeDeckButtonMutation,\n useExecuteVibeDeckButtonMutation,\n} from '@/generated/wspace-operations';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\n\ntype ModalState =\n | { type: 'none' }\n | { type: 'createDeck' }\n | { type: 'editDeck'; deckId: string }\n | { type: 'createButton'; row: number; column: number }\n | { type: 'editButton'; buttonId: string }\n | { type: 'confirmDelete'; target: 'deck' | 'button'; id: string; name: string }\n | { type: 'confirmExecute'; buttonId: string; label: string; detail: string }\n | { type: 'duplicateDeck'; deckId: string }\n | { type: 'shareDeck'; deckId: string; deckName: string };\n\ninterface ErrorToast {\n message: string;\n variant: 'success' | 'error';\n detail?: string | null;\n id: number;\n}\n\nexport function VibeDeckPage() {\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const tr = (\n (i18n) =>\n (key: string, fallback: string): string => {\n const v = i18n.t(key);\n return v === key ? fallback : v;\n }\n )(useI18n());\n\n const deckPermsRegistered = (\n Array.isArray(permissions) ? permissions : permissions?.permissionStrings\n )?.some?.((p: string) => p.startsWith('vibedeck:'));\n const canList = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_LIST) : true;\n const canCreate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_CREATE) : true;\n const canUpdate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_UPDATE) : true;\n const canDelete = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_DELETE) : true;\n const canExecute = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_EXECUTE) : true;\n const shouldSkipQueries = permissionsLoading;\n\n const { activeVibeDeckId, setActiveVibeDeckId, vibeDeckMode, setVibeDeckMode } =\n useVibeControlsStore();\n const isPreview = vibeDeckMode === 'preview';\n const effectiveCanCreate = isPreview ? false : canCreate;\n const effectiveCanUpdate = isPreview ? false : canUpdate;\n const effectiveCanDelete = isPreview ? false : canDelete;\n\n const [modal, setModal] = useState<ModalState>({ type: 'none' });\n const [executingButtonId, setExecutingButtonId] = useState<string | null>(null);\n const [selectedDeckIds, setSelectedDeckIds] = useState<Set<string>>(new Set());\n const [selectedButtonIds, setSelectedButtonIds] = useState<Set<string>>(new Set());\n const [bulkDeckAction, setBulkDeckAction] = useState<'delete' | null>(null);\n const [bulkButtonAction, setBulkButtonAction] = useState<'execute' | 'delete' | null>(null);\n const [bulkBusy, setBulkBusy] = useState<\n 'duplicateDeck' | 'deleteDeck' | 'setDefaultDeck' | 'executeButton' | 'deleteButton' | null\n >(null);\n const [toasts, setToasts] = useState<ErrorToast[]>([]);\n const toastTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n // Execution log state\n const [executionLog, setExecutionLog] = useState<ExecutionLogEntry[]>([]);\n const [executionLogExpanded, setExecutionLogExpanded] = useState(true);\n const [buttonStatuses, setButtonStatuses] = useState<\n Map<string, 'idle' | 'executing' | 'success' | 'error'>\n >(new Map());\n const [quotaDialog, setQuotaDialog] = useState<{ title: string; message: string } | null>(null);\n const statusTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = statusTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const addLogEntry = useCallback((entry: Omit<ExecutionLogEntry, 'id'>) => {\n const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const newEntry = { ...entry, id };\n setExecutionLog((prev) => [...prev.slice(-49), newEntry]);\n return id;\n }, []);\n\n const updateLogEntry = useCallback((id: string, updates: Partial<ExecutionLogEntry>) => {\n setExecutionLog((prev) =>\n prev.map((entry) => (entry.id === id ? { ...entry, ...updates } : entry))\n );\n }, []);\n\n const setButtonStatus = useCallback(\n (buttonId: string, status: 'idle' | 'executing' | 'success' | 'error') => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n next.set(buttonId, status);\n return next;\n });\n if (status === 'success' || status === 'error') {\n const timer = setTimeout(() => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n if (next.get(buttonId) === status) next.delete(buttonId);\n return next;\n });\n statusTimers.current.delete(buttonId);\n }, 3000);\n const existing = statusTimers.current.get(buttonId);\n if (existing) clearTimeout(existing);\n statusTimers.current.set(buttonId, timer);\n }\n },\n []\n );\n\n useEffect(() => {\n const timers = toastTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const showToast = useCallback(\n (message: string, variant: 'success' | 'error', detail?: string | null, duration?: number) => {\n const id = Date.now() + Math.random();\n setToasts((prev) => [...prev, { message, variant, detail, id }]);\n const autoDismiss = duration ?? (variant === 'success' ? 10000 : 30000);\n const timer = setTimeout(() => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n toastTimers.current.delete(id);\n }, autoDismiss);\n toastTimers.current.set(id, timer);\n },\n []\n );\n\n const dismissToast = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n const timer = toastTimers.current.get(id);\n if (timer) {\n clearTimeout(timer);\n toastTimers.current.delete(id);\n }\n }, []);\n\n const showQuotaDialog = useCallback((title: string, message: string) => {\n // Coordinate with the global QuotaExhaustedProvider so it skips its\n // own dialog when this page renders a local one (BOFF-2625 review).\n markQuotaHandledLocally();\n setQuotaDialog({ title, message });\n }, []);\n\n const {\n data: decksData,\n loading: decksLoading,\n error: decksError,\n refetch: refetchDecks,\n } = useListVibeDecksQuery({\n variables: { pagination: { limit: 50 } },\n skip: shouldSkipQueries,\n });\n\n const decks = useMemo(() => decksData?.vibeDecks?.items || [], [decksData]);\n\n const effectiveActiveDeckId =\n activeVibeDeckId ||\n decks.find((d: { isDefault: boolean }) => d.isDefault)?.id ||\n decks[0]?.id ||\n null;\n\n const {\n data: deckData,\n loading: deckLoading,\n refetch: refetchDeck,\n } = useGetVibeDeckQuery({\n variables: { id: effectiveActiveDeckId! },\n skip: !effectiveActiveDeckId || shouldSkipQueries,\n });\n\n const activeDeck = deckData?.vibeDeck;\n\n const selectedDecks = useMemo(\n () => decks.filter((deck: { id: string }) => selectedDeckIds.has(deck.id)),\n [decks, selectedDeckIds]\n );\n\n const selectedButtons = useMemo(\n () =>\n activeDeck?.buttons?.filter((button: { id: string }) => selectedButtonIds.has(button.id)) ??\n [],\n [activeDeck?.buttons, selectedButtonIds]\n );\n\n const allDecksSelected =\n decks.length > 0 && decks.every((deck: { id: string }) => selectedDeckIds.has(deck.id));\n const partiallySelectedDecks =\n decks.some((deck: { id: string }) => selectedDeckIds.has(deck.id)) && !allDecksSelected;\n const allButtonsSelected =\n (activeDeck?.buttons?.length ?? 0) > 0 &&\n (activeDeck?.buttons ?? []).every((button: { id: string }) => selectedButtonIds.has(button.id));\n const partiallySelectedButtons =\n (activeDeck?.buttons ?? []).some((button: { id: string }) =>\n selectedButtonIds.has(button.id)\n ) && !allButtonsSelected;\n\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n const agents = (agentsData?.vibecontrolsAgents ?? []).map(\n (a: { id: string; name: string; isActive: boolean }) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n })\n );\n\n const [createDeck, { loading: createDeckLoading }] = useCreateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const { setDraftTagIds: setDeckDraftTagIds, flushTags: flushDeckTags } =\n useDraftEntityTags('VIBECONTROLS_VIBEDECK');\n const [updateDeck, { loading: updateDeckLoading }] = useUpdateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [deleteDeck] = useDeleteVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [setDefaultDeck] = useSetDefaultVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [duplicateDeck] = useDuplicateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [createButton, { loading: createButtonLoading }] = useCreateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [updateButton, { loading: updateButtonLoading }] = useUpdateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [deleteButton] = useDeleteVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [moveButton] = useMoveVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [executeButton] = useExecuteVibeDeckButtonMutation();\n\n useEffect(() => {\n setSelectedButtonIds(new Set());\n }, [effectiveActiveDeckId]);\n\n const toggleDeckSelection = useCallback((deckId: string, checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(deckId);\n } else {\n next.delete(deckId);\n }\n return next;\n });\n }, []);\n\n const toggleAllDecks = useCallback(\n (checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n decks.forEach((deck: { id: string }) => {\n if (checked) {\n next.add(deck.id);\n } else {\n next.delete(deck.id);\n }\n });\n return next;\n });\n },\n [decks]\n );\n\n const clearDeckSelection = useCallback(() => {\n setSelectedDeckIds(new Set());\n }, []);\n\n const toggleButtonSelection = useCallback((buttonId: string, checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(buttonId);\n } else {\n next.delete(buttonId);\n }\n return next;\n });\n }, []);\n\n const toggleAllButtons = useCallback(\n (checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n (activeDeck?.buttons ?? []).forEach((button: { id: string }) => {\n if (checked) {\n next.add(button.id);\n } else {\n next.delete(button.id);\n }\n });\n return next;\n });\n },\n [activeDeck?.buttons]\n );\n\n const clearButtonSelection = useCallback(() => {\n setSelectedButtonIds(new Set());\n }, []);\n\n const handleCreateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n try {\n const res = await createDeck({\n variables: {\n input: {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n },\n },\n });\n const newDeckId = res.data?.createVibeDeck?.id;\n setModal({ type: 'none' });\n showToast('Deck created', 'success');\n if (newDeckId) await flushDeckTags(newDeckId);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setModal({ type: 'none' });\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to create more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to create deck', 'error');\n }\n },\n [createDeck, flushDeckTags, showQuotaDialog, showToast]\n );\n\n const handleUpdateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n if (modal.type !== 'editDeck') return;\n try {\n await updateDeck({\n variables: {\n id: modal.deckId,\n input: {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Deck updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update deck', 'error');\n }\n },\n [updateDeck, modal, showToast]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (modal.type !== 'confirmDelete') return;\n try {\n if (modal.target === 'deck') {\n await deleteDeck({ variables: { id: modal.id } });\n if (activeVibeDeckId === modal.id) {\n setActiveVibeDeckId(null);\n }\n showToast('Deck deleted', 'success');\n } else {\n await deleteButton({ variables: { id: modal.id } });\n showToast('Button deleted', 'success');\n }\n setModal({ type: 'none' });\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete', 'error');\n if (isNative()) void nativeNotify('error');\n }\n }, [modal, deleteDeck, deleteButton, activeVibeDeckId, setActiveVibeDeckId, showToast]);\n\n const doExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button || !button.action) return;\n\n const action = button.action;\n\n // Add log entry\n const logId = addLogEntry({\n buttonId,\n buttonLabel: button.label,\n actionType: action.actionType as any,\n command:\n action.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action.command || undefined,\n status: 'running',\n startedAt: new Date(),\n });\n\n setExecutingButtonId(buttonId);\n setButtonStatus(buttonId, 'executing');\n\n try {\n const result = await executeButton({ variables: { id: buttonId } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec) {\n const isSuccess = exec.status === 'success';\n setButtonStatus(buttonId, isSuccess ? 'success' : 'error');\n updateLogEntry(logId, {\n status: isSuccess ? 'success' : 'error',\n output: exec.output,\n exitCode: exec.exitCode,\n webhookStatusCode: exec.webhookStatusCode,\n completedAt: new Date(),\n });\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: 'Usage quota exhausted',\n completedAt: new Date(),\n });\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n const errorMsg = err instanceof Error ? err.message : 'Execution failed';\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: errorMsg,\n completedAt: new Date(),\n });\n } finally {\n setExecutingButtonId(null);\n }\n },\n [activeDeck, executeButton, addLogEntry, updateLogEntry, setButtonStatus, showQuotaDialog]\n );\n\n const handleExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button) return;\n\n if (button.confirmationRequired) {\n const action = button.action;\n const detail =\n action?.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action?.command || '';\n setModal({ type: 'confirmExecute', buttonId, label: button.label, detail });\n return;\n }\n\n await doExecuteButton(buttonId);\n },\n [activeDeck, doExecuteButton]\n );\n\n const handleConfirmExecute = useCallback(async () => {\n if (modal.type !== 'confirmExecute') return;\n const buttonId = modal.buttonId;\n setModal({ type: 'none' });\n await doExecuteButton(buttonId);\n }, [modal, doExecuteButton]);\n\n const handleCreateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'createButton' || !effectiveActiveDeckId) return;\n\n try {\n await createButton({\n variables: {\n input: {\n vibeDeckId: effectiveActiveDeckId,\n label: data.label,\n actionId: data.actionId,\n row: modal.row,\n column: modal.column,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button created', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to create button', 'error');\n }\n },\n [modal, createButton, effectiveActiveDeckId, showToast]\n );\n\n const handleUpdateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'editButton') return;\n\n try {\n await updateButton({\n variables: {\n id: modal.buttonId,\n input: {\n label: data.label,\n actionId: data.actionId,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update button', 'error');\n }\n },\n [modal, updateButton, showToast]\n );\n\n const handleDropButton = useCallback(\n async (buttonId: string, targetRow: number, targetColumn: number) => {\n try {\n await moveButton({\n variables: {\n id: buttonId,\n input: { row: targetRow, column: targetColumn },\n },\n });\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to move button', 'error');\n }\n },\n [moveButton, showToast]\n );\n\n const handleDuplicateDeck = useCallback(\n async (deckId: string) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === deckId);\n if (!deck) return;\n try {\n const newName = `${deck.name} (Copy)`;\n await duplicateDeck({ variables: { id: deckId, name: newName } });\n showToast('Deck duplicated', 'success');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate deck', 'error');\n }\n },\n [decks, duplicateDeck, showQuotaDialog, showToast]\n );\n\n const handleSetDefault = useCallback(\n async (id: string) => {\n try {\n await setDefaultDeck({ variables: { id } });\n showToast('Default deck set', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default', 'error');\n }\n },\n [setDefaultDeck, showToast]\n );\n\n const handleSetSelectedDefault = useCallback(async () => {\n if (selectedDecks.length !== 1) return;\n setBulkBusy('setDefaultDeck');\n try {\n await setDefaultDeck({ variables: { id: selectedDecks[0].id } });\n showToast('Default deck set', 'success');\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default deck', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, setDefaultDeck, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDuplicateDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('duplicateDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string; name: string }) =>\n duplicateDeck({ variables: { id: deck.id, name: `${deck.name} (Copy)` } })\n )\n );\n\n const quotaRejected = results.some(\n (result: PromiseSettledResult<unknown>): result is PromiseRejectedResult =>\n result.status === 'rejected' && isQuotaExhaustedError(result.reason)\n );\n\n if (quotaRejected) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Duplicated ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, duplicateDeck, showQuotaDialog, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDeleteDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('deleteDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string }) => deleteDeck({ variables: { id: deck.id } }))\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n if (selectedDecks.some((deck: { id: string }) => deck.id === activeVibeDeckId)) {\n setActiveVibeDeckId(null);\n }\n showToast(\n `Deleted ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n setBulkDeckAction(null);\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [\n selectedDecks,\n deleteDeck,\n activeVibeDeckId,\n setActiveVibeDeckId,\n showToast,\n clearDeckSelection,\n refetchDecks,\n ]);\n\n const handleBulkExecuteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('executeButton');\n let succeeded = 0;\n let failed = 0;\n try {\n for (const button of selectedButtons) {\n try {\n const result = await executeButton({ variables: { id: button.id } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec?.status === 'success') {\n succeeded += 1;\n } else {\n failed += 1;\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setBulkButtonAction(null);\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n failed += 1;\n }\n }\n showToast(\n `Executed ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to execute buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, executeButton, showQuotaDialog, showToast, clearButtonSelection]);\n\n const handleBulkDeleteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('deleteButton');\n try {\n const results = await Promise.allSettled(\n selectedButtons.map((button: { id: string }) =>\n deleteButton({ variables: { id: button.id } })\n )\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Deleted ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n await refetchDeck();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, deleteButton, showToast, clearButtonSelection, refetchDeck]);\n\n if (permissionsLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading permissions...\" />\n </MotionPage>\n );\n }\n\n if (permissions && (permissions.permissionStrings?.length ?? 0) > 0 && !canList) {\n return (\n <MotionPage>\n <AccessDenied />\n </MotionPage>\n );\n }\n\n if (decksLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading Vibe Decks...\" />\n </MotionPage>\n );\n }\n\n if (decksError) {\n return (\n <MotionPage>\n <ErrorState message={decksError.message} onRetry={() => refetchDecks()} />\n </MotionPage>\n );\n }\n\n const editingButton =\n modal.type === 'editButton'\n ? activeDeck?.buttons?.find((b: { id: string }) => b.id === modal.buttonId)\n : null;\n const editingDeck =\n modal.type === 'editDeck' ? decks.find((d: { id: string }) => d.id === modal.deckId) : null;\n\n return (\n <MotionPage className=\"p-4 sm:p-6 space-y-6\">\n <PageHeader\n title={tr('nav.vibeDeck', 'Vibe Deck')}\n description=\"Your digital command board — execute commands or trigger webhooks with one click\"\n actions={\n <div className=\"flex items-center gap-2\">\n <div className=\"flex items-center rounded-lg border border-border-default bg-bg-secondary p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('preview')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title=\"Preview mode — execute only\"\n >\n <Eye size={14} />\n Preview\n </button>\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('edit')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n !isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title=\"Edit mode — full editing\"\n >\n <Pencil size={14} />\n Edit\n </button>\n </div>\n {effectiveActiveDeckId && activeDeck && (\n <button\n type=\"button\"\n onClick={() =>\n setModal({\n type: 'shareDeck',\n deckId: effectiveActiveDeckId,\n deckName: activeDeck.name,\n })\n }\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Share deck\"\n >\n <Share2 size={16} />\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => {\n refetchDecks();\n if (effectiveActiveDeckId) refetchDeck();\n }}\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw size={16} />\n </button>\n </div>\n }\n />\n\n {!isPreview && decks.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allDecksSelected}\n indeterminate={partiallySelectedDecks}\n onChange={toggleAllDecks}\n ariaLabel=\"Select all vibe decks\"\n />\n <span className=\"text-sm font-medium text-text-primary\">Select all decks</span>\n </div>\n {selectedDeckIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearDeckSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedDeckIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={handleSetSelectedDefault}\n disabled={\n selectedDecks.length !== 1 || selectedDecks[0]?.isDefault || bulkBusy !== null\n }\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Star size={14} />\n Set Selected Default\n </span>\n </button>\n <button\n type=\"button\"\n onClick={handleBulkDuplicateDecks}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Copy size={14} />\n Duplicate Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkDeckAction('delete')}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <VibeDeckSwitcher\n decks={decks.map((d: any) => ({\n id: d.id,\n name: d.name,\n icon: d.icon || null,\n colorTheme: d.colorTheme || null,\n isDefault: d.isDefault,\n }))}\n activeDeckId={effectiveActiveDeckId}\n selectedDeckIds={selectedDeckIds}\n onSelectDeck={(id) => setActiveVibeDeckId(id)}\n onToggleSelectDeck={toggleDeckSelection}\n onCreateDeck={() => setModal({ type: 'createDeck' })}\n onSetDefault={handleSetDefault}\n onDuplicate={(id) => handleDuplicateDeck(id)}\n onDelete={(id) => {\n const deck = decks.find((d: { id: string }) => d.id === id);\n setModal({ type: 'confirmDelete', target: 'deck', id, name: deck?.name || 'this deck' });\n }}\n onEdit={(id) => setModal({ type: 'editDeck', deckId: id })}\n onShare={(id) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === id);\n if (deck) setModal({ type: 'shareDeck', deckId: id, deckName: deck.name });\n }}\n // Create button is ungated by preview mode so users can always add a\n // new deck. Update/delete stay gated because they mutate selected decks.\n canCreate={canCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n />\n\n <AnimatePresence mode=\"wait\">\n {!activeDeck && !deckLoading && decks.length === 0 && (\n <EmptyState\n title=\"No Vibe Decks yet\"\n description=\"Create your first Vibe Deck to start organizing your commands and webhooks\"\n action={\n <div className=\"flex items-center gap-2\">\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setModal({ type: 'createDeck' })}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:opacity-90 text-sm font-medium\"\n >\n Create Vibe Deck\n </button>\n )}\n <GetHelpButton prompt=\"What is a VibeDeck and how do I use it to organise controls?\" />\n </div>\n }\n />\n )}\n\n {deckLoading && <LoadingState message=\"Loading deck...\" />}\n </AnimatePresence>\n\n {activeDeck && (\n <div className=\"space-y-4\">\n {!isPreview && (activeDeck.buttons?.length ?? 0) > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allButtonsSelected}\n indeterminate={partiallySelectedButtons}\n onChange={toggleAllButtons}\n ariaLabel=\"Select all buttons in active deck\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all buttons in this deck\n </span>\n </div>\n {selectedButtonIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearButtonSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedButtonIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('execute')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play size={14} />\n Execute Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('delete')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <VibeDeckGrid\n gridColumns={activeDeck.gridColumns}\n gridRows={activeDeck.gridRows}\n buttons={(activeDeck.buttons || []).map((b: any) => ({\n id: b.id,\n label: b.label,\n icon: b.icon || null,\n color: b.color || null,\n category: b.category || null,\n confirmationRequired: b.confirmationRequired,\n keyboardShortcut: b.keyboardShortcut || null,\n actionId: b.actionId || null,\n action: b.action\n ? {\n id: b.action.id,\n name: b.action.name,\n displayName: b.action.displayName || null,\n actionType: b.action.actionType,\n command: b.action.command || null,\n script: b.action.script || null,\n webhookUrl: b.action.webhookUrl || null,\n targetAgentId: b.action.targetAgentId || null,\n }\n : null,\n row: b.row,\n column: b.column,\n rowSpan: b.rowSpan,\n colSpan: b.colSpan,\n }))}\n onExecuteButton={handleExecuteButton}\n onEditButton={(id) => setModal({ type: 'editButton', buttonId: id })}\n onDeleteButton={(id) => {\n const btn = activeDeck.buttons?.find((b: { id: string }) => b.id === id);\n setModal({\n type: 'confirmDelete',\n target: 'button',\n id,\n name: btn?.label || 'this button',\n });\n }}\n onDropButton={handleDropButton}\n onAddButton={(row, column) => setModal({ type: 'createButton', row, column })}\n executingButtonId={executingButtonId}\n canCreate={effectiveCanCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n canExecute={canExecute}\n />\n </div>\n )}\n\n {executionLog.length > 0 && (\n <VibeDeckExecutionLog\n entries={executionLog}\n expanded={executionLogExpanded}\n onToggleExpanded={() => setExecutionLogExpanded((prev) => !prev)}\n onClear={() => setExecutionLog([])}\n />\n )}\n\n <AnimatePresence>\n {toasts.length > 0 && (\n <div className=\"fixed bottom-4 left-4 right-4 sm:left-auto sm:bottom-6 sm:right-6 z-50 flex flex-col gap-2 max-w-[calc(100vw-2rem)] sm:max-w-md\">\n {toasts.slice(-3).map((toast) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => dismissToast(toast.id))();\n }\n }}\n key={toast.id}\n className={`p-4 rounded-xl shadow-lg border cursor-pointer ${\n toast.variant === 'success'\n ? 'bg-status-success-bg/10 border-status-success-border/30 text-status-success-text'\n : 'bg-status-error-bg/10 border-status-error-border/30 text-status-error-text'\n }`}\n onClick={() => dismissToast(toast.id)}\n >\n <div className=\"font-medium text-sm\">{toast.message}</div>\n {toast.detail && (\n <pre className=\"text-xs font-mono opacity-80 mt-1 max-h-20 overflow-y-auto whitespace-pre-wrap\">\n {toast.detail}\n </pre>\n )}\n </div>\n ))}\n </div>\n )}\n </AnimatePresence>\n\n {modal.type === 'createDeck' && (\n <VibeDeckForm\n title=\"Create Vibe Deck\"\n onSubmit={handleCreateDeck}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createDeckLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker\n entityType=\"VIBECONTROLS_VIBEDECK\"\n onDraftChange={setDeckDraftTagIds}\n />\n </div>\n }\n />\n )}\n\n {modal.type === 'editDeck' && editingDeck && (\n <VibeDeckForm\n title=\"Edit Vibe Deck\"\n initialData={{\n name: editingDeck.name,\n description: editingDeck.description || '',\n colorTheme: editingDeck.colorTheme || '',\n gridColumns: editingDeck.gridColumns,\n gridRows: editingDeck.gridRows,\n }}\n onSubmit={handleUpdateDeck}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateDeckLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBEDECK\" entityId={editingDeck.id} />\n </div>\n }\n />\n )}\n\n {modal.type === 'createButton' && (\n <VibeDeckButtonForm\n title=\"Add Button\"\n agents={agents}\n onSubmit={handleCreateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={modal.row}\n buttonColumn={modal.column}\n />\n )}\n\n {modal.type === 'editButton' && editingButton && (\n <VibeDeckButtonForm\n title=\"Edit Button\"\n initialData={\n {\n label: editingButton.label,\n actionId: (editingButton as any).actionId || '',\n color: editingButton.color || '',\n category: editingButton.category || '',\n keyboardShortcut: editingButton.keyboardShortcut || '',\n confirmationRequired: editingButton.confirmationRequired,\n rowSpan: editingButton.rowSpan,\n colSpan: editingButton.colSpan,\n } as any\n }\n agents={agents}\n onSubmit={handleUpdateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={editingButton.row}\n buttonColumn={editingButton.column}\n />\n )}\n\n {modal.type === 'confirmDelete' && (\n <ConfirmDialog\n isOpen={true}\n title={`Delete ${modal.target === 'deck' ? 'Vibe Deck' : 'Button'}`}\n message={`Are you sure you want to delete \"${modal.name}\"?${modal.target === 'deck' ? ' All buttons in this deck will also be deleted.' : ''}`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDeleteConfirm}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n {modal.type === 'confirmExecute' && (\n <ConfirmDialog\n isOpen={true}\n title={`Execute: ${modal.label}`}\n message={`Are you sure you want to execute this action?\\n\\n${modal.detail}`}\n confirmLabel=\"Execute\"\n variant=\"warning\"\n onConfirm={handleConfirmExecute}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <ConfirmDialog\n isOpen={bulkDeckAction === 'delete'}\n title=\"Delete Vibe Decks\"\n message={`Are you sure you want to delete ${selectedDecks.length} selected deck${selectedDecks.length === 1 ? '' : 's'}? All buttons in those decks will also be deleted.`}\n confirmLabel=\"Delete Decks\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteDecks}\n onClose={() => setBulkDeckAction(null)}\n isLoading={bulkBusy === 'deleteDeck'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'execute'}\n title=\"Execute Vibe Deck Buttons\"\n message={`Execute ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This may trigger commands, scripts, or webhooks immediately.`}\n confirmLabel=\"Execute Buttons\"\n variant=\"warning\"\n onConfirm={handleBulkExecuteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'executeButton'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'delete'}\n title=\"Delete Vibe Deck Buttons\"\n message={`Delete ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel=\"Delete Buttons\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'deleteButton'}\n />\n\n {modal.type === 'shareDeck' && (\n <VibeDeckShareDialog\n vibeDeckId={modal.deckId}\n vibeDeckName={modal.deckName}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={quotaDialog !== null}\n onClose={() => setQuotaDialog(null)}\n onConfirm={() => {\n setQuotaDialog(null);\n navigate('/billing');\n }}\n title={quotaDialog?.title || 'Quota Reached'}\n message={quotaDialog?.message || 'Upgrade your plan to continue.'}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProvidersTab.js","names":[],"sources":["../../../src/pages/tunnels/ProvidersTab.tsx"],"sourcesContent":["import { PageHeader } from '@/components/shared/PageHeader';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { ErrorState } from '@/components/shared/ErrorState';\nimport { EmptyState } from '@/components/shared/EmptyState';\nimport { useListTunnelProvidersQuery } from '@/generated/wspace-operations';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport function ProvidersTab() {\n const tr = useTr();\n const { data, loading, error, refetch } = useListTunnelProvidersQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n // Show VibeTunnels first (preferred default), then Cloudflare, then any\n // future providers alphabetically. This matches the ordering used in the\n // tunnels list filter and the create wizard's provider selector.\n const providers = [...(data?.tunnelProviders ?? [])].sort(\n (a: { key: string }, b: { key: string }) => {\n const rank = (key: string) => (key === 'VIBETUNNELS' ? 0 : key === 'CLOUDFLARE' ? 1 : 2);\n const diff = rank(a.key) - rank(b.key);\n if (diff !== 0) return diff;\n return a.key.localeCompare(b.key);\n }\n );\n\n return (\n <div className=\"p-6 max-w-7xl mx-auto\">\n <PageHeader\n title={tr('vibecontrols.tunnels.providers.title', 'Providers')}\n description={tr(\n 'vibecontrols.tunnels.providers.description',\n 'Tunnel providers registered for this workspace with their capabilities.'\n )}\n />\n\n {loading && !data && <LoadingState message=\"Loading providers\" />}\n {error && (\n <ErrorState\n title=\"Failed to load providers\"\n message={error.message}\n onRetry={() => refetch()}\n />\n )}\n\n {data && providers.length === 0 && (\n <EmptyState\n title={tr('vibecontrols.tunnels.providers.emptyTitle', 'No providers registered')}\n description={tr(\n 'vibecontrols.tunnels.providers.emptyDescription',\n 'Contact your workspace admin to enable VibeTunnels or Cloudflare providers.'\n )}\n />\n )}\n\n {data && providers.length > 0 && (\n
|
|
1
|
+
{"version":3,"file":"ProvidersTab.js","names":[],"sources":["../../../src/pages/tunnels/ProvidersTab.tsx"],"sourcesContent":["import { PageHeader } from '@/components/shared/PageHeader';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { ErrorState } from '@/components/shared/ErrorState';\nimport { EmptyState } from '@/components/shared/EmptyState';\nimport { useListTunnelProvidersQuery } from '@/generated/wspace-operations';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport function ProvidersTab() {\n const tr = useTr();\n const { data, loading, error, refetch } = useListTunnelProvidersQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n // Show VibeTunnels first (preferred default), then Cloudflare, then any\n // future providers alphabetically. This matches the ordering used in the\n // tunnels list filter and the create wizard's provider selector.\n const providers = [...(data?.tunnelProviders ?? [])].sort(\n (a: { key: string }, b: { key: string }) => {\n const rank = (key: string) => (key === 'VIBETUNNELS' ? 0 : key === 'CLOUDFLARE' ? 1 : 2);\n const diff = rank(a.key) - rank(b.key);\n if (diff !== 0) return diff;\n return a.key.localeCompare(b.key);\n }\n );\n\n return (\n <div className=\"p-6 max-w-7xl mx-auto\">\n <PageHeader\n title={tr('vibecontrols.tunnels.providers.title', 'Providers')}\n description={tr(\n 'vibecontrols.tunnels.providers.description',\n 'Tunnel providers registered for this workspace with their capabilities.'\n )}\n />\n\n {loading && !data && <LoadingState message=\"Loading providers\" />}\n {error && (\n <ErrorState\n title=\"Failed to load providers\"\n message={error.message}\n onRetry={() => refetch()}\n />\n )}\n\n {data && providers.length === 0 && (\n <EmptyState\n title={tr('vibecontrols.tunnels.providers.emptyTitle', 'No providers registered')}\n description={tr(\n 'vibecontrols.tunnels.providers.emptyDescription',\n 'Contact your workspace admin to enable VibeTunnels or Cloudflare providers.'\n )}\n />\n )}\n\n {data && providers.length > 0 && (\n <div className=\"grid gap-4 md:grid-cols-2\">\n {providers.map((provider: any) => {\n const caps = provider.capabilities;\n return (\n <div\n key={provider.id}\n className={`rounded-lg border p-4 ${\n provider.isEnabled\n ? 'border-border-default bg-bg-surface'\n : 'border-border-subtle bg-bg-sunken opacity-60'\n }`}\n >\n <div className=\"flex items-start justify-between gap-3 mb-3\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {provider.displayName}\n </h3>\n <p className=\"text-xs text-text-muted\">{provider.key}</p>\n </div>\n <span\n className={`rounded-full px-2 py-0.5 text-xs ${\n provider.isEnabled\n ? 'bg-status-success-bg text-status-success-fg'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {provider.isEnabled ? 'Enabled' : 'Disabled'}\n </span>\n </div>\n {provider.description && (\n <p className=\"text-sm text-text-secondary mb-3\">{provider.description}</p>\n )}\n <dl className=\"grid grid-cols-2 gap-1 text-xs\">\n <Capability label=\"HTTP\" value={caps.supportsHttp} />\n <Capability label=\"HTTPS\" value={caps.supportsHttps} />\n <Capability label=\"TCP\" value={caps.supportsTcp} />\n <Capability label=\"Custom domains\" value={caps.supportsCustomDomains} />\n <Capability label=\"Managed subdomains\" value={caps.supportsManagedSubdomains} />\n <Capability label=\"Live logs\" value={caps.supportsLiveLogs} />\n <Capability label=\"Usage metrics\" value={caps.supportsUsageMetrics} />\n <Capability label=\"Credential rotation\" value={caps.supportsRotateCredentials} />\n </dl>\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nfunction Capability({ label, value }: { label: string; value: boolean }) {\n return (\n <div className=\"flex items-center gap-2\">\n <span\n className={`inline-block size-2 rounded-full ${\n value ? 'bg-status-success-bg' : 'bg-border-default'\n }`}\n />\n <dt className=\"text-text-secondary\">{label}</dt>\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,IAAe;CAC7B,IAAM,IAAK,GAAO,EACZ,EAAE,SAAM,YAAS,UAAO,eAAY,EAA4B,EACpE,aAAa,qBACd,CAAC,EAKI,IAAY,CAAC,GAAI,GAAM,mBAAmB,EAAE,CAAE,CAAC,MAClD,GAAoB,MAAuB;EAC1C,IAAM,KAAQ,MAAiB,MAAQ,gBAAgB,IAAI,MAAQ,eAAe,IAAI,GAChF,IAAO,EAAK,EAAE,IAAI,GAAG,EAAK,EAAE,IAAI;AAEtC,SADI,MAAS,IACN,EAAE,IAAI,cAAc,EAAE,IAAI,GADV;GAG1B;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,wCAAwC,YAAY;IAC9D,aAAa,EACX,8CACA,0EACD;IACD,CAAA;GAED,KAAW,CAAC,KAAQ,kBAAC,GAAD,EAAc,SAAQ,qBAAsB,CAAA;GAChE,KACC,kBAAC,GAAD;IACE,OAAM;IACN,SAAS,EAAM;IACf,eAAe,GAAS;IACxB,CAAA;GAGH,KAAQ,EAAU,WAAW,KAC5B,kBAAC,GAAD;IACE,OAAO,EAAG,6CAA6C,0BAA0B;IACjF,aAAa,EACX,mDACA,8EACD;IACD,CAAA;GAGH,KAAQ,EAAU,SAAS,KAC1B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAU,KAAK,MAAkB;KAChC,IAAM,IAAO,EAAS;AACtB,YACE,kBAAC,OAAD;MAEE,WAAW,yBACT,EAAS,YACL,wCACA;gBALR;OAQE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAS;SACP,CAAA,EACL,kBAAC,KAAD;SAAG,WAAU;mBAA2B,EAAS;SAAQ,CAAA,CACrD,EAAA,CAAA,EACN,kBAAC,QAAD;SACE,WAAW,oCACT,EAAS,YACL,gDACA;mBAGL,EAAS,YAAY,YAAY;SAC7B,CAAA,CACH;;OACL,EAAS,eACR,kBAAC,KAAD;QAAG,WAAU;kBAAoC,EAAS;QAAgB,CAAA;OAE5E,kBAAC,MAAD;QAAI,WAAU;kBAAd;SACE,kBAAC,GAAD;UAAY,OAAM;UAAO,OAAO,EAAK;UAAgB,CAAA;SACrD,kBAAC,GAAD;UAAY,OAAM;UAAQ,OAAO,EAAK;UAAiB,CAAA;SACvD,kBAAC,GAAD;UAAY,OAAM;UAAM,OAAO,EAAK;UAAe,CAAA;SACnD,kBAAC,GAAD;UAAY,OAAM;UAAiB,OAAO,EAAK;UAAyB,CAAA;SACxE,kBAAC,GAAD;UAAY,OAAM;UAAqB,OAAO,EAAK;UAA6B,CAAA;SAChF,kBAAC,GAAD;UAAY,OAAM;UAAY,OAAO,EAAK;UAAoB,CAAA;SAC9D,kBAAC,GAAD;UAAY,OAAM;UAAgB,OAAO,EAAK;UAAwB,CAAA;SACtE,kBAAC,GAAD;UAAY,OAAM;UAAsB,OAAO,EAAK;UAA6B,CAAA;SAC9E;;OACD;QArCC,EAAS,GAqCV;MAER;IACE,CAAA;GAEJ;;;AAIV,SAAS,EAAW,EAAE,UAAO,YAA4C;AACvE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD,EACE,WAAW,oCACT,IAAQ,yBAAyB,uBAEnC,CAAA,EACF,kBAAC,MAAD;GAAI,WAAU;aAAuB;GAAW,CAAA,CAC5C"}
|