@burdenoff/microfe-vibecontrols 2026.520.2 → 2026.520.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.
@@ -1 +1 @@
1
- {"version":3,"file":"ActionsPage.js","names":[],"sources":["../../src/pages/ActionsPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Copy, Play, Plus, RefreshCw, Trash2 } from 'lucide-react';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { AnimatePresence } from 'framer-motion';\nimport {\n PageHeader,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n SearchInput,\n FilterDropdown,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport {\n ActionCard,\n ActionForm,\n ActionSchedulesList,\n ExecutionHistory,\n} from '@/components/actions';\nimport type { ActionFormData } from '@/components/actions';\nimport {\n useListActionsQuery,\n useGetActionQuery,\n useListAgentsQuery,\n useCreateActionMutation,\n useUpdateActionMutation,\n useDeleteActionMutation,\n useExecuteActionMutation,\n useDuplicateActionMutation,\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 { useVibeControls } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { isQuotaExhaustedError, getQuotaDialogContent } from '@/utils/quotaUtils';\nimport { QuotaExhaustedDialog } from '@/components/shared/QuotaExhaustedDialog';\n\ntype ModalState =\n | { type: 'none' }\n | { type: 'create' }\n | { type: 'edit'; actionId: string }\n | { type: 'confirmDelete'; id: string; name: string }\n | { type: 'confirmExecute'; actionId: string; name: string; agentId?: string };\n\ninterface ErrorToast {\n message: string;\n variant: 'success' | 'error';\n detail?: string | null;\n id: number;\n}\n\nconst ACTION_TYPE_OPTIONS = [\n { value: 'COMMAND', label: 'Command' },\n { value: 'SCRIPT', label: 'Script' },\n { value: 'WEBHOOK', label: 'Webhook' },\n { value: 'WORKFLOW', label: 'Workflow' },\n];\n\nconst TRIGGER_TYPE_OPTIONS = [\n { value: 'MANUAL', label: 'Manual' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'EVENT', label: 'Event' },\n { value: 'ON_DEMAND', label: 'On Demand' },\n];\n\nexport function ActionsPage() {\n const navigate = useNavigate();\n const { workspaceId } = useVibeControls();\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 // Default to true when action permissions haven't been registered in RBAC yet\n const actionPermsRegistered = (\n Array.isArray(permissions) ? permissions : permissions?.permissionStrings\n )?.some?.((p: string) => p.startsWith('action:'));\n const canList = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_LIST) : true;\n const canCreate = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_CREATE) : true;\n const canUpdate = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_UPDATE) : true;\n const canDelete = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_DELETE) : true;\n const canExecute = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_EXECUTE) : true;\n const shouldSkipQueries = permissionsLoading;\n\n const [modal, setModal] = useState<ModalState>({ type: 'none' });\n const [toasts, setToasts] = useState<ErrorToast[]>([]);\n const [searchQuery, setSearchQuery] = useState('');\n const [actionTypeFilter, setActionTypeFilter] = useState<string | null>(null);\n const [triggerTypeFilter, setTriggerTypeFilter] = useState<string | null>(null);\n const [selectedActionId, setSelectedActionId] = useState<string | null>(null);\n const [selectedActionIds, setSelectedActionIds] = useState<Set<string>>(new Set());\n const [executingActionId, setExecutingActionId] = useState<string | null>(null);\n const [bulkModal, setBulkModal] = useState<'delete' | 'execute' | null>(null);\n const [bulkBusy, setBulkBusy] = useState<'delete' | 'execute' | 'duplicate' | null>(null);\n const [quotaKind, setQuotaKind] = useState<'action' | 'command-execution' | null>(null);\n\n const showToast = useCallback(\n (message: string, variant: 'success' | 'error', detail?: string | null, duration = 10000) => {\n const id = Date.now() + Math.random();\n setToasts((prev) => [...prev, { message, variant, detail, id }]);\n if (variant === 'success') {\n setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), duration);\n }\n },\n []\n );\n\n const dismissToast = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n // Queries\n const {\n data: actionsData,\n loading: actionsLoading,\n error: actionsError,\n refetch: refetchActions,\n } = useListActionsQuery({\n variables: {\n filter: {\n ...(actionTypeFilter && actionTypeFilter !== 'all' ? { actionType: actionTypeFilter } : {}),\n ...(triggerTypeFilter && triggerTypeFilter !== 'all'\n ? { triggerType: triggerTypeFilter }\n : {}),\n ...(searchQuery ? { search: searchQuery } : {}),\n },\n pagination: { limit: 50 },\n },\n skip: shouldSkipQueries,\n });\n\n const actions = useMemo(() => actionsData?.actions?.items || [], [actionsData]);\n\n // Fetch selected action details (with executions)\n const { data: selectedActionData } = useGetActionQuery({\n variables: { id: selectedActionId! },\n skip: !selectedActionId || shouldSkipQueries,\n });\n\n const selectedAction = selectedActionData?.action;\n const recentExecutions = selectedAction?.recentExecutions || [];\n\n // Agents for form\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n const agents = useMemo(\n () =>\n (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 [agentsData]\n );\n\n // Mutations\n const [createAction, { loading: createLoading }] = useCreateActionMutation({\n refetchQueries: ['ListActions'],\n });\n const { setDraftTagIds: setActionDraftTagIds, flushTags: flushActionTags } =\n useDraftEntityTags('VIBECONTROLS_ACTION');\n const [updateAction, { loading: updateLoading }] = useUpdateActionMutation({\n refetchQueries: ['ListActions', 'GetAction'],\n });\n const [deleteAction] = useDeleteActionMutation({\n refetchQueries: ['ListActions'],\n });\n const [executeAction] = useExecuteActionMutation({\n refetchQueries: ['GetAction'],\n });\n const [duplicateAction] = useDuplicateActionMutation({\n refetchQueries: ['ListActions'],\n });\n\n // Handlers\n const handleCreate = useCallback(\n async (data: ActionFormData) => {\n try {\n const result = await createAction({\n variables: {\n input: {\n name: data.name,\n displayName: data.displayName || null,\n description: data.description || null,\n actionType: data.actionType,\n triggerType: data.triggerType,\n schedule: data.triggerType === 'SCHEDULED' ? data.schedule || null : null,\n category: data.category || null,\n tags: data.tags.length > 0 ? data.tags : null,\n command: data.actionType === 'COMMAND' ? data.command || null : null,\n workingDirectory:\n data.actionType !== 'WEBHOOK' ? data.workingDirectory || null : null,\n targetAgentId: data.actionType !== 'WEBHOOK' ? data.targetAgentId || null : null,\n script: data.actionType === 'SCRIPT' ? data.script || null : null,\n scriptInterpreter:\n data.actionType === 'SCRIPT' ? data.scriptInterpreter || 'bash' : null,\n webhookUrl: data.actionType === 'WEBHOOK' ? data.webhookUrl || null : null,\n webhookMethod: data.actionType === 'WEBHOOK' ? data.webhookMethod || 'POST' : null,\n webhookBody: data.actionType === 'WEBHOOK' ? data.webhookBody || null : null,\n webhookAuthType: data.actionType === 'WEBHOOK' ? data.webhookAuthType || null : null,\n timeoutMs: data.timeoutMs,\n retryCount: data.retryCount,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n // With errorPolicy:'all', quota errors resolve on result.error instead of throwing\n if (result.error && isQuotaExhaustedError(result.error)) {\n setModal({ type: 'none' });\n setQuotaKind('action');\n return;\n }\n const newActionId = result.data?.createAction?.id;\n if (newActionId) await flushActionTags(newActionId);\n setModal({ type: 'none' });\n showToast('Action created', 'success');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setModal({ type: 'none' });\n setQuotaKind('action');\n return;\n }\n showToast(err instanceof Error ? err.message : 'Failed to create action', 'error');\n }\n },\n [createAction, flushActionTags, showToast]\n );\n\n const handleUpdate = useCallback(\n async (data: ActionFormData) => {\n if (modal.type !== 'edit') return;\n try {\n await updateAction({\n variables: {\n id: modal.actionId,\n input: {\n displayName: data.displayName || null,\n description: data.description || null,\n actionType: data.actionType,\n triggerType: data.triggerType,\n schedule: data.triggerType === 'SCHEDULED' ? data.schedule || null : null,\n category: data.category || null,\n tags: data.tags.length > 0 ? data.tags : null,\n command: data.actionType === 'COMMAND' ? data.command || null : null,\n workingDirectory:\n data.actionType !== 'WEBHOOK' ? data.workingDirectory || null : null,\n targetAgentId: data.actionType !== 'WEBHOOK' ? data.targetAgentId || null : null,\n script: data.actionType === 'SCRIPT' ? data.script || null : null,\n scriptInterpreter:\n data.actionType === 'SCRIPT' ? data.scriptInterpreter || 'bash' : null,\n webhookUrl: data.actionType === 'WEBHOOK' ? data.webhookUrl || null : null,\n webhookMethod: data.actionType === 'WEBHOOK' ? data.webhookMethod || 'POST' : null,\n webhookBody: data.actionType === 'WEBHOOK' ? data.webhookBody || null : null,\n webhookAuthType: data.actionType === 'WEBHOOK' ? data.webhookAuthType || null : null,\n timeoutMs: data.timeoutMs,\n retryCount: data.retryCount,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Action updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update action', 'error');\n }\n },\n [updateAction, modal, showToast]\n );\n\n const handleDelete = useCallback(async () => {\n if (modal.type !== 'confirmDelete') return;\n try {\n await deleteAction({ variables: { id: modal.id } });\n if (selectedActionId === modal.id) setSelectedActionId(null);\n setModal({ type: 'none' });\n showToast('Action deleted', 'success');\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete action', 'error');\n if (isNative()) void nativeNotify('error');\n }\n }, [modal, deleteAction, selectedActionId, showToast]);\n\n const handleExecute = async (actionId: string) => {\n const action = actions.find((a: { id: string }) => a.id === actionId) as\n | { id: string; name: string; displayName?: string | null; confirmationRequired: boolean }\n | undefined;\n if (!action) return;\n\n if (action.confirmationRequired) {\n setModal({\n type: 'confirmExecute',\n actionId,\n name: action.displayName || action.name,\n });\n return;\n }\n\n await doExecute(actionId);\n };\n\n const doExecute = useCallback(\n async (actionId: string) => {\n setExecutingActionId(actionId);\n try {\n const result = await executeAction({ variables: { id: actionId } });\n // With errorPolicy:'all', quota errors resolve on result.error instead of throwing\n if (result.error && isQuotaExhaustedError(result.error)) {\n setQuotaKind('command-execution');\n return;\n }\n const exec = result.data?.executeAction;\n if (exec) {\n showToast(\n exec.status === 'SUCCESS' || exec.status === 'COMPLETED'\n ? 'Action executed successfully'\n : 'Action execution failed',\n exec.status === 'SUCCESS' || exec.status === 'COMPLETED' ? 'success' : 'error',\n exec.output || exec.errorMessage\n );\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setQuotaKind('command-execution');\n return;\n }\n showToast(err instanceof Error ? err.message : 'Execution failed', 'error');\n } finally {\n setExecutingActionId(null);\n }\n },\n [executeAction, showToast]\n );\n\n const handleConfirmExecute = useCallback(async () => {\n if (modal.type !== 'confirmExecute') return;\n const actionId = modal.actionId;\n setModal({ type: 'none' });\n await doExecute(actionId);\n }, [modal, doExecute]);\n\n const handleDuplicate = useCallback(\n async (actionId: string) => {\n const action = actions.find((a: { id: string; name: string }) => a.id === actionId);\n if (!action) return;\n try {\n await duplicateAction({\n variables: { id: actionId, name: `${action.name}-copy` },\n });\n showToast('Action duplicated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to duplicate action', 'error');\n }\n },\n [actions, duplicateAction, showToast]\n );\n\n // Edit action: find the action data for the form\n const editingAction = useMemo(() => {\n if (modal.type !== 'edit') return null;\n return actions.find((a: { id: string }) => a.id === modal.actionId) || null;\n }, [modal, actions]);\n\n const selectedActions = useMemo(\n () => actions.filter((action: { id: string }) => selectedActionIds.has(action.id)),\n [actions, selectedActionIds]\n );\n\n const allVisibleSelected =\n actions.length > 0 &&\n actions.every((action: { id: string }) => selectedActionIds.has(action.id));\n\n const partiallySelected =\n actions.some((action: { id: string }) => selectedActionIds.has(action.id)) &&\n !allVisibleSelected;\n\n const toggleActionSelection = useCallback((actionId: string, checked: boolean) => {\n setSelectedActionIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(actionId);\n } else {\n next.delete(actionId);\n }\n return next;\n });\n }, []);\n\n const toggleAllVisibleActions = useCallback(\n (checked: boolean) => {\n setSelectedActionIds((prev) => {\n const next = new Set(prev);\n actions.forEach((action: { id: string }) => {\n if (checked) {\n next.add(action.id);\n } else {\n next.delete(action.id);\n }\n });\n return next;\n });\n },\n [actions]\n );\n\n const clearSelection = useCallback(() => {\n setSelectedActionIds(new Set());\n }, []);\n\n const handleBulkDuplicate = useCallback(async () => {\n if (selectedActions.length === 0) return;\n setBulkBusy('duplicate');\n try {\n const results = await Promise.allSettled(\n selectedActions.map((action: { id: string; name: string }) =>\n duplicateAction({\n variables: { id: action.id, name: `${action.name}-copy` },\n })\n )\n );\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n\n showToast(\n `Duplicated ${succeeded} action${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearSelection();\n await refetchActions();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to duplicate actions', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedActions, duplicateAction, showToast, clearSelection, refetchActions]);\n\n const handleBulkDelete = useCallback(async () => {\n if (selectedActions.length === 0) return;\n setBulkBusy('delete');\n try {\n const results = await Promise.allSettled(\n selectedActions.map((action: { id: string }) =>\n deleteAction({ variables: { id: action.id } })\n )\n );\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n\n if (selectedActionId && selectedActionIds.has(selectedActionId)) {\n setSelectedActionId(null);\n }\n\n showToast(\n `Deleted ${succeeded} action${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearSelection();\n setBulkModal(null);\n await refetchActions();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete actions', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [\n selectedActions,\n deleteAction,\n selectedActionId,\n selectedActionIds,\n showToast,\n clearSelection,\n refetchActions,\n ]);\n\n const handleBulkExecute = useCallback(async () => {\n if (selectedActions.length === 0) return;\n setBulkBusy('execute');\n let succeeded = 0;\n let failed = 0;\n let quotaHit = false;\n\n try {\n for (const action of selectedActions) {\n try {\n const result = await executeAction({ variables: { id: action.id } });\n // With errorPolicy:'all', quota errors resolve on result.error instead of throwing\n if (result.error && isQuotaExhaustedError(result.error)) {\n setQuotaKind('command-execution');\n quotaHit = true;\n break;\n }\n const exec = result.data?.executeAction;\n if (exec?.status === 'SUCCESS' || exec?.status === 'COMPLETED') {\n succeeded += 1;\n } else {\n failed += 1;\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setQuotaKind('command-execution');\n quotaHit = true;\n break;\n }\n failed += 1;\n }\n }\n\n // Skip summary toast when quota was hit — the quota dialog is the feedback\n if (!quotaHit) {\n showToast(\n `Executed ${succeeded} action${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n }\n clearSelection();\n setBulkModal(null);\n if (selectedActionId) {\n await refetchActions();\n }\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to execute actions', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedActions, executeAction, showToast, clearSelection, selectedActionId, refetchActions]);\n\n // Guard states\n if (!permissionsLoading && permissions && permissions.permissionStrings?.length > 0 && !canList) {\n return (\n <MotionPage>\n <AccessDenied />\n </MotionPage>\n );\n }\n\n if (actionsLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading Actions...\" />\n </MotionPage>\n );\n }\n\n if (actionsError) {\n return (\n <MotionPage>\n <ErrorState message={actionsError.message} onRetry={() => refetchActions()} />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"p-4 sm:p-6 space-y-6\">\n <PageHeader\n title={tr('nav.actions', 'Actions')}\n description=\"Automate tasks — run commands, scripts, or trigger webhooks on your agents\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetchActions()}\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 {canCreate && (\n <button\n onClick={() => setModal({ type: 'create' })}\n className=\"inline-flex items-center gap-1.5 px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:opacity-90 text-sm font-medium transition-opacity\"\n >\n <Plus size={16} />\n Create Action\n </button>\n )}\n </div>\n }\n />\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('actions.searchPlaceholder', 'Search actions...')}\n className=\"w-full sm:w-64\"\n />\n <FilterDropdown\n label=\"Type\"\n options={ACTION_TYPE_OPTIONS}\n value={actionTypeFilter}\n onChange={setActionTypeFilter}\n />\n <FilterDropdown\n label=\"Trigger\"\n options={TRIGGER_TYPE_OPTIONS}\n value={triggerTypeFilter}\n onChange={setTriggerTypeFilter}\n />\n </div>\n\n {actions.length > 0 && (\n <div className=\"rounded-lg border border-border-default 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 <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleActions}\n ariaLabel=\"Select all visible actions\"\n />\n <span className=\"text-sm font-medium text-text-primary\">Select all visible</span>\n </div>\n {selectedActionIds.size > 0 && (\n <button\n onClick={clearSelection}\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 {selectedActionIds.size} selected\n </span>\n <button\n onClick={() => setBulkModal('execute')}\n disabled={selectedActions.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{selectedActions.length > 0 ? ` (${selectedActions.length})` : ''}\n </span>\n </button>\n <button\n onClick={handleBulkDuplicate}\n disabled={selectedActions.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\n {selectedActions.length > 0 ? ` (${selectedActions.length})` : ''}\n </span>\n </button>\n <button\n onClick={() => setBulkModal('delete')}\n disabled={selectedActions.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{selectedActions.length > 0 ? ` (${selectedActions.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Actions Grid */}\n <AnimatePresence mode=\"wait\">\n {actions.length === 0 && !actionsLoading && (\n <EmptyState\n title=\"No actions yet\"\n description=\"Create your first action to automate commands, scripts, and webhooks\"\n action={\n <div className=\"flex items-center gap-2\">\n {canCreate && (\n <button\n onClick={() => setModal({ type: 'create' })}\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 Action\n </button>\n )}\n <GetHelpButton prompt=\"What is an Action in VibeControls and how do I create one?\" />\n </div>\n }\n />\n )}\n </AnimatePresence>\n\n {actions.length > 0 && (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {actions.map(\n (action: {\n id: string;\n name: string;\n displayName?: string | null;\n description?: string | null;\n actionType: string;\n sourceType?: string | null;\n triggerType: string;\n category?: string | null;\n tags?: string[] | null;\n executionCount?: number | null;\n lastExecutedAt?: string | null;\n confirmationRequired: boolean;\n }) => (\n <div\n key={action.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => setSelectedActionId(action.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n setSelectedActionId(action.id);\n }\n }}\n aria-label={`Select action ${action.displayName || action.name}`}\n >\n <ActionCard\n action={action}\n onExecute={handleExecute}\n onEdit={(id) => setModal({ type: 'edit', actionId: id })}\n onDuplicate={handleDuplicate}\n onDelete={(id) =>\n setModal({\n type: 'confirmDelete',\n id,\n name: action.displayName || action.name,\n })\n }\n // Schedule navigates to the scheduler microfe with the\n // actionId prefilled. The scheduler Create-Job form reads\n // `?actionId=` and auto-selects the action so users don't\n // need to copy IDs across tabs.\n onSchedule={(id) => navigate(`/scheduler?actionId=${encodeURIComponent(id)}`)}\n // Add-to-Calendar navigates to the VibeCalendar page with\n // the actionId prefilled so the event-create modal can\n // bind a VibeCalendarTask to this action.\n onAddToCalendar={(id) =>\n navigate(`/vibecontrols/calendar?actionId=${encodeURIComponent(id)}`)\n }\n canUpdate={canUpdate}\n canDelete={canDelete}\n canExecute={canExecute}\n isExecuting={executingActionId === action.id}\n selected={selectedActionIds.has(action.id)}\n onToggleSelect={(checked) => toggleActionSelection(action.id, checked)}\n />\n </div>\n )\n )}\n </div>\n )}\n\n {/* Schedules + Execution History Panels for the selected action */}\n <AnimatePresence>\n {selectedActionId && selectedAction && (\n <div className=\"space-y-3\">\n {workspaceId && (\n <ActionSchedulesList\n actionId={selectedActionId}\n workspaceId={workspaceId}\n actionName={selectedAction.displayName || selectedAction.name}\n onClose={() => setSelectedActionId(null)}\n />\n )}\n <ExecutionHistory\n executions={recentExecutions}\n actionName={selectedAction.displayName || selectedAction.name}\n onClose={() => setSelectedActionId(null)}\n />\n </div>\n )}\n </AnimatePresence>\n\n {/* Toast Notifications */}\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 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 {/* Modals */}\n {modal.type === 'create' && (\n <ActionForm\n title=\"Create Action\"\n agents={agents}\n onSubmit={handleCreate}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('actions.tags', 'Tags')}\n </h3>\n <EntityTagPicker\n entityType=\"VIBECONTROLS_ACTION\"\n onDraftChange={setActionDraftTagIds}\n />\n </div>\n }\n />\n )}\n\n {modal.type === 'edit' && editingAction && (\n <ActionForm\n title=\"Edit Action\"\n initialData={{\n name: editingAction.name,\n displayName: editingAction.displayName || '',\n description: editingAction.description || '',\n actionType: editingAction.actionType || 'COMMAND',\n triggerType: editingAction.triggerType || 'MANUAL',\n schedule: editingAction.schedule || '',\n category: editingAction.category || '',\n tags: editingAction.tags || [],\n command: editingAction.command || '',\n workingDirectory: editingAction.workingDirectory || '',\n targetAgentId: editingAction.targetAgentId || '',\n script: editingAction.script || '',\n scriptInterpreter: editingAction.scriptInterpreter || 'bash',\n webhookUrl: editingAction.webhookUrl || '',\n webhookMethod: editingAction.webhookMethod || 'POST',\n webhookBody: editingAction.webhookBody || '',\n webhookAuthType: editingAction.webhookAuthType || '',\n timeoutMs: editingAction.timeoutMs || 30000,\n retryCount: editingAction.retryCount || 0,\n confirmationRequired: editingAction.confirmationRequired ?? false,\n }}\n agents={agents}\n onSubmit={handleUpdate}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('actions.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_ACTION\" entityId={editingAction.id} />\n </div>\n }\n />\n )}\n\n {modal.type === 'confirmDelete' && (\n <ConfirmDialog\n isOpen={true}\n title=\"Delete Action\"\n message={`Are you sure you want to delete \"${modal.name}\"? This cannot be undone.`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDelete}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n {modal.type === 'confirmExecute' && (\n <ConfirmDialog\n isOpen={true}\n title={`Execute: ${modal.name}`}\n message={`Are you sure you want to execute \"${modal.name}\"?`}\n confirmLabel=\"Execute\"\n variant=\"warning\"\n onConfirm={handleConfirmExecute}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <ConfirmDialog\n isOpen={bulkModal === 'delete'}\n title=\"Delete Selected Actions\"\n message={`Are you sure you want to delete ${selectedActions.length} selected action${selectedActions.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel=\"Delete Actions\"\n variant=\"danger\"\n onConfirm={handleBulkDelete}\n onClose={() => setBulkModal(null)}\n isLoading={bulkBusy === 'delete'}\n />\n\n <ConfirmDialog\n isOpen={bulkModal === 'execute'}\n title=\"Execute Selected Actions\"\n message={`Execute ${selectedActions.length} selected action${selectedActions.length === 1 ? '' : 's'}? This may trigger commands, scripts, or webhooks immediately.`}\n confirmLabel=\"Execute Actions\"\n variant=\"warning\"\n onConfirm={handleBulkExecute}\n onClose={() => setBulkModal(null)}\n isLoading={bulkBusy === 'execute'}\n />\n\n <QuotaExhaustedDialog\n isOpen={quotaKind !== null}\n onClose={() => setQuotaKind(null)}\n onConfirm={() => {\n setQuotaKind(null);\n navigate('/billing');\n }}\n title={quotaKind ? getQuotaDialogContent(quotaKind).title : undefined}\n message={quotaKind ? getQuotaDialogContent(quotaKind).message : ''}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ActionsPage.js","names":[],"sources":["../../src/pages/ActionsPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Copy, Play, Plus, RefreshCw, Trash2 } from 'lucide-react';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { AnimatePresence } from 'framer-motion';\nimport {\n PageHeader,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n SearchInput,\n FilterDropdown,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport {\n ActionCard,\n ActionForm,\n ActionSchedulesList,\n ExecutionHistory,\n} from '@/components/actions';\nimport type { ActionFormData } from '@/components/actions';\nimport {\n useListActionsQuery,\n useGetActionQuery,\n useListAgentsQuery,\n useCreateActionMutation,\n useUpdateActionMutation,\n useDeleteActionMutation,\n useExecuteActionMutation,\n useDuplicateActionMutation,\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 { useVibeControls } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { isQuotaExhaustedError, getQuotaDialogContent } from '@/utils/quotaUtils';\nimport { QuotaExhaustedDialog } from '@/components/shared/QuotaExhaustedDialog';\n\ntype ModalState =\n | { type: 'none' }\n | { type: 'create' }\n | { type: 'edit'; actionId: string }\n | { type: 'confirmDelete'; id: string; name: string }\n | { type: 'confirmExecute'; actionId: string; name: string; agentId?: string };\n\ninterface ErrorToast {\n message: string;\n variant: 'success' | 'error';\n detail?: string | null;\n id: number;\n}\n\nconst ACTION_TYPE_OPTIONS = [\n { value: 'COMMAND', label: 'Command' },\n { value: 'SCRIPT', label: 'Script' },\n { value: 'WEBHOOK', label: 'Webhook' },\n { value: 'WORKFLOW', label: 'Workflow' },\n];\n\nconst TRIGGER_TYPE_OPTIONS = [\n { value: 'MANUAL', label: 'Manual' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'EVENT', label: 'Event' },\n { value: 'ON_DEMAND', label: 'On Demand' },\n];\n\nexport function ActionsPage() {\n const navigate = useNavigate();\n const { workspaceId } = useVibeControls();\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 // Default to true when action permissions haven't been registered in RBAC yet\n const actionPermsRegistered = (\n Array.isArray(permissions) ? permissions : permissions?.permissionStrings\n )?.some?.((p: string) => p.startsWith('action:'));\n const canList = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_LIST) : true;\n const canCreate = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_CREATE) : true;\n const canUpdate = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_UPDATE) : true;\n const canDelete = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_DELETE) : true;\n const canExecute = actionPermsRegistered ? hasPermission(VIBE_PERMISSIONS.ACTION_EXECUTE) : true;\n const shouldSkipQueries = permissionsLoading;\n\n const [modal, setModal] = useState<ModalState>({ type: 'none' });\n const [toasts, setToasts] = useState<ErrorToast[]>([]);\n const [searchQuery, setSearchQuery] = useState('');\n const [actionTypeFilter, setActionTypeFilter] = useState<string | null>(null);\n const [triggerTypeFilter, setTriggerTypeFilter] = useState<string | null>(null);\n const [selectedActionId, setSelectedActionId] = useState<string | null>(null);\n const [selectedActionIds, setSelectedActionIds] = useState<Set<string>>(new Set());\n const [executingActionId, setExecutingActionId] = useState<string | null>(null);\n const [bulkModal, setBulkModal] = useState<'delete' | 'execute' | null>(null);\n const [bulkBusy, setBulkBusy] = useState<'delete' | 'execute' | 'duplicate' | null>(null);\n const [quotaKind, setQuotaKind] = useState<'action' | 'command-execution' | null>(null);\n\n const showToast = useCallback(\n (message: string, variant: 'success' | 'error', detail?: string | null, duration = 10000) => {\n const id = Date.now() + Math.random();\n setToasts((prev) => [...prev, { message, variant, detail, id }]);\n if (variant === 'success') {\n setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), duration);\n }\n },\n []\n );\n\n const dismissToast = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n // Queries\n const {\n data: actionsData,\n loading: actionsLoading,\n error: actionsError,\n refetch: refetchActions,\n } = useListActionsQuery({\n variables: {\n filter: {\n ...(actionTypeFilter && actionTypeFilter !== 'all' ? { actionType: actionTypeFilter } : {}),\n ...(triggerTypeFilter && triggerTypeFilter !== 'all'\n ? { triggerType: triggerTypeFilter }\n : {}),\n ...(searchQuery ? { search: searchQuery } : {}),\n },\n pagination: { limit: 50 },\n },\n skip: shouldSkipQueries,\n });\n\n const actions = useMemo(() => actionsData?.actions?.items || [], [actionsData]);\n\n // Fetch selected action details (with executions)\n const { data: selectedActionData } = useGetActionQuery({\n variables: { id: selectedActionId! },\n skip: !selectedActionId || shouldSkipQueries,\n });\n\n const selectedAction = selectedActionData?.action;\n const recentExecutions = selectedAction?.recentExecutions || [];\n\n // Agents for form\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n const agents = useMemo(\n () =>\n (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 [agentsData]\n );\n\n // Mutations\n const [createAction, { loading: createLoading }] = useCreateActionMutation({\n refetchQueries: ['ListActions'],\n });\n const { setDraftTagIds: setActionDraftTagIds, flushTags: flushActionTags } =\n useDraftEntityTags('VIBECONTROLS_ACTION');\n const [updateAction, { loading: updateLoading }] = useUpdateActionMutation({\n refetchQueries: ['ListActions', 'GetAction'],\n });\n const [deleteAction] = useDeleteActionMutation({\n refetchQueries: ['ListActions'],\n });\n const [executeAction] = useExecuteActionMutation({\n refetchQueries: ['GetAction'],\n });\n const [duplicateAction] = useDuplicateActionMutation({\n refetchQueries: ['ListActions'],\n });\n\n // Handlers\n const handleCreate = useCallback(\n async (data: ActionFormData) => {\n try {\n const result = await createAction({\n variables: {\n input: {\n name: data.name,\n displayName: data.displayName || null,\n description: data.description || null,\n actionType: data.actionType,\n triggerType: data.triggerType,\n schedule: data.triggerType === 'SCHEDULED' ? data.schedule || null : null,\n category: data.category || null,\n tags: data.tags.length > 0 ? data.tags : null,\n command: data.actionType === 'COMMAND' ? data.command || null : null,\n workingDirectory:\n data.actionType !== 'WEBHOOK' ? data.workingDirectory || null : null,\n targetAgentId: data.actionType !== 'WEBHOOK' ? data.targetAgentId || null : null,\n script: data.actionType === 'SCRIPT' ? data.script || null : null,\n scriptInterpreter:\n data.actionType === 'SCRIPT' ? data.scriptInterpreter || 'bash' : null,\n webhookUrl: data.actionType === 'WEBHOOK' ? data.webhookUrl || null : null,\n webhookMethod: data.actionType === 'WEBHOOK' ? data.webhookMethod || 'POST' : null,\n webhookBody: data.actionType === 'WEBHOOK' ? data.webhookBody || null : null,\n webhookAuthType: data.actionType === 'WEBHOOK' ? data.webhookAuthType || null : null,\n timeoutMs: data.timeoutMs,\n retryCount: data.retryCount,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n // With errorPolicy:'all', quota errors resolve on result.error instead of throwing\n if (result.error && isQuotaExhaustedError(result.error)) {\n setModal({ type: 'none' });\n setQuotaKind('action');\n return;\n }\n const newActionId = result.data?.createAction?.id;\n setModal({ type: 'none' });\n showToast('Action created', 'success');\n if (newActionId) await flushActionTags(newActionId);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setModal({ type: 'none' });\n setQuotaKind('action');\n return;\n }\n showToast(err instanceof Error ? err.message : 'Failed to create action', 'error');\n }\n },\n [createAction, flushActionTags, showToast]\n );\n\n const handleUpdate = useCallback(\n async (data: ActionFormData) => {\n if (modal.type !== 'edit') return;\n try {\n await updateAction({\n variables: {\n id: modal.actionId,\n input: {\n displayName: data.displayName || null,\n description: data.description || null,\n actionType: data.actionType,\n triggerType: data.triggerType,\n schedule: data.triggerType === 'SCHEDULED' ? data.schedule || null : null,\n category: data.category || null,\n tags: data.tags.length > 0 ? data.tags : null,\n command: data.actionType === 'COMMAND' ? data.command || null : null,\n workingDirectory:\n data.actionType !== 'WEBHOOK' ? data.workingDirectory || null : null,\n targetAgentId: data.actionType !== 'WEBHOOK' ? data.targetAgentId || null : null,\n script: data.actionType === 'SCRIPT' ? data.script || null : null,\n scriptInterpreter:\n data.actionType === 'SCRIPT' ? data.scriptInterpreter || 'bash' : null,\n webhookUrl: data.actionType === 'WEBHOOK' ? data.webhookUrl || null : null,\n webhookMethod: data.actionType === 'WEBHOOK' ? data.webhookMethod || 'POST' : null,\n webhookBody: data.actionType === 'WEBHOOK' ? data.webhookBody || null : null,\n webhookAuthType: data.actionType === 'WEBHOOK' ? data.webhookAuthType || null : null,\n timeoutMs: data.timeoutMs,\n retryCount: data.retryCount,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Action updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update action', 'error');\n }\n },\n [updateAction, modal, showToast]\n );\n\n const handleDelete = useCallback(async () => {\n if (modal.type !== 'confirmDelete') return;\n try {\n await deleteAction({ variables: { id: modal.id } });\n if (selectedActionId === modal.id) setSelectedActionId(null);\n setModal({ type: 'none' });\n showToast('Action deleted', 'success');\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete action', 'error');\n if (isNative()) void nativeNotify('error');\n }\n }, [modal, deleteAction, selectedActionId, showToast]);\n\n const handleExecute = async (actionId: string) => {\n const action = actions.find((a: { id: string }) => a.id === actionId) as\n | { id: string; name: string; displayName?: string | null; confirmationRequired: boolean }\n | undefined;\n if (!action) return;\n\n if (action.confirmationRequired) {\n setModal({\n type: 'confirmExecute',\n actionId,\n name: action.displayName || action.name,\n });\n return;\n }\n\n await doExecute(actionId);\n };\n\n const doExecute = useCallback(\n async (actionId: string) => {\n setExecutingActionId(actionId);\n try {\n const result = await executeAction({ variables: { id: actionId } });\n // With errorPolicy:'all', quota errors resolve on result.error instead of throwing\n if (result.error && isQuotaExhaustedError(result.error)) {\n setQuotaKind('command-execution');\n return;\n }\n const exec = result.data?.executeAction;\n if (exec) {\n showToast(\n exec.status === 'SUCCESS' || exec.status === 'COMPLETED'\n ? 'Action executed successfully'\n : 'Action execution failed',\n exec.status === 'SUCCESS' || exec.status === 'COMPLETED' ? 'success' : 'error',\n exec.output || exec.errorMessage\n );\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setQuotaKind('command-execution');\n return;\n }\n showToast(err instanceof Error ? err.message : 'Execution failed', 'error');\n } finally {\n setExecutingActionId(null);\n }\n },\n [executeAction, showToast]\n );\n\n const handleConfirmExecute = useCallback(async () => {\n if (modal.type !== 'confirmExecute') return;\n const actionId = modal.actionId;\n setModal({ type: 'none' });\n await doExecute(actionId);\n }, [modal, doExecute]);\n\n const handleDuplicate = useCallback(\n async (actionId: string) => {\n const action = actions.find((a: { id: string; name: string }) => a.id === actionId);\n if (!action) return;\n try {\n await duplicateAction({\n variables: { id: actionId, name: `${action.name}-copy` },\n });\n showToast('Action duplicated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to duplicate action', 'error');\n }\n },\n [actions, duplicateAction, showToast]\n );\n\n // Edit action: find the action data for the form\n const editingAction = useMemo(() => {\n if (modal.type !== 'edit') return null;\n return actions.find((a: { id: string }) => a.id === modal.actionId) || null;\n }, [modal, actions]);\n\n const selectedActions = useMemo(\n () => actions.filter((action: { id: string }) => selectedActionIds.has(action.id)),\n [actions, selectedActionIds]\n );\n\n const allVisibleSelected =\n actions.length > 0 &&\n actions.every((action: { id: string }) => selectedActionIds.has(action.id));\n\n const partiallySelected =\n actions.some((action: { id: string }) => selectedActionIds.has(action.id)) &&\n !allVisibleSelected;\n\n const toggleActionSelection = useCallback((actionId: string, checked: boolean) => {\n setSelectedActionIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(actionId);\n } else {\n next.delete(actionId);\n }\n return next;\n });\n }, []);\n\n const toggleAllVisibleActions = useCallback(\n (checked: boolean) => {\n setSelectedActionIds((prev) => {\n const next = new Set(prev);\n actions.forEach((action: { id: string }) => {\n if (checked) {\n next.add(action.id);\n } else {\n next.delete(action.id);\n }\n });\n return next;\n });\n },\n [actions]\n );\n\n const clearSelection = useCallback(() => {\n setSelectedActionIds(new Set());\n }, []);\n\n const handleBulkDuplicate = useCallback(async () => {\n if (selectedActions.length === 0) return;\n setBulkBusy('duplicate');\n try {\n const results = await Promise.allSettled(\n selectedActions.map((action: { id: string; name: string }) =>\n duplicateAction({\n variables: { id: action.id, name: `${action.name}-copy` },\n })\n )\n );\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n\n showToast(\n `Duplicated ${succeeded} action${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearSelection();\n await refetchActions();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to duplicate actions', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedActions, duplicateAction, showToast, clearSelection, refetchActions]);\n\n const handleBulkDelete = useCallback(async () => {\n if (selectedActions.length === 0) return;\n setBulkBusy('delete');\n try {\n const results = await Promise.allSettled(\n selectedActions.map((action: { id: string }) =>\n deleteAction({ variables: { id: action.id } })\n )\n );\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n\n if (selectedActionId && selectedActionIds.has(selectedActionId)) {\n setSelectedActionId(null);\n }\n\n showToast(\n `Deleted ${succeeded} action${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearSelection();\n setBulkModal(null);\n await refetchActions();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete actions', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [\n selectedActions,\n deleteAction,\n selectedActionId,\n selectedActionIds,\n showToast,\n clearSelection,\n refetchActions,\n ]);\n\n const handleBulkExecute = useCallback(async () => {\n if (selectedActions.length === 0) return;\n setBulkBusy('execute');\n let succeeded = 0;\n let failed = 0;\n let quotaHit = false;\n\n try {\n for (const action of selectedActions) {\n try {\n const result = await executeAction({ variables: { id: action.id } });\n // With errorPolicy:'all', quota errors resolve on result.error instead of throwing\n if (result.error && isQuotaExhaustedError(result.error)) {\n setQuotaKind('command-execution');\n quotaHit = true;\n break;\n }\n const exec = result.data?.executeAction;\n if (exec?.status === 'SUCCESS' || exec?.status === 'COMPLETED') {\n succeeded += 1;\n } else {\n failed += 1;\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setQuotaKind('command-execution');\n quotaHit = true;\n break;\n }\n failed += 1;\n }\n }\n\n // Skip summary toast when quota was hit — the quota dialog is the feedback\n if (!quotaHit) {\n showToast(\n `Executed ${succeeded} action${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n }\n clearSelection();\n setBulkModal(null);\n if (selectedActionId) {\n await refetchActions();\n }\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to execute actions', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedActions, executeAction, showToast, clearSelection, selectedActionId, refetchActions]);\n\n // Guard states\n if (!permissionsLoading && permissions && permissions.permissionStrings?.length > 0 && !canList) {\n return (\n <MotionPage>\n <AccessDenied />\n </MotionPage>\n );\n }\n\n if (actionsLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading Actions...\" />\n </MotionPage>\n );\n }\n\n if (actionsError) {\n return (\n <MotionPage>\n <ErrorState message={actionsError.message} onRetry={() => refetchActions()} />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"p-4 sm:p-6 space-y-6\">\n <PageHeader\n title={tr('nav.actions', 'Actions')}\n description=\"Automate tasks — run commands, scripts, or trigger webhooks on your agents\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetchActions()}\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 {canCreate && (\n <button\n onClick={() => setModal({ type: 'create' })}\n className=\"inline-flex items-center gap-1.5 px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:opacity-90 text-sm font-medium transition-opacity\"\n >\n <Plus size={16} />\n Create Action\n </button>\n )}\n </div>\n }\n />\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('actions.searchPlaceholder', 'Search actions...')}\n className=\"w-full sm:w-64\"\n />\n <FilterDropdown\n label=\"Type\"\n options={ACTION_TYPE_OPTIONS}\n value={actionTypeFilter}\n onChange={setActionTypeFilter}\n />\n <FilterDropdown\n label=\"Trigger\"\n options={TRIGGER_TYPE_OPTIONS}\n value={triggerTypeFilter}\n onChange={setTriggerTypeFilter}\n />\n </div>\n\n {actions.length > 0 && (\n <div className=\"rounded-lg border border-border-default 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 <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleActions}\n ariaLabel=\"Select all visible actions\"\n />\n <span className=\"text-sm font-medium text-text-primary\">Select all visible</span>\n </div>\n {selectedActionIds.size > 0 && (\n <button\n onClick={clearSelection}\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 {selectedActionIds.size} selected\n </span>\n <button\n onClick={() => setBulkModal('execute')}\n disabled={selectedActions.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{selectedActions.length > 0 ? ` (${selectedActions.length})` : ''}\n </span>\n </button>\n <button\n onClick={handleBulkDuplicate}\n disabled={selectedActions.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\n {selectedActions.length > 0 ? ` (${selectedActions.length})` : ''}\n </span>\n </button>\n <button\n onClick={() => setBulkModal('delete')}\n disabled={selectedActions.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{selectedActions.length > 0 ? ` (${selectedActions.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Actions Grid */}\n <AnimatePresence mode=\"wait\">\n {actions.length === 0 && !actionsLoading && (\n <EmptyState\n title=\"No actions yet\"\n description=\"Create your first action to automate commands, scripts, and webhooks\"\n action={\n <div className=\"flex items-center gap-2\">\n {canCreate && (\n <button\n onClick={() => setModal({ type: 'create' })}\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 Action\n </button>\n )}\n <GetHelpButton prompt=\"What is an Action in VibeControls and how do I create one?\" />\n </div>\n }\n />\n )}\n </AnimatePresence>\n\n {actions.length > 0 && (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {actions.map(\n (action: {\n id: string;\n name: string;\n displayName?: string | null;\n description?: string | null;\n actionType: string;\n sourceType?: string | null;\n triggerType: string;\n category?: string | null;\n tags?: string[] | null;\n executionCount?: number | null;\n lastExecutedAt?: string | null;\n confirmationRequired: boolean;\n }) => (\n <div\n key={action.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => setSelectedActionId(action.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n setSelectedActionId(action.id);\n }\n }}\n aria-label={`Select action ${action.displayName || action.name}`}\n >\n <ActionCard\n action={action}\n onExecute={handleExecute}\n onEdit={(id) => setModal({ type: 'edit', actionId: id })}\n onDuplicate={handleDuplicate}\n onDelete={(id) =>\n setModal({\n type: 'confirmDelete',\n id,\n name: action.displayName || action.name,\n })\n }\n // Schedule navigates to the scheduler microfe with the\n // actionId prefilled. The scheduler Create-Job form reads\n // `?actionId=` and auto-selects the action so users don't\n // need to copy IDs across tabs.\n onSchedule={(id) => navigate(`/scheduler?actionId=${encodeURIComponent(id)}`)}\n // Add-to-Calendar navigates to the VibeCalendar page with\n // the actionId prefilled so the event-create modal can\n // bind a VibeCalendarTask to this action.\n onAddToCalendar={(id) =>\n navigate(`/vibecontrols/calendar?actionId=${encodeURIComponent(id)}`)\n }\n canUpdate={canUpdate}\n canDelete={canDelete}\n canExecute={canExecute}\n isExecuting={executingActionId === action.id}\n selected={selectedActionIds.has(action.id)}\n onToggleSelect={(checked) => toggleActionSelection(action.id, checked)}\n />\n </div>\n )\n )}\n </div>\n )}\n\n {/* Schedules + Execution History Panels for the selected action */}\n <AnimatePresence>\n {selectedActionId && selectedAction && (\n <div className=\"space-y-3\">\n {workspaceId && (\n <ActionSchedulesList\n actionId={selectedActionId}\n workspaceId={workspaceId}\n actionName={selectedAction.displayName || selectedAction.name}\n onClose={() => setSelectedActionId(null)}\n />\n )}\n <ExecutionHistory\n executions={recentExecutions}\n actionName={selectedAction.displayName || selectedAction.name}\n onClose={() => setSelectedActionId(null)}\n />\n </div>\n )}\n </AnimatePresence>\n\n {/* Toast Notifications */}\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 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 {/* Modals */}\n {modal.type === 'create' && (\n <ActionForm\n title=\"Create Action\"\n agents={agents}\n onSubmit={handleCreate}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('actions.tags', 'Tags')}\n </h3>\n <EntityTagPicker\n entityType=\"VIBECONTROLS_ACTION\"\n onDraftChange={setActionDraftTagIds}\n />\n </div>\n }\n />\n )}\n\n {modal.type === 'edit' && editingAction && (\n <ActionForm\n title=\"Edit Action\"\n initialData={{\n name: editingAction.name,\n displayName: editingAction.displayName || '',\n description: editingAction.description || '',\n actionType: editingAction.actionType || 'COMMAND',\n triggerType: editingAction.triggerType || 'MANUAL',\n schedule: editingAction.schedule || '',\n category: editingAction.category || '',\n tags: editingAction.tags || [],\n command: editingAction.command || '',\n workingDirectory: editingAction.workingDirectory || '',\n targetAgentId: editingAction.targetAgentId || '',\n script: editingAction.script || '',\n scriptInterpreter: editingAction.scriptInterpreter || 'bash',\n webhookUrl: editingAction.webhookUrl || '',\n webhookMethod: editingAction.webhookMethod || 'POST',\n webhookBody: editingAction.webhookBody || '',\n webhookAuthType: editingAction.webhookAuthType || '',\n timeoutMs: editingAction.timeoutMs || 30000,\n retryCount: editingAction.retryCount || 0,\n confirmationRequired: editingAction.confirmationRequired ?? false,\n }}\n agents={agents}\n onSubmit={handleUpdate}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('actions.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_ACTION\" entityId={editingAction.id} />\n </div>\n }\n />\n )}\n\n {modal.type === 'confirmDelete' && (\n <ConfirmDialog\n isOpen={true}\n title=\"Delete Action\"\n message={`Are you sure you want to delete \"${modal.name}\"? This cannot be undone.`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDelete}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n {modal.type === 'confirmExecute' && (\n <ConfirmDialog\n isOpen={true}\n title={`Execute: ${modal.name}`}\n message={`Are you sure you want to execute \"${modal.name}\"?`}\n confirmLabel=\"Execute\"\n variant=\"warning\"\n onConfirm={handleConfirmExecute}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <ConfirmDialog\n isOpen={bulkModal === 'delete'}\n title=\"Delete Selected Actions\"\n message={`Are you sure you want to delete ${selectedActions.length} selected action${selectedActions.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel=\"Delete Actions\"\n variant=\"danger\"\n onConfirm={handleBulkDelete}\n onClose={() => setBulkModal(null)}\n isLoading={bulkBusy === 'delete'}\n />\n\n <ConfirmDialog\n isOpen={bulkModal === 'execute'}\n title=\"Execute Selected Actions\"\n message={`Execute ${selectedActions.length} selected action${selectedActions.length === 1 ? '' : 's'}? This may trigger commands, scripts, or webhooks immediately.`}\n confirmLabel=\"Execute Actions\"\n variant=\"warning\"\n onConfirm={handleBulkExecute}\n onClose={() => setBulkModal(null)}\n isLoading={bulkBusy === 'execute'}\n />\n\n <QuotaExhaustedDialog\n isOpen={quotaKind !== null}\n onClose={() => setQuotaKind(null)}\n onConfirm={() => {\n setQuotaKind(null);\n navigate('/billing');\n }}\n title={quotaKind ? getQuotaDialogContent(quotaKind).title : undefined}\n message={quotaKind ? getQuotaDialogContent(quotaKind).message : ''}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -241,7 +241,8 @@ function O() {
241
241
  tags: e.tags.length > 0 ? e.tags : void 0
242
242
  } } });
243
243
  if (!t.data?.createVibecontrolsComponent?.id) throw Error(M("component.failedCreate", "Failed to create component"));
244
- await Z(t.data.createVibecontrolsComponent.id), await J(), H(!1), W(M("component.createdSuccess", "Component created successfully")), setTimeout(() => W(null), 3e3);
244
+ let n = t.data.createVibecontrolsComponent.id;
245
+ H(!1), W(M("component.createdSuccess", "Component created successfully")), await Z(n), await J(), setTimeout(() => W(null), 3e3);
245
246
  },
246
247
  extraSection: /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("h3", {
247
248
  className: "text-sm font-medium text-text-secondary mb-2",
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentsPage.js","names":[],"sources":["../../src/pages/ComponentsPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Plus, Box, RefreshCw } from 'lucide-react';\nimport { ComponentCard } from '@/components/idp-components';\nimport type { ComponentCardData } from '@/components/idp-components';\nimport { ComponentForm } from '@/components/idp-components';\nimport type { ComponentFormData } from '@/components/idp-components';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListComponentsQuery, useCreateComponentMutation } from '@/generated/wspace-operations';\n\nexport function ComponentsPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const typeOptions = [\n { value: 'all', label: tr('component.allTypes', 'All Types') },\n { value: 'SERVICE', label: tr('component.typeService', 'Service') },\n { value: 'FRONTEND', label: tr('component.typeFrontend', 'Frontend') },\n { value: 'LIBRARY', label: tr('component.typeLibrary', 'Library') },\n { value: 'API', label: tr('component.typeApi', 'API') },\n { value: 'MOBILE', label: tr('component.typeMobile', 'Mobile') },\n { value: 'DATABASE', label: tr('component.typeDatabase', 'Database') },\n { value: 'INFRASTRUCTURE', label: tr('component.typeInfrastructure', 'Infrastructure') },\n { value: 'CUSTOM', label: tr('component.typeCustom', 'Custom') },\n ];\n\n const lifecycleOptions = [\n { value: 'all', label: tr('component.allLifecycles', 'All Lifecycles') },\n { value: 'EXPERIMENTAL', label: tr('component.lifecycleExperimental', 'Experimental') },\n { value: 'PRODUCTION', label: tr('component.lifecycleProduction', 'Production') },\n { value: 'DEPRECATED', label: tr('component.lifecycleDeprecated', 'Deprecated') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState('all');\n const [lifecycleFilter, setLifecycleFilter] = useState('all');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const { data, loading, error, refetch } = useListComponentsQuery({\n variables: {\n filter: {\n type: typeFilter !== 'all' ? typeFilter : undefined,\n lifecycle: lifecycleFilter !== 'all' ? lifecycleFilter : undefined,\n },\n ...(searchQuery ? { pagination: { limit: 1000 } } : {}),\n },\n fetchPolicy: 'cache-and-network',\n });\n\n const [createComponent] = useCreateComponentMutation({\n refetchQueries: ['ListComponents'],\n awaitRefetchQueries: true,\n });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_COMPONENT');\n\n const components: ComponentCardData[] = useMemo(() => {\n const items = data?.vibecontrolsComponents?.items ?? [];\n return items.map((item: Record<string, unknown>) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n type: item.type as ComponentCardData['type'],\n lifecycle: item.lifecycle as ComponentCardData['lifecycle'],\n version: (item.version as string) || null,\n language: (item.language as string) || null,\n framework: (item.framework as string) || null,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n }));\n }, [data?.vibecontrolsComponents?.items]);\n\n const filteredComponents = useMemo(() => {\n if (!searchQuery) return components;\n const query = searchQuery.toLowerCase();\n return components.filter(\n (c) =>\n c.name.toLowerCase().includes(query) ||\n c.description?.toLowerCase().includes(query) ||\n c.language?.toLowerCase().includes(query) ||\n c.framework?.toLowerCase().includes(query) ||\n c.tags?.some((tag) => tag.toLowerCase().includes(query))\n );\n }, [components, searchQuery]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleComponentSelect = (component: ComponentCardData) => {\n navigate(`/components/${component.id}`);\n };\n\n const handleCreateComponent = async (formData: ComponentFormData) => {\n const result = await createComponent({\n variables: {\n input: {\n name: formData.name,\n description: formData.description || undefined,\n type: formData.type,\n lifecycle: formData.lifecycle,\n language: formData.language || undefined,\n framework: formData.framework || undefined,\n version: formData.version || undefined,\n owner: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n if (!result.data?.createVibecontrolsComponent?.id) {\n throw new Error(tr('component.failedCreate', 'Failed to create component'));\n }\n await flushTags(result.data.createVibecontrolsComponent.id);\n await refetch();\n setShowCreateForm(false);\n setSuccessMessage(tr('component.createdSuccess', 'Component created successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n if (loading && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('component.loading', 'Loading components...')} />\n </MotionPage>\n );\n }\n\n if (error && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('component.failedLoad', 'Failed to load components')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('component.title', 'Components')}\n description={tr('component.description', '{{total}} components', {\n total: components.length,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n </div>\n }\n />\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('component.searchPlaceholder', 'Search components...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n </div>\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('component.filterType', 'Type')}\n value={typeFilter}\n options={typeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('component.filterLifecycle', 'Lifecycle')}\n value={lifecycleFilter}\n options={lifecycleOptions}\n onChange={setLifecycleFilter}\n />\n </div>\n </div>\n\n {/* Results count */}\n {(searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all') && (\n <p className=\"text-sm text-text-secondary\">\n {tr('component.showingResults', 'Showing {{filtered}} of {{total}} components', {\n filtered: filteredComponents.length,\n total: components.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('common.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n )}\n\n {/* Grid */}\n {filteredComponents.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"component-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredComponents.map((component) => (\n <MotionItem key={component.id}>\n <ComponentCard component={component} onOpen={handleComponentSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Box className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyNoResults', 'No components found')\n : tr('component.emptyNoComponents', 'No components yet')\n }\n description={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'component.emptyCreate',\n 'Register your first component to start tracking your software catalog'\n )\n }\n action={\n !searchQuery && typeFilter === 'all' && lifecycleFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n <GetHelpButton prompt=\"What is a Component in VibeControls and how do I add one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n {/* Create Form */}\n <ComponentForm\n isOpen={showCreateForm}\n onClose={() => setShowCreateForm(false)}\n onSubmit={handleCreateComponent}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('component.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_COMPONENT\" onDraftChange={setDraftTagIds} />\n </div>\n }\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ComponentsPage.js","names":[],"sources":["../../src/pages/ComponentsPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Plus, Box, RefreshCw } from 'lucide-react';\nimport { ComponentCard } from '@/components/idp-components';\nimport type { ComponentCardData } from '@/components/idp-components';\nimport { ComponentForm } from '@/components/idp-components';\nimport type { ComponentFormData } from '@/components/idp-components';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListComponentsQuery, useCreateComponentMutation } from '@/generated/wspace-operations';\n\nexport function ComponentsPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const typeOptions = [\n { value: 'all', label: tr('component.allTypes', 'All Types') },\n { value: 'SERVICE', label: tr('component.typeService', 'Service') },\n { value: 'FRONTEND', label: tr('component.typeFrontend', 'Frontend') },\n { value: 'LIBRARY', label: tr('component.typeLibrary', 'Library') },\n { value: 'API', label: tr('component.typeApi', 'API') },\n { value: 'MOBILE', label: tr('component.typeMobile', 'Mobile') },\n { value: 'DATABASE', label: tr('component.typeDatabase', 'Database') },\n { value: 'INFRASTRUCTURE', label: tr('component.typeInfrastructure', 'Infrastructure') },\n { value: 'CUSTOM', label: tr('component.typeCustom', 'Custom') },\n ];\n\n const lifecycleOptions = [\n { value: 'all', label: tr('component.allLifecycles', 'All Lifecycles') },\n { value: 'EXPERIMENTAL', label: tr('component.lifecycleExperimental', 'Experimental') },\n { value: 'PRODUCTION', label: tr('component.lifecycleProduction', 'Production') },\n { value: 'DEPRECATED', label: tr('component.lifecycleDeprecated', 'Deprecated') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState('all');\n const [lifecycleFilter, setLifecycleFilter] = useState('all');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const { data, loading, error, refetch } = useListComponentsQuery({\n variables: {\n filter: {\n type: typeFilter !== 'all' ? typeFilter : undefined,\n lifecycle: lifecycleFilter !== 'all' ? lifecycleFilter : undefined,\n },\n ...(searchQuery ? { pagination: { limit: 1000 } } : {}),\n },\n fetchPolicy: 'cache-and-network',\n });\n\n const [createComponent] = useCreateComponentMutation({\n refetchQueries: ['ListComponents'],\n awaitRefetchQueries: true,\n });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_COMPONENT');\n\n const components: ComponentCardData[] = useMemo(() => {\n const items = data?.vibecontrolsComponents?.items ?? [];\n return items.map((item: Record<string, unknown>) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n type: item.type as ComponentCardData['type'],\n lifecycle: item.lifecycle as ComponentCardData['lifecycle'],\n version: (item.version as string) || null,\n language: (item.language as string) || null,\n framework: (item.framework as string) || null,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n }));\n }, [data?.vibecontrolsComponents?.items]);\n\n const filteredComponents = useMemo(() => {\n if (!searchQuery) return components;\n const query = searchQuery.toLowerCase();\n return components.filter(\n (c) =>\n c.name.toLowerCase().includes(query) ||\n c.description?.toLowerCase().includes(query) ||\n c.language?.toLowerCase().includes(query) ||\n c.framework?.toLowerCase().includes(query) ||\n c.tags?.some((tag) => tag.toLowerCase().includes(query))\n );\n }, [components, searchQuery]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleComponentSelect = (component: ComponentCardData) => {\n navigate(`/components/${component.id}`);\n };\n\n const handleCreateComponent = async (formData: ComponentFormData) => {\n const result = await createComponent({\n variables: {\n input: {\n name: formData.name,\n description: formData.description || undefined,\n type: formData.type,\n lifecycle: formData.lifecycle,\n language: formData.language || undefined,\n framework: formData.framework || undefined,\n version: formData.version || undefined,\n owner: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n if (!result.data?.createVibecontrolsComponent?.id) {\n throw new Error(tr('component.failedCreate', 'Failed to create component'));\n }\n const newComponentId = result.data.createVibecontrolsComponent.id;\n setShowCreateForm(false);\n setSuccessMessage(tr('component.createdSuccess', 'Component created successfully'));\n await flushTags(newComponentId);\n await refetch();\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n if (loading && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('component.loading', 'Loading components...')} />\n </MotionPage>\n );\n }\n\n if (error && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('component.failedLoad', 'Failed to load components')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('component.title', 'Components')}\n description={tr('component.description', '{{total}} components', {\n total: components.length,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n </div>\n }\n />\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('component.searchPlaceholder', 'Search components...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n </div>\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('component.filterType', 'Type')}\n value={typeFilter}\n options={typeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('component.filterLifecycle', 'Lifecycle')}\n value={lifecycleFilter}\n options={lifecycleOptions}\n onChange={setLifecycleFilter}\n />\n </div>\n </div>\n\n {/* Results count */}\n {(searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all') && (\n <p className=\"text-sm text-text-secondary\">\n {tr('component.showingResults', 'Showing {{filtered}} of {{total}} components', {\n filtered: filteredComponents.length,\n total: components.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('common.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n )}\n\n {/* Grid */}\n {filteredComponents.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"component-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredComponents.map((component) => (\n <MotionItem key={component.id}>\n <ComponentCard component={component} onOpen={handleComponentSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Box className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyNoResults', 'No components found')\n : tr('component.emptyNoComponents', 'No components yet')\n }\n description={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'component.emptyCreate',\n 'Register your first component to start tracking your software catalog'\n )\n }\n action={\n !searchQuery && typeFilter === 'all' && lifecycleFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n <GetHelpButton prompt=\"What is a Component in VibeControls and how do I add one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n {/* Create Form */}\n <ComponentForm\n isOpen={showCreateForm}\n onClose={() => setShowCreateForm(false)}\n onSubmit={handleCreateComponent}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('component.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_COMPONENT\" onDraftChange={setDraftTagIds} />\n </div>\n }\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -507,7 +507,7 @@ function it() {
507
507
  autoStart: a,
508
508
  externalSessionName: n
509
509
  } } }), c = o.data?.createSession?.id;
510
- c && await tn(c), O(!1), M(b("sessions.createdSuccess", "Session created successfully")), setTimeout(() => M(null), 3e3), E === "terminal" && o.data?.createSession && Q({
510
+ O(!1), M(b("sessions.createdSuccess", "Session created successfully")), c && await tn(c), setTimeout(() => M(null), 3e3), E === "terminal" && o.data?.createSession && Q({
511
511
  id: o.data.createSession.id,
512
512
  workspaceId: o.data.createSession.workspaceId,
513
513
  vibeId: o.data.createSession.vibeId || null,