@cat-factory/app 0.311.2 → 0.312.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -4
- package/app/app.config.ts +7 -0
- package/app/components/assistant/AssistantModal.vue +2 -5
- package/app/components/auth/AuthGate.vue +2 -1
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +14 -15
- package/app/components/binaryOutput/StoredAssetView.vue +1 -3
- package/app/components/board/AddTaskModal.vue +3 -10
- package/app/components/board/AgentFailureCard.vue +4 -5
- package/app/components/board/AgentStopButton.vue +5 -7
- package/app/components/board/BoardCanvas.vue +31 -30
- package/app/components/board/CreateInitiativeModal.vue +2 -4
- package/app/components/board/RecurringPipelineModal.vue +2 -7
- package/app/components/board/nodes/AgentChip.vue +3 -2
- package/app/components/board/nodes/BlockNode.vue +14 -11
- package/app/components/board/nodes/InitiativeCard.vue +6 -3
- package/app/components/board/nodes/TaskCard.vue +45 -212
- package/app/components/bootstrap/AdoptionReviewModal.vue +2 -5
- package/app/components/bootstrap/BootstrapModal.vue +6 -15
- package/app/components/bootstrap/BootstrapRunSteps.vue +3 -1
- package/app/components/brainstorm/BrainstormWindow.vue +32 -26
- package/app/components/bugFishing/BugFishingWindow.vue +15 -13
- package/app/components/clarity/ClarityReviewWindow.vue +31 -26
- package/app/components/common/ClarificationItem.vue +3 -1
- package/app/components/common/EmptyState.vue +41 -19
- package/app/components/common/InterviewGateNotice.vue +3 -1
- package/app/components/common/Spinner.vue +28 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +5 -5
- package/app/components/docs/DocInterviewWindow.vue +6 -6
- package/app/components/documents/ContextDocumentPicker.vue +2 -2
- package/app/components/documents/DocumentImportModal.vue +9 -8
- package/app/components/documents/DocumentSourceConnectModal.vue +10 -10
- package/app/components/documents/DocumentTemplatesModal.vue +13 -7
- package/app/components/documents/RepoContextDocPicker.vue +2 -2
- package/app/components/documents/SpawnPreviewModal.vue +4 -6
- package/app/components/documents/StartFromDesignModal.vue +2 -1
- package/app/components/documents/TaskContextDocs.vue +4 -5
- package/app/components/environments/EnvironmentStatusPanel.vue +11 -8
- package/app/components/environments/steps/EnvPickStep.vue +7 -3
- package/app/components/environments/steps/EnvPreflightStep.vue +6 -5
- package/app/components/focus/BlockFocusView.vue +6 -5
- package/app/components/followUp/FollowUpWindow.vue +7 -9
- package/app/components/forkDecision/ForkDecisionWindow.vue +9 -8
- package/app/components/foundational/FoundationalServiceCatalogList.vue +9 -5
- package/app/components/foundational/FoundationalServiceRegistry.vue +11 -7
- package/app/components/foundational/FoundationalServiceSources.vue +20 -22
- package/app/components/foundational/FoundationalSuppressions.vue +2 -2
- package/app/components/foundational/ServiceCatalogConnection.vue +15 -15
- package/app/components/fragments/FragmentLibraryManager.vue +36 -34
- package/app/components/fragments/FragmentSelector.vue +7 -3
- package/app/components/gates/GateResultView.vue +6 -9
- package/app/components/github/AddServiceFromRepoModal.vue +6 -10
- package/app/components/github/GitHubConnect.vue +10 -17
- package/app/components/github/GitHubPanel.vue +44 -47
- package/app/components/github/RepoTreeBrowser.vue +9 -5
- package/app/components/guidedReview/GuidedReviewModal.vue +16 -6
- package/app/components/guidedReview/GuidedReviewOverview.vue +2 -1
- package/app/components/guidedReview/GuidedReviewPicker.vue +10 -7
- package/app/components/guidedReview/GuidedReviewThread.vue +9 -4
- package/app/components/humanTest/HumanTestWindow.vue +2 -1
- package/app/components/initiative/InitiativePlanningWindow.vue +6 -6
- package/app/components/initiative/InitiativeTrackerWindow.vue +11 -11
- package/app/components/judge/JudgeResultView.vue +2 -7
- package/app/components/kaizen/KaizenPanel.vue +20 -5
- package/app/components/kaizen/KaizenStepStatus.vue +2 -1
- package/app/components/layout/AccountAuditLog.vue +11 -3
- package/app/components/layout/AccountDeploymentSettings.vue +17 -62
- package/app/components/layout/AccountFailureKindRules.vue +6 -5
- package/app/components/layout/AccountModelPolicySettings.vue +2 -6
- package/app/components/layout/AccountPlatformAlertSettings.vue +5 -13
- package/app/components/layout/AccountRiskPolicySettings.vue +13 -14
- package/app/components/layout/AccountRunCredentialSettings.vue +2 -2
- package/app/components/layout/AccountTeamSettings.vue +12 -9
- package/app/components/layout/BoardSwitcher.vue +2 -2
- package/app/components/layout/BoardToolbar.vue +27 -37
- package/app/components/layout/BoardTopOverlays.vue +8 -2
- package/app/components/layout/CommandBar.vue +7 -3
- package/app/components/layout/ConnectionStatusBanner.vue +2 -1
- package/app/components/layout/IntegrationsHub.vue +7 -3
- package/app/components/layout/ModelProvidersHub.vue +7 -3
- package/app/components/layout/NotificationsInbox.vue +3 -11
- package/app/components/layout/SideBar.vue +5 -0
- package/app/components/layout/WorkViewSwitcher.vue +64 -0
- package/app/components/layout/WorkspaceMembersSettings.vue +12 -5
- package/app/components/media/ArtifactLightbox.vue +4 -3
- package/app/components/notifications/NotificationSettingsPanel.vue +4 -11
- package/app/components/observability/ToolCallList.vue +12 -17
- package/app/components/panels/AgentStepDetail.vue +5 -5
- package/app/components/panels/GenericStructuredResultView.vue +6 -9
- package/app/components/panels/InspectorPanel.vue +2 -1
- package/app/components/panels/MergerResultView.vue +5 -6
- package/app/components/panels/ObservabilityPanel.vue +26 -41
- package/app/components/panels/OperatorDashboardPanel.vue +29 -17
- package/app/components/panels/ReportsPanel.vue +17 -11
- package/app/components/panels/ReportsSpendBreakdown.vue +7 -3
- package/app/components/panels/RunDetailLoadState.vue +4 -3
- package/app/components/panels/StepContainerStatus.vue +4 -3
- package/app/components/panels/StepDelegatedStatus.vue +3 -2
- package/app/components/panels/StepMetadataCard.vue +6 -8
- package/app/components/panels/StepRunMeta.vue +2 -5
- package/app/components/panels/inspector/ContainerSummary.vue +7 -1
- package/app/components/panels/inspector/EpicChildren.vue +7 -3
- package/app/components/panels/inspector/FrontendConfig.vue +7 -3
- package/app/components/panels/inspector/InitiativeInspector.vue +5 -2
- package/app/components/panels/inspector/ServiceConnections.vue +7 -3
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -6
- package/app/components/panels/inspector/ServiceTestSecrets.vue +2 -6
- package/app/components/panels/inspector/ServiceTestingContext.vue +2 -6
- package/app/components/panels/inspector/ServiceValidationConfig.vue +38 -39
- package/app/components/panels/inspector/TaskExecution.vue +5 -6
- package/app/components/panels/inspector/TaskRunSettings.vue +7 -3
- package/app/components/panels/inspector/TaskStructure.vue +0 -1
- package/app/components/pipeline/AgentPromptEditor.vue +25 -18
- package/app/components/pipeline/PipelineBuilder.vue +14 -24
- package/app/components/pipeline/PipelineProgress.vue +16 -17
- package/app/components/prReview/PrReviewPhaseBadge.vue +3 -2
- package/app/components/prReview/PrReviewWindow.vue +16 -20
- package/app/components/providers/ApiKeysSection.vue +2 -6
- package/app/components/providers/PersonalCredentialModal.vue +2 -6
- package/app/components/providers/PersonalSubscriptionSection.vue +3 -7
- package/app/components/providers/VendorCredentialsModal.vue +2 -6
- package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -6
- package/app/components/queue/QueueCard.vue +269 -0
- package/app/components/queue/QueueView.vue +192 -0
- package/app/components/ralph/RalphLoopResultView.vue +2 -7
- package/app/components/requirements/RequirementsReviewWindow.vue +44 -39
- package/app/components/sandbox/SandboxPanel.vue +37 -25
- package/app/components/settings/AccountSettingsPanel.vue +6 -3
- package/app/components/settings/ApiTokensPanel.vue +2 -6
- package/app/components/settings/BudgetSettings.vue +2 -2
- package/app/components/settings/CapabilityCredentialsPanel.vue +2 -6
- package/app/components/settings/CloudflareHandlerSection.vue +3 -7
- package/app/components/settings/ConsensusGroupsSection.vue +7 -3
- package/app/components/settings/CustomManifestTypeEditor.vue +7 -3
- package/app/components/settings/DefaultProvisionTypeSection.vue +2 -2
- package/app/components/settings/InfraHandlersConfigurator.vue +8 -11
- package/app/components/settings/IssueTrackerPanel.vue +9 -9
- package/app/components/settings/LocalContainerPoolSettings.vue +2 -6
- package/app/components/settings/LocalModelEndpointsPanel.vue +5 -7
- package/app/components/settings/McpAuthorizeScreen.vue +8 -3
- package/app/components/settings/McpOAuthCallbackScreen.vue +2 -1
- package/app/components/settings/ModelConfigurationPanel.vue +45 -17
- package/app/components/settings/ObservabilityConnectionPanel.vue +4 -15
- package/app/components/settings/OpenRouterCatalogPanel.vue +8 -24
- package/app/components/settings/PackageRegistriesPanel.vue +2 -6
- package/app/components/settings/ProviderConnectionTab.vue +3 -7
- package/app/components/settings/RiskPolicyPanel.vue +4 -16
- package/app/components/settings/SharedStacksPanel.vue +8 -18
- package/app/components/settings/TaskTypeSuppressionsPanel.vue +10 -6
- package/app/components/settings/UsageSettings.vue +11 -2
- package/app/components/settings/UserSecretsSection.vue +5 -7
- package/app/components/settings/WorkspaceMetadataSettings.vue +9 -9
- package/app/components/settings/WorkspaceSettingsPanel.vue +3 -11
- package/app/components/skills/ReviewSkillQueue.vue +7 -3
- package/app/components/skills/SkillLibraryManager.vue +20 -18
- package/app/components/slack/SlackPanel.vue +5 -8
- package/app/components/spec/ServiceSpecWindow.vue +32 -29
- package/app/components/tasks/BugHuntModal.vue +23 -19
- package/app/components/tasks/ContextIssuePicker.vue +2 -2
- package/app/components/tasks/TaskContextIssues.vue +4 -5
- package/app/components/tasks/TaskImportModal.vue +10 -8
- package/app/components/tasks/TaskSourceConnectModal.vue +11 -11
- package/app/components/testing/TestReportWindow.vue +6 -12
- package/app/components/tutorial/TutorialCatalogue.vue +7 -3
- package/app/components/tutorial/TutorialOverlay.vue +2 -1
- package/app/components/tutorial/TutorialPrompt.vue +7 -3
- package/app/components/vcs/GitLabConnect.vue +2 -6
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +9 -4
- package/app/composables/toastTitleKeys.spec.ts +143 -0
- package/app/composables/useActionToast.spec.ts +71 -0
- package/app/composables/useActionToast.ts +101 -0
- package/app/composables/useBlockDeletion.ts +3 -6
- package/app/composables/useConfirmAction.ts +5 -3
- package/app/composables/useContextLinking.spec.ts +3 -0
- package/app/composables/useContextLinking.ts +11 -16
- package/app/composables/useCopyToClipboard.ts +4 -3
- package/app/composables/useFrameLanes.ts +6 -82
- package/app/composables/useNavContributions.ts +6 -5
- package/app/composables/usePipelineErrorToast.spec.ts +24 -0
- package/app/composables/usePipelineErrorToast.ts +26 -2
- package/app/composables/usePipelineLibraryActions.ts +8 -9
- package/app/composables/useTaskActions.ts +254 -0
- package/app/composables/useTaskLaneClassifier.ts +88 -0
- package/app/modular/nav-contributions.spec.ts +4 -0
- package/app/modular/nav-contributions.ts +31 -0
- package/app/modular/tutorial-tours.ts +10 -0
- package/app/pages/index.vue +42 -8
- package/app/stores/board/context.ts +4 -3
- package/app/stores/board/placement.ts +8 -20
- package/app/stores/board/removal.ts +4 -13
- package/app/stores/board.spec.ts +10 -1
- package/app/stores/board.ts +3 -2
- package/app/stores/workView.ts +51 -0
- package/app/stores/workspaceQueue.ts +148 -0
- package/app/utils/bootstrapSteps.ts +11 -2
- package/app/utils/pipelineRender.ts +21 -8
- package/app/utils/queueSections.spec.ts +111 -0
- package/app/utils/queueSections.ts +117 -0
- package/app/utils/workView.spec.ts +16 -0
- package/app/utils/workView.ts +25 -0
- package/i18n/locales/de.json +73 -32
- package/i18n/locales/en.json +73 -32
- package/i18n/locales/es.json +73 -32
- package/i18n/locales/fr.json +73 -32
- package/i18n/locales/he.json +73 -32
- package/i18n/locales/it.json +73 -32
- package/i18n/locales/ja.json +73 -32
- package/i18n/locales/pl.json +73 -32
- package/i18n/locales/tr.json +73 -32
- package/i18n/locales/uk.json +73 -32
- package/package.json +1 -1
|
@@ -41,7 +41,7 @@ const ui = useUiStore()
|
|
|
41
41
|
const github = useGitHubStore()
|
|
42
42
|
const board = useBoardStore()
|
|
43
43
|
const services = useServicesStore()
|
|
44
|
-
const
|
|
44
|
+
const actionToast = useActionToast()
|
|
45
45
|
const { present } = usePipelineErrorToast()
|
|
46
46
|
const { freeFramePosition, focusFrame } = useFramePlacement()
|
|
47
47
|
|
|
@@ -349,11 +349,8 @@ async function add() {
|
|
|
349
349
|
// Centre the camera on the newly imported service.
|
|
350
350
|
await focusFrame(block.id)
|
|
351
351
|
configuredBlockId.value = block.id
|
|
352
|
-
toast.
|
|
353
|
-
title: t('github.addService.toast.addedTitle'),
|
|
352
|
+
actionToast.success('github.addService.toast.addedTitle', {
|
|
354
353
|
description: t('github.addService.toast.addedDescription', { title: block.title }),
|
|
355
|
-
icon: 'i-lucide-check',
|
|
356
|
-
color: 'success',
|
|
357
354
|
})
|
|
358
355
|
} catch (e) {
|
|
359
356
|
present(e, 'github.addService.toast.addFailedTitle')
|
|
@@ -417,17 +414,16 @@ async function addServices() {
|
|
|
417
414
|
const lastBlockId = created.at(-1)?.blockId
|
|
418
415
|
if (lastBlockId) await focusFrame(lastBlockId)
|
|
419
416
|
selectedDirectories.value = []
|
|
420
|
-
|
|
421
|
-
title: t('github.addService.toast.servicesAddedTitle'),
|
|
417
|
+
const added = {
|
|
422
418
|
description: [
|
|
423
419
|
t('github.addService.toast.servicesAddedDescription', { count: dirs.length }, dirs.length),
|
|
424
420
|
frontendNote(designatedDirectory, wiringLanded),
|
|
425
421
|
]
|
|
426
422
|
.filter(Boolean)
|
|
427
423
|
.join(' '),
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
424
|
+
}
|
|
425
|
+
if (wiringLanded) actionToast.success('github.addService.toast.servicesAddedTitle', added)
|
|
426
|
+
else actionToast.warning('github.addService.toast.servicesAddedTitle', added)
|
|
431
427
|
} catch (e) {
|
|
432
428
|
present(e, 'github.addService.toast.addFailedTitle')
|
|
433
429
|
} finally {
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
// react to. Shared by the GitHub panel and the bootstrap modal so the connect
|
|
8
8
|
// flow lives in one place.
|
|
9
9
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
10
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
10
11
|
|
|
11
12
|
const { t } = useI18n()
|
|
12
13
|
const github = useGitHubStore()
|
|
13
|
-
const
|
|
14
|
+
const actionToast = useActionToast()
|
|
14
15
|
const { present } = usePipelineErrorToast()
|
|
15
16
|
|
|
16
17
|
const installing = ref(false)
|
|
@@ -48,11 +49,7 @@ async function connect(id: number, onDone?: () => void) {
|
|
|
48
49
|
try {
|
|
49
50
|
await github.connect(id)
|
|
50
51
|
onDone?.()
|
|
51
|
-
toast.
|
|
52
|
-
title: t('github.connect.toast.connected'),
|
|
53
|
-
icon: 'i-lucide-check',
|
|
54
|
-
color: 'success',
|
|
55
|
-
})
|
|
52
|
+
actionToast.success('github.connect.toast.connected')
|
|
56
53
|
} catch (e) {
|
|
57
54
|
present(e, 'github.connect.errors.connect')
|
|
58
55
|
} finally {
|
|
@@ -90,20 +87,16 @@ async function connectManually() {
|
|
|
90
87
|
</UButton>
|
|
91
88
|
</div>
|
|
92
89
|
|
|
93
|
-
<div
|
|
94
|
-
v-
|
|
95
|
-
class="flex items-center gap-2 py-3 text-sm text-muted"
|
|
96
|
-
>
|
|
97
|
-
<UIcon name="i-lucide-loader" class="h-4 w-4 animate-spin" />
|
|
98
|
-
{{ t('github.connect.lookingForInstallations') }}
|
|
90
|
+
<div v-if="github.loadingInstallations && !github.installations.length" class="space-y-2">
|
|
91
|
+
<USkeleton v-for="i in 2" :key="i" class="h-12 w-full" />
|
|
99
92
|
</div>
|
|
100
93
|
|
|
101
|
-
<
|
|
94
|
+
<EmptyState
|
|
102
95
|
v-else-if="!github.installations.length"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
compact
|
|
97
|
+
icon="i-lucide-github"
|
|
98
|
+
:title="t('github.connect.noInstallations')"
|
|
99
|
+
/>
|
|
107
100
|
|
|
108
101
|
<div
|
|
109
102
|
v-for="inst in github.installations"
|
|
@@ -12,12 +12,13 @@ import VcsConnectSurfaces from '~/components/vcs/VcsConnectSurfaces.vue'
|
|
|
12
12
|
import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
13
13
|
import { VCS_PROVIDER_ICONS, VCS_PROVIDER_LABELS } from '~/utils/vcs'
|
|
14
14
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
15
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
15
16
|
|
|
16
17
|
const { t } = useI18n()
|
|
17
18
|
const ui = useUiStore()
|
|
18
19
|
const access = useWorkspaceAccess()
|
|
19
20
|
const github = useGitHubStore()
|
|
20
|
-
const
|
|
21
|
+
const actionToast = useActionToast()
|
|
21
22
|
const { present } = usePipelineErrorToast()
|
|
22
23
|
const { confirm } = useConfirm()
|
|
23
24
|
|
|
@@ -80,6 +81,12 @@ const PULL_TERMS = {
|
|
|
80
81
|
},
|
|
81
82
|
} as const satisfies Record<VcsProvider, Record<string, string>>
|
|
82
83
|
const pullTerms = computed(() => PULL_TERMS[github.provider])
|
|
84
|
+
// Skeleton rows only while there is nothing to show yet. `github.loading` is also set by every
|
|
85
|
+
// refresh (after a resync, on re-open), and blanking lists the reader is looking at to skeletons
|
|
86
|
+
// and back loses their place for no gain.
|
|
87
|
+
const firstLoad = computed(
|
|
88
|
+
() => github.loading && !github.repos.length && !github.pulls.length && !github.issues.length,
|
|
89
|
+
)
|
|
83
90
|
|
|
84
91
|
// What the repo picker can offer, and why a repo might be missing from it, differ by how the
|
|
85
92
|
// workspace authenticates rather than by provider: an App installation is shared across the
|
|
@@ -110,9 +117,8 @@ async function disconnect() {
|
|
|
110
117
|
if (!ok) return
|
|
111
118
|
try {
|
|
112
119
|
await github.disconnect()
|
|
113
|
-
toast.
|
|
114
|
-
|
|
115
|
-
icon: 'i-lucide-unplug',
|
|
120
|
+
actionToast.success('vcs.panel.toast.disconnected', {
|
|
121
|
+
params: { provider: VCS_PROVIDER_LABELS[github.provider] },
|
|
116
122
|
})
|
|
117
123
|
} catch (e) {
|
|
118
124
|
present(e, 'github.panel.errors.disconnect')
|
|
@@ -122,11 +128,7 @@ async function disconnect() {
|
|
|
122
128
|
async function resync(full = false) {
|
|
123
129
|
try {
|
|
124
130
|
const { status } = await github.resync({ full })
|
|
125
|
-
toast.
|
|
126
|
-
title: t('github.panel.toast.resync', { status }),
|
|
127
|
-
icon: 'i-lucide-refresh-cw',
|
|
128
|
-
color: 'info',
|
|
129
|
-
})
|
|
131
|
+
actionToast.info('github.panel.toast.resync', { params: { status } })
|
|
130
132
|
} catch (e) {
|
|
131
133
|
present(e, 'github.panel.errors.resync')
|
|
132
134
|
}
|
|
@@ -168,11 +170,7 @@ async function saveRepos() {
|
|
|
168
170
|
try {
|
|
169
171
|
await github.setLinkedRepos([...selected.value])
|
|
170
172
|
managing.value = false
|
|
171
|
-
toast.
|
|
172
|
-
title: t('github.panel.toast.reposUpdated'),
|
|
173
|
-
icon: 'i-lucide-check',
|
|
174
|
-
color: 'success',
|
|
175
|
-
})
|
|
173
|
+
actionToast.success('github.panel.toast.reposUpdated')
|
|
176
174
|
} catch (e) {
|
|
177
175
|
present(e, 'github.panel.errors.updateRepos')
|
|
178
176
|
}
|
|
@@ -207,11 +205,7 @@ async function createBranch(repo: GitHubRepo) {
|
|
|
207
205
|
try {
|
|
208
206
|
await github.createBranch(repo.githubId, { name, fromSha })
|
|
209
207
|
branchForm.value = { name: '', fromSha: '' }
|
|
210
|
-
toast.
|
|
211
|
-
title: t('github.panel.toast.branchCreated', { name }),
|
|
212
|
-
icon: 'i-lucide-check',
|
|
213
|
-
color: 'success',
|
|
214
|
-
})
|
|
208
|
+
actionToast.success('github.panel.toast.branchCreated', { params: { name } })
|
|
215
209
|
} catch (e) {
|
|
216
210
|
present(e, 'github.panel.errors.createBranch')
|
|
217
211
|
} finally {
|
|
@@ -259,7 +253,7 @@ async function openPr() {
|
|
|
259
253
|
})
|
|
260
254
|
showPrForm.value = false
|
|
261
255
|
prForm.value = { repoGithubId: null, title: '', head: '', base: '' }
|
|
262
|
-
|
|
256
|
+
actionToast.success(pullTerms.value.opened)
|
|
263
257
|
} catch (e) {
|
|
264
258
|
present(e, pullTerms.value.openFailed)
|
|
265
259
|
} finally {
|
|
@@ -283,11 +277,7 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
283
277
|
merging.value = pr.number
|
|
284
278
|
try {
|
|
285
279
|
await github.mergePullRequest(pr.repoGithubId, pr.number, { method: 'squash' })
|
|
286
|
-
|
|
287
|
-
title: t(pullTerms.value.mergedToast, { number: pr.number }),
|
|
288
|
-
icon: 'i-lucide-git-merge',
|
|
289
|
-
color: 'success',
|
|
290
|
-
})
|
|
280
|
+
actionToast.success(pullTerms.value.mergedToast, { params: { number: pr.number } })
|
|
291
281
|
} catch (e) {
|
|
292
282
|
present(e, 'github.panel.errors.merge')
|
|
293
283
|
} finally {
|
|
@@ -372,9 +362,8 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
372
362
|
</UButton>
|
|
373
363
|
</div>
|
|
374
364
|
|
|
375
|
-
<div v-if="
|
|
376
|
-
<
|
|
377
|
-
{{ t('github.panel.loading') }}
|
|
365
|
+
<div v-if="firstLoad" class="space-y-2">
|
|
366
|
+
<USkeleton v-for="i in 3" :key="i" class="h-12 w-full" />
|
|
378
367
|
</div>
|
|
379
368
|
|
|
380
369
|
<!-- repositories -->
|
|
@@ -405,20 +394,19 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
405
394
|
isAppConnection ? t('vcs.panel.manageHintApp') : t('vcs.panel.manageHintToken')
|
|
406
395
|
}}
|
|
407
396
|
</p>
|
|
408
|
-
<div
|
|
409
|
-
v-
|
|
410
|
-
class="flex items-center gap-2 py-3 text-sm text-muted"
|
|
411
|
-
>
|
|
412
|
-
<UIcon name="i-lucide-loader" class="h-4 w-4 animate-spin" />
|
|
413
|
-
{{ t('github.panel.loadingRepos') }}
|
|
397
|
+
<div v-if="github.loadingAvailable" class="space-y-1">
|
|
398
|
+
<USkeleton v-for="i in 4" :key="i" class="h-8 w-full" />
|
|
414
399
|
</div>
|
|
415
|
-
<
|
|
416
|
-
|
|
400
|
+
<EmptyState
|
|
401
|
+
v-else-if="!github.availableRepos.length"
|
|
402
|
+
compact
|
|
403
|
+
icon="i-lucide-folder-git-2"
|
|
404
|
+
:title="
|
|
417
405
|
isAppConnection
|
|
418
406
|
? t('vcs.panel.noAvailableReposApp')
|
|
419
407
|
: t('vcs.panel.noAvailableReposToken')
|
|
420
|
-
|
|
421
|
-
|
|
408
|
+
"
|
|
409
|
+
/>
|
|
422
410
|
<div v-else class="max-h-64 space-y-1 overflow-y-auto">
|
|
423
411
|
<UButton
|
|
424
412
|
color="neutral"
|
|
@@ -463,9 +451,12 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
463
451
|
</div>
|
|
464
452
|
</div>
|
|
465
453
|
|
|
466
|
-
<
|
|
467
|
-
|
|
468
|
-
|
|
454
|
+
<EmptyState
|
|
455
|
+
v-if="!github.repos.length && !managing"
|
|
456
|
+
compact
|
|
457
|
+
icon="i-lucide-folder-git-2"
|
|
458
|
+
:title="t('github.panel.noLinkedRepos')"
|
|
459
|
+
/>
|
|
469
460
|
|
|
470
461
|
<!-- Whether the host actually protects what an agent run pushes to. Placed above the
|
|
471
462
|
repo list because it is a posture check across ALL of them, not a per-repo
|
|
@@ -634,9 +625,12 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
634
625
|
</div>
|
|
635
626
|
</div>
|
|
636
627
|
|
|
637
|
-
<
|
|
638
|
-
|
|
639
|
-
|
|
628
|
+
<EmptyState
|
|
629
|
+
v-if="!github.pulls.length"
|
|
630
|
+
compact
|
|
631
|
+
icon="i-lucide-git-pull-request"
|
|
632
|
+
:title="t(pullTerms.none)"
|
|
633
|
+
/>
|
|
640
634
|
<div
|
|
641
635
|
v-for="pr in github.pulls"
|
|
642
636
|
:key="`${pr.repoGithubId}-${pr.number}`"
|
|
@@ -681,9 +675,12 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
681
675
|
|
|
682
676
|
<!-- issues -->
|
|
683
677
|
<section v-else class="space-y-2">
|
|
684
|
-
<
|
|
685
|
-
|
|
686
|
-
|
|
678
|
+
<EmptyState
|
|
679
|
+
v-if="!github.issues.length"
|
|
680
|
+
compact
|
|
681
|
+
icon="i-lucide-circle-dot"
|
|
682
|
+
:title="t('github.panel.noIssues')"
|
|
683
|
+
/>
|
|
687
684
|
<div
|
|
688
685
|
v-for="issue in github.issues"
|
|
689
686
|
:key="`${issue.repoGithubId}-${issue.number}`"
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
// service-directory field). Nothing in the tree can be the target then, so the pick is the
|
|
20
20
|
// folder the caller is standing in and the emitted value is that folder plus the new name.
|
|
21
21
|
import type { RepoTreeEntry } from '~/types/domain'
|
|
22
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
22
23
|
|
|
23
24
|
const props = withDefaults(
|
|
24
25
|
defineProps<{
|
|
@@ -194,12 +195,15 @@ watch(
|
|
|
194
195
|
|
|
195
196
|
<!-- listing -->
|
|
196
197
|
<div class="max-h-56 overflow-auto rounded-sm border border-default">
|
|
197
|
-
<div v-if="loading" class="
|
|
198
|
-
|
|
199
|
-
</div>
|
|
200
|
-
<div v-else-if="isEmpty" class="p-3 text-sm text-muted">
|
|
201
|
-
{{ mode === 'dir' ? t('github.repoTree.noSubdirectories') : t('github.repoTree.empty') }}
|
|
198
|
+
<div v-if="loading" class="space-y-1 p-2">
|
|
199
|
+
<USkeleton v-for="i in 4" :key="i" class="h-7 w-full" />
|
|
202
200
|
</div>
|
|
201
|
+
<EmptyState
|
|
202
|
+
v-else-if="isEmpty"
|
|
203
|
+
compact
|
|
204
|
+
icon="i-lucide-folder-open"
|
|
205
|
+
:title="mode === 'dir' ? t('github.repoTree.noSubdirectories') : t('github.repoTree.empty')"
|
|
206
|
+
/>
|
|
203
207
|
<ul v-else class="divide-y divide-default">
|
|
204
208
|
<li
|
|
205
209
|
v-for="entry in dirEntries"
|
|
@@ -7,6 +7,7 @@ import type { OpenGuidedReviewInput } from '~/types/domain'
|
|
|
7
7
|
import GuidedReviewOverview from '~/components/guidedReview/GuidedReviewOverview.vue'
|
|
8
8
|
import GuidedReviewThread from '~/components/guidedReview/GuidedReviewThread.vue'
|
|
9
9
|
import GuidedReviewPicker from '~/components/guidedReview/GuidedReviewPicker.vue'
|
|
10
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
10
11
|
import {
|
|
11
12
|
NEW_THREAD_TAB,
|
|
12
13
|
isStale,
|
|
@@ -175,10 +176,16 @@ const title = computed(() =>
|
|
|
175
176
|
@open="openFor"
|
|
176
177
|
/>
|
|
177
178
|
|
|
178
|
-
<
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
<div v-else-if="!session" class="flex h-full min-h-0 flex-col gap-4 lg:flex-row">
|
|
180
|
+
<div class="space-y-3 lg:w-2/5 lg:pr-2">
|
|
181
|
+
<USkeleton class="h-6 w-2/3" />
|
|
182
|
+
<USkeleton v-for="n in 4" :key="n" class="h-4 w-full" />
|
|
183
|
+
</div>
|
|
184
|
+
<div class="flex flex-1 flex-col gap-3">
|
|
185
|
+
<USkeleton class="h-8 w-1/2" />
|
|
186
|
+
<USkeleton class="h-48 w-full rounded-lg" />
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
182
189
|
|
|
183
190
|
<div v-else class="flex h-full min-h-0 flex-col gap-4 lg:flex-row">
|
|
184
191
|
<div class="min-h-0 space-y-3 overflow-y-auto lg:w-2/5 lg:pr-2">
|
|
@@ -210,11 +217,14 @@ const title = computed(() =>
|
|
|
210
217
|
:aria-selected="tab === activeTab"
|
|
211
218
|
:variant="tab === activeTab ? 'soft' : 'ghost'"
|
|
212
219
|
:color="tab === activeTab ? 'primary' : 'neutral'"
|
|
213
|
-
:icon="threadBusy(tab) ? 'i-lucide-loader-circle' : undefined"
|
|
214
|
-
:ui="{ leadingIcon: threadBusy(tab) ? 'animate-spin' : '' }"
|
|
215
220
|
:data-testid="`guided-review-tab-${tab}`"
|
|
216
221
|
@click="activeTab = tab"
|
|
217
222
|
>
|
|
223
|
+
<!-- Not `:loading`: that disables the tab, and a reader switches back to a thread
|
|
224
|
+
precisely while its reply is pending. -->
|
|
225
|
+
<template v-if="threadBusy(tab)" #leading>
|
|
226
|
+
<Spinner class="size-4 shrink-0" />
|
|
227
|
+
</template>
|
|
218
228
|
<span class="max-w-48 truncate">{{ threadTitle(tab) }}</span>
|
|
219
229
|
</UButton>
|
|
220
230
|
<UButton
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// chips the window turns into new threads.
|
|
4
4
|
import type { GuidedReviewSession } from '~/types/domain'
|
|
5
5
|
import MarkdownProse from '~/components/common/MarkdownProse.vue'
|
|
6
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
6
7
|
import GuidedReviewFailure from '~/components/guidedReview/GuidedReviewFailure.vue'
|
|
7
8
|
import { citationLabel } from '~/components/guidedReview/GuidedReview.logic'
|
|
8
9
|
|
|
@@ -53,7 +54,7 @@ const SEVERITY_COLOR = { high: 'error', medium: 'warning', low: 'neutral' } as c
|
|
|
53
54
|
class="flex items-center gap-2 text-sm text-muted"
|
|
54
55
|
data-testid="guided-review-overview-working"
|
|
55
56
|
>
|
|
56
|
-
<
|
|
57
|
+
<Spinner class="h-4 w-4" />
|
|
57
58
|
{{ t('guidedReview.overview.working') }}
|
|
58
59
|
</p>
|
|
59
60
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Pick a linked repository and one of its pull requests (an open one from the synced list, or any
|
|
3
3
|
// number typed in) to open a guided review on.
|
|
4
4
|
import type { OpenGuidedReviewInput } from '~/types/domain'
|
|
5
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
5
6
|
|
|
6
7
|
defineProps<{ opening: boolean; loading: boolean }>()
|
|
7
8
|
const emit = defineEmits<{ open: [target: OpenGuidedReviewInput] }>()
|
|
@@ -34,13 +35,15 @@ function submit(number = prNumber.value): void {
|
|
|
34
35
|
<template>
|
|
35
36
|
<div class="mx-auto max-w-xl space-y-4" data-testid="guided-review-picker">
|
|
36
37
|
<p class="text-sm text-muted">{{ t('guidedReview.picker.intro') }}</p>
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
</
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
<div v-if="loading" class="space-y-4">
|
|
39
|
+
<USkeleton class="h-8 w-full" />
|
|
40
|
+
<USkeleton class="h-8 w-full" />
|
|
41
|
+
</div>
|
|
42
|
+
<EmptyState
|
|
43
|
+
v-else-if="!github.repos.length"
|
|
44
|
+
icon="i-lucide-folder-git-2"
|
|
45
|
+
:title="t('guidedReview.picker.noRepos')"
|
|
46
|
+
/>
|
|
44
47
|
<template v-else>
|
|
45
48
|
<UFormField :label="t('guidedReview.picker.repo')">
|
|
46
49
|
<USelectMenu
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { isPostableDraft } from '@cat-factory/contracts'
|
|
5
5
|
import type { GuidedReviewCommentDraft, GuidedReviewThreadView } from '~/types/domain'
|
|
6
6
|
import MarkdownProse from '~/components/common/MarkdownProse.vue'
|
|
7
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
8
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
7
9
|
import GuidedReviewFailure from '~/components/guidedReview/GuidedReviewFailure.vue'
|
|
8
10
|
import GuidedReviewDraftCard from '~/components/guidedReview/GuidedReviewDraftCard.vue'
|
|
9
11
|
import {
|
|
@@ -112,9 +114,12 @@ async function draftComments(): Promise<void> {
|
|
|
112
114
|
<template>
|
|
113
115
|
<div class="flex h-full min-h-0 flex-col gap-3" data-testid="guided-review-thread">
|
|
114
116
|
<div class="min-h-0 flex-1 space-y-3 overflow-y-auto pr-1">
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
<EmptyState
|
|
118
|
+
v-if="!messages.length"
|
|
119
|
+
compact
|
|
120
|
+
icon="i-lucide-message-circle-question"
|
|
121
|
+
:title="t('guidedReview.thread.empty')"
|
|
122
|
+
/>
|
|
118
123
|
<div
|
|
119
124
|
v-for="m in messages"
|
|
120
125
|
:key="m.id"
|
|
@@ -129,7 +134,7 @@ async function draftComments(): Promise<void> {
|
|
|
129
134
|
<p class="whitespace-pre-wrap">{{ m.content }}</p>
|
|
130
135
|
</template>
|
|
131
136
|
<p v-else-if="isLive(m)" class="flex items-center gap-2 text-muted">
|
|
132
|
-
<
|
|
137
|
+
<Spinner class="h-4 w-4" />
|
|
133
138
|
{{
|
|
134
139
|
m.depth === 'deep'
|
|
135
140
|
? t('guidedReview.thread.investigating')
|
|
@@ -16,6 +16,7 @@ type HumanTestRoundOutcome = NonNullable<HumanTestRound['outcome']>
|
|
|
16
16
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
17
17
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
18
18
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
19
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
19
20
|
|
|
20
21
|
const board = useBoardStore()
|
|
21
22
|
const execution = useExecutionStore()
|
|
@@ -253,7 +254,7 @@ const canDestroy = computed(
|
|
|
253
254
|
v-if="working"
|
|
254
255
|
class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-xs text-toned"
|
|
255
256
|
>
|
|
256
|
-
<
|
|
257
|
+
<Spinner class="h-3.5 w-3.5 text-app-warning-300" />
|
|
257
258
|
{{ phase ? t(PHASE_LABEL[phase]) : '' }}
|
|
258
259
|
</p>
|
|
259
260
|
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
} from '~/utils/interviewGate'
|
|
38
38
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
39
39
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
40
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
40
41
|
|
|
41
42
|
const board = useBoardStore()
|
|
42
43
|
const initiatives = useInitiativesStore()
|
|
@@ -212,13 +213,12 @@ async function onDiscard() {
|
|
|
212
213
|
<div class="flex min-h-0 flex-1">
|
|
213
214
|
<div class="min-w-0 flex-1 overflow-y-auto px-5 py-4">
|
|
214
215
|
<!-- No entity yet -->
|
|
215
|
-
<
|
|
216
|
+
<EmptyState
|
|
216
217
|
v-if="!initiative"
|
|
217
|
-
class="
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
</div>
|
|
218
|
+
class="h-full"
|
|
219
|
+
icon="i-lucide-messages-square"
|
|
220
|
+
:title="t('initiative.planning.empty')"
|
|
221
|
+
/>
|
|
222
222
|
|
|
223
223
|
<template v-else>
|
|
224
224
|
<p class="mb-4 text-sm leading-relaxed text-toned">
|
|
@@ -31,6 +31,7 @@ import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
|
31
31
|
import InitiativePlanReview from '~/components/initiative/InitiativePlanReview.vue'
|
|
32
32
|
import InitiativePlanNotice from '~/components/initiative/InitiativePlanNotice.vue'
|
|
33
33
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
34
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
34
35
|
|
|
35
36
|
const board = useBoardStore()
|
|
36
37
|
const initiatives = useInitiativesStore()
|
|
@@ -373,14 +374,12 @@ const { requestClose } = useUnsavedGuard({
|
|
|
373
374
|
<!-- No entity yet (module unwired / still creating). Centred in the column when it is the
|
|
374
375
|
only thing in it; merely inset when the notice above it means `h-full` would overflow
|
|
375
376
|
the scroller by the notice's own height. -->
|
|
376
|
-
<
|
|
377
|
+
<EmptyState
|
|
377
378
|
v-if="!initiative"
|
|
378
|
-
class="flex flex-col items-center justify-center gap-2 text-center text-muted"
|
|
379
379
|
:class="planApproval ? 'py-16' : 'h-full'"
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
</div>
|
|
380
|
+
icon="i-lucide-milestone"
|
|
381
|
+
:title="t('initiative.tracker.empty')"
|
|
382
|
+
/>
|
|
384
383
|
|
|
385
384
|
<template v-else>
|
|
386
385
|
<!-- Paused at a phase checkpoint (D2): a completed checkpoint phase is awaiting
|
|
@@ -467,12 +466,13 @@ const { requestClose } = useUnsavedGuard({
|
|
|
467
466
|
</section>
|
|
468
467
|
|
|
469
468
|
<!-- Awaiting planning -->
|
|
470
|
-
<
|
|
469
|
+
<EmptyState
|
|
471
470
|
v-if="phases.length === 0"
|
|
472
|
-
class="mb-4
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
471
|
+
class="mb-4"
|
|
472
|
+
compact
|
|
473
|
+
icon="i-lucide-list-todo"
|
|
474
|
+
:title="t('initiative.tracker.noPlan')"
|
|
475
|
+
/>
|
|
476
476
|
|
|
477
477
|
<!-- Phases + items -->
|
|
478
478
|
<section v-for="phase in phases" :key="phase.id" class="mb-5">
|
|
@@ -14,6 +14,7 @@ import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
|
14
14
|
import CopyButton from '~/components/common/CopyButton.vue'
|
|
15
15
|
import MarkdownProse from '~/components/common/MarkdownProse.vue'
|
|
16
16
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
17
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
17
18
|
|
|
18
19
|
const board = useBoardStore()
|
|
19
20
|
const execution = useExecutionStore()
|
|
@@ -149,13 +150,7 @@ async function act(choice: 'proceed' | 'bounce' | 'stop') {
|
|
|
149
150
|
</template>
|
|
150
151
|
|
|
151
152
|
<div class="min-w-0 flex-1 overflow-y-auto px-5 py-4">
|
|
152
|
-
<
|
|
153
|
-
v-if="!judge"
|
|
154
|
-
class="flex h-full flex-col items-center justify-center gap-2 text-center text-muted"
|
|
155
|
-
>
|
|
156
|
-
<UIcon :name="meta.icon" class="h-8 w-8 opacity-40" />
|
|
157
|
-
<p class="text-sm">{{ t('judge.empty') }}</p>
|
|
158
|
-
</div>
|
|
153
|
+
<EmptyState v-if="!judge" :icon="meta.icon" :title="t('judge.empty')" />
|
|
159
154
|
|
|
160
155
|
<template v-else>
|
|
161
156
|
<!-- The score against the task's threshold — the whole verdict in one line. -->
|
|
@@ -4,6 +4,7 @@ import { onKeyStroke } from '@vueuse/core'
|
|
|
4
4
|
import type { TableColumn } from '@nuxt/ui'
|
|
5
5
|
import type { KaizenGrading } from '~/types/domain'
|
|
6
6
|
import { agentKindMeta } from '~/utils/catalog'
|
|
7
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
7
8
|
import { FULL_BLEED_TAKEOVER_CLASS } from '~/utils/safeArea'
|
|
8
9
|
|
|
9
10
|
// The Kaizen screen: a full-panel overlay listing the workspace's grading history and
|
|
@@ -123,7 +124,16 @@ const historyColumns = computed<TableColumn<KaizenGrading>[]>(() => [
|
|
|
123
124
|
<p class="mb-3 text-2xs text-dimmed">
|
|
124
125
|
{{ t('kaizen.verifiedCombos.hint') }}
|
|
125
126
|
</p>
|
|
126
|
-
<
|
|
127
|
+
<div v-if="kaizen.loadingOverview && kaizen.verified.length === 0" class="space-y-2">
|
|
128
|
+
<USkeleton v-for="n in 3" :key="n" class="h-14 w-full rounded-lg" />
|
|
129
|
+
</div>
|
|
130
|
+
<EmptyState
|
|
131
|
+
v-else-if="kaizen.verified.length === 0"
|
|
132
|
+
compact
|
|
133
|
+
icon="i-lucide-badge-check"
|
|
134
|
+
:title="t('kaizen.verifiedCombos.empty')"
|
|
135
|
+
/>
|
|
136
|
+
<ul v-else class="space-y-2">
|
|
127
137
|
<li
|
|
128
138
|
v-for="c in kaizen.verified"
|
|
129
139
|
:key="c.comboKey"
|
|
@@ -156,9 +166,6 @@ const historyColumns = computed<TableColumn<KaizenGrading>[]>(() => [
|
|
|
156
166
|
}}
|
|
157
167
|
</div>
|
|
158
168
|
</li>
|
|
159
|
-
<li v-if="kaizen.verified.length === 0" class="text-xs text-app-600">
|
|
160
|
-
{{ t('kaizen.verifiedCombos.empty') }}
|
|
161
|
-
</li>
|
|
162
169
|
</ul>
|
|
163
170
|
</section>
|
|
164
171
|
|
|
@@ -171,6 +178,7 @@ const historyColumns = computed<TableColumn<KaizenGrading>[]>(() => [
|
|
|
171
178
|
<UTable
|
|
172
179
|
:data="kaizen.history"
|
|
173
180
|
:columns="historyColumns"
|
|
181
|
+
:loading="kaizen.loadingOverview"
|
|
174
182
|
:ui="{
|
|
175
183
|
root: 'overflow-hidden rounded-lg border border-default',
|
|
176
184
|
base: 'text-xs',
|
|
@@ -220,8 +228,15 @@ const historyColumns = computed<TableColumn<KaizenGrading>[]>(() => [
|
|
|
220
228
|
</span>
|
|
221
229
|
</div>
|
|
222
230
|
</template>
|
|
231
|
+
<!-- UTable draws its loading row only through this slot; without it an empty first
|
|
232
|
+
load falls through to #empty and claims there is no history. -->
|
|
233
|
+
<template #loading>
|
|
234
|
+
<div class="space-y-2">
|
|
235
|
+
<USkeleton v-for="n in 3" :key="n" class="h-8 w-full" />
|
|
236
|
+
</div>
|
|
237
|
+
</template>
|
|
223
238
|
<template #empty>
|
|
224
|
-
<
|
|
239
|
+
<EmptyState compact icon="i-lucide-history" :title="t('kaizen.history.empty')" />
|
|
225
240
|
</template>
|
|
226
241
|
</UTable>
|
|
227
242
|
</section>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, watch } from 'vue'
|
|
3
3
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
4
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
4
5
|
|
|
5
6
|
// Per-step Kaizen grading status, shown inside the run window (NOT on the board). Reads
|
|
6
7
|
// the grading for this run's step from the kaizen store, lazily loading the run's
|
|
@@ -54,7 +55,7 @@ const tone = computed(() => {
|
|
|
54
55
|
<span class="text-muted">{{ t('kaizen.status.scheduled') }}</span>
|
|
55
56
|
</template>
|
|
56
57
|
<template v-else-if="grading.status === 'running'">
|
|
57
|
-
<
|
|
58
|
+
<Spinner class="h-3.5 w-3.5 text-app-hue-teal" />
|
|
58
59
|
<span class="text-app-hue-teal">{{ t('kaizen.status.grading') }}</span>
|
|
59
60
|
</template>
|
|
60
61
|
<template v-else-if="grading.status === 'failed'">
|