@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
|
@@ -3,6 +3,7 @@ import { computed, onMounted, ref, watch } from 'vue'
|
|
|
3
3
|
import { apiErrorEnvelope } from '~/composables/api/errors'
|
|
4
4
|
import type { AuditEventWire } from '@cat-factory/contracts'
|
|
5
5
|
import { actorLabel, describeEvent } from './AccountAuditLog.logic'
|
|
6
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
6
7
|
|
|
7
8
|
// The account audit log: who did what, when, for the privileged actions an account admin is
|
|
8
9
|
// answerable for. Read-only by construction — the store exposes no mutation and the backend has
|
|
@@ -109,9 +110,16 @@ function timestamp(at: number): string {
|
|
|
109
110
|
<span class="text-muted">{{ loadError }}</span>
|
|
110
111
|
</p>
|
|
111
112
|
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
</
|
|
113
|
+
<div v-else-if="events.length === 0 && loading" class="space-y-2">
|
|
114
|
+
<USkeleton v-for="i in 3" :key="i" class="h-14 w-full" />
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<EmptyState
|
|
118
|
+
v-else-if="events.length === 0"
|
|
119
|
+
compact
|
|
120
|
+
icon="i-lucide-scroll-text"
|
|
121
|
+
:title="t('layout.auditLog.empty')"
|
|
122
|
+
/>
|
|
115
123
|
|
|
116
124
|
<ol v-else class="space-y-2" data-testid="audit-log-list">
|
|
117
125
|
<li
|
|
@@ -13,7 +13,7 @@ const props = defineProps<{ accountId: string }>()
|
|
|
13
13
|
|
|
14
14
|
const store = useAccountSettingsStore()
|
|
15
15
|
const ui = useUiStore()
|
|
16
|
-
const
|
|
16
|
+
const actionToast = useActionToast()
|
|
17
17
|
const { present } = usePipelineErrorToast()
|
|
18
18
|
const { t } = useI18n()
|
|
19
19
|
const { confirmAction } = useConfirmAction()
|
|
@@ -178,10 +178,7 @@ async function saveStorage() {
|
|
|
178
178
|
}
|
|
179
179
|
if (backend === 's3') {
|
|
180
180
|
if (!cs.region.trim() || !cs.bucket.trim()) {
|
|
181
|
-
|
|
182
|
-
title: t('layout.accountDeployment.contentStorage.regionBucketValidation'),
|
|
183
|
-
color: 'error',
|
|
184
|
-
})
|
|
181
|
+
actionToast.error('layout.accountDeployment.contentStorage.regionBucketValidation')
|
|
185
182
|
return
|
|
186
183
|
}
|
|
187
184
|
config.s3 = {
|
|
@@ -203,16 +200,10 @@ async function saveStorage() {
|
|
|
203
200
|
if (id && key) {
|
|
204
201
|
input.secrets = { s3: { accessKeyId: id, secretAccessKey: key } }
|
|
205
202
|
} else if (id || key) {
|
|
206
|
-
|
|
207
|
-
title: t('layout.accountDeployment.contentStorage.bothKeysValidation'),
|
|
208
|
-
color: 'error',
|
|
209
|
-
})
|
|
203
|
+
actionToast.error('layout.accountDeployment.contentStorage.bothKeysValidation')
|
|
210
204
|
return
|
|
211
205
|
} else if (!storageSummary.value?.s3CredentialsConfigured) {
|
|
212
|
-
|
|
213
|
-
title: t('layout.accountDeployment.contentStorage.keysValidation'),
|
|
214
|
-
color: 'error',
|
|
215
|
-
})
|
|
206
|
+
actionToast.error('layout.accountDeployment.contentStorage.keysValidation')
|
|
216
207
|
return
|
|
217
208
|
}
|
|
218
209
|
// else: keys already stored and none re-entered → leave them unchanged.
|
|
@@ -224,11 +215,7 @@ async function saveStorage() {
|
|
|
224
215
|
try {
|
|
225
216
|
await store.save(props.accountId, input)
|
|
226
217
|
hydrateStorage()
|
|
227
|
-
|
|
228
|
-
title: t('layout.accountDeployment.contentStorage.saved'),
|
|
229
|
-
icon: 'i-lucide-check',
|
|
230
|
-
color: 'success',
|
|
231
|
-
})
|
|
218
|
+
actionToast.success('layout.accountDeployment.contentStorage.saved')
|
|
232
219
|
} catch (e) {
|
|
233
220
|
present(e, 'layout.accountDeployment.contentStorage.saveFailed')
|
|
234
221
|
} finally {
|
|
@@ -238,7 +225,7 @@ async function saveStorage() {
|
|
|
238
225
|
|
|
239
226
|
async function saveSlack() {
|
|
240
227
|
if (!slack.clientId.trim() || !slack.clientSecret.trim() || !slack.redirectUrl.trim()) {
|
|
241
|
-
|
|
228
|
+
actionToast.error('layout.accountDeployment.slack.validation')
|
|
242
229
|
return
|
|
243
230
|
}
|
|
244
231
|
savingSlack.value = true
|
|
@@ -255,11 +242,7 @@ async function saveSlack() {
|
|
|
255
242
|
slack.clientId = ''
|
|
256
243
|
slack.clientSecret = ''
|
|
257
244
|
slack.redirectUrl = ''
|
|
258
|
-
|
|
259
|
-
title: t('layout.accountDeployment.slack.saved'),
|
|
260
|
-
icon: 'i-lucide-check',
|
|
261
|
-
color: 'success',
|
|
262
|
-
})
|
|
245
|
+
actionToast.success('layout.accountDeployment.slack.saved')
|
|
263
246
|
} catch (e) {
|
|
264
247
|
present(e, 'layout.accountDeployment.slack.saveFailed')
|
|
265
248
|
} finally {
|
|
@@ -272,11 +255,7 @@ async function clearSlack() {
|
|
|
272
255
|
savingSlack.value = true
|
|
273
256
|
try {
|
|
274
257
|
await store.save(props.accountId, { secrets: { slackOAuth: null } })
|
|
275
|
-
|
|
276
|
-
title: t('layout.accountDeployment.slack.cleared'),
|
|
277
|
-
icon: 'i-lucide-check',
|
|
278
|
-
color: 'success',
|
|
279
|
-
})
|
|
258
|
+
actionToast.success('layout.accountDeployment.slack.cleared')
|
|
280
259
|
} catch (e) {
|
|
281
260
|
present(e, 'layout.accountDeployment.slack.clearFailed')
|
|
282
261
|
} finally {
|
|
@@ -286,7 +265,7 @@ async function clearSlack() {
|
|
|
286
265
|
|
|
287
266
|
async function saveLinear() {
|
|
288
267
|
if (!linear.clientId.trim() || !linear.clientSecret.trim() || !linear.redirectUrl.trim()) {
|
|
289
|
-
|
|
268
|
+
actionToast.error('layout.accountDeployment.linear.validation')
|
|
290
269
|
return
|
|
291
270
|
}
|
|
292
271
|
savingLinear.value = true
|
|
@@ -303,11 +282,7 @@ async function saveLinear() {
|
|
|
303
282
|
linear.clientId = ''
|
|
304
283
|
linear.clientSecret = ''
|
|
305
284
|
linear.redirectUrl = ''
|
|
306
|
-
|
|
307
|
-
title: t('layout.accountDeployment.linear.saved'),
|
|
308
|
-
icon: 'i-lucide-check',
|
|
309
|
-
color: 'success',
|
|
310
|
-
})
|
|
285
|
+
actionToast.success('layout.accountDeployment.linear.saved')
|
|
311
286
|
} catch (e) {
|
|
312
287
|
present(e, 'layout.accountDeployment.linear.saveFailed')
|
|
313
288
|
} finally {
|
|
@@ -320,11 +295,7 @@ async function clearLinear() {
|
|
|
320
295
|
savingLinear.value = true
|
|
321
296
|
try {
|
|
322
297
|
await store.save(props.accountId, { secrets: { linearOAuth: null } })
|
|
323
|
-
|
|
324
|
-
title: t('layout.accountDeployment.linear.cleared'),
|
|
325
|
-
icon: 'i-lucide-check',
|
|
326
|
-
color: 'success',
|
|
327
|
-
})
|
|
298
|
+
actionToast.success('layout.accountDeployment.linear.cleared')
|
|
328
299
|
} catch (e) {
|
|
329
300
|
present(e, 'layout.accountDeployment.linear.clearFailed')
|
|
330
301
|
} finally {
|
|
@@ -334,7 +305,7 @@ async function clearLinear() {
|
|
|
334
305
|
|
|
335
306
|
async function saveFigma() {
|
|
336
307
|
if (!figma.clientId.trim() || !figma.clientSecret.trim() || !figma.redirectUrl.trim()) {
|
|
337
|
-
|
|
308
|
+
actionToast.error('layout.accountDeployment.figma.validation')
|
|
338
309
|
return
|
|
339
310
|
}
|
|
340
311
|
savingFigma.value = true
|
|
@@ -351,11 +322,7 @@ async function saveFigma() {
|
|
|
351
322
|
figma.clientId = ''
|
|
352
323
|
figma.clientSecret = ''
|
|
353
324
|
figma.redirectUrl = ''
|
|
354
|
-
|
|
355
|
-
title: t('layout.accountDeployment.figma.saved'),
|
|
356
|
-
icon: 'i-lucide-check',
|
|
357
|
-
color: 'success',
|
|
358
|
-
})
|
|
325
|
+
actionToast.success('layout.accountDeployment.figma.saved')
|
|
359
326
|
} catch (e) {
|
|
360
327
|
present(e, 'layout.accountDeployment.figma.saveFailed')
|
|
361
328
|
} finally {
|
|
@@ -368,11 +335,7 @@ async function clearFigma() {
|
|
|
368
335
|
savingFigma.value = true
|
|
369
336
|
try {
|
|
370
337
|
await store.save(props.accountId, { secrets: { figmaOAuth: null } })
|
|
371
|
-
|
|
372
|
-
title: t('layout.accountDeployment.figma.cleared'),
|
|
373
|
-
icon: 'i-lucide-check',
|
|
374
|
-
color: 'success',
|
|
375
|
-
})
|
|
338
|
+
actionToast.success('layout.accountDeployment.figma.cleared')
|
|
376
339
|
} catch (e) {
|
|
377
340
|
present(e, 'layout.accountDeployment.figma.clearFailed')
|
|
378
341
|
} finally {
|
|
@@ -384,7 +347,7 @@ async function saveWeb() {
|
|
|
384
347
|
const brave = web.braveApiKey.trim()
|
|
385
348
|
const searxng = web.searxngUrl.trim()
|
|
386
349
|
if (!brave && !searxng) {
|
|
387
|
-
|
|
350
|
+
actionToast.error('layout.accountDeployment.web.validation')
|
|
388
351
|
return
|
|
389
352
|
}
|
|
390
353
|
savingWeb.value = true
|
|
@@ -401,11 +364,7 @@ async function saveWeb() {
|
|
|
401
364
|
web.braveApiKey = ''
|
|
402
365
|
web.searxngUrl = ''
|
|
403
366
|
web.searxngApiKey = ''
|
|
404
|
-
|
|
405
|
-
title: t('layout.accountDeployment.web.saved'),
|
|
406
|
-
icon: 'i-lucide-check',
|
|
407
|
-
color: 'success',
|
|
408
|
-
})
|
|
367
|
+
actionToast.success('layout.accountDeployment.web.saved')
|
|
409
368
|
} catch (e) {
|
|
410
369
|
present(e, 'layout.accountDeployment.web.saveFailed')
|
|
411
370
|
} finally {
|
|
@@ -418,11 +377,7 @@ async function clearWeb() {
|
|
|
418
377
|
savingWeb.value = true
|
|
419
378
|
try {
|
|
420
379
|
await store.save(props.accountId, { secrets: { webSearch: null } })
|
|
421
|
-
|
|
422
|
-
title: t('layout.accountDeployment.web.cleared'),
|
|
423
|
-
icon: 'i-lucide-check',
|
|
424
|
-
color: 'success',
|
|
425
|
-
})
|
|
380
|
+
actionToast.success('layout.accountDeployment.web.cleared')
|
|
426
381
|
} catch (e) {
|
|
427
382
|
present(e, 'layout.accountDeployment.web.clearFailed')
|
|
428
383
|
} finally {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
3
4
|
import type { AgentFailureKind, PlatformFailureKindRule } from '~/types/execution'
|
|
4
5
|
import {
|
|
5
6
|
AGENT_FAILURE_KINDS,
|
|
@@ -165,13 +166,13 @@ const faults = computed(() => failureKindRuleFaults(rules.value))
|
|
|
165
166
|
</div>
|
|
166
167
|
|
|
167
168
|
<template v-if="overriding">
|
|
168
|
-
<
|
|
169
|
+
<EmptyState
|
|
169
170
|
v-if="rules.length === 0"
|
|
170
|
-
|
|
171
|
+
compact
|
|
172
|
+
icon="i-lucide-list-filter"
|
|
173
|
+
:title="t('settings.platformAlerts.failureKinds.empty')"
|
|
171
174
|
data-testid="platform-alert-failure-kinds-empty"
|
|
172
|
-
|
|
173
|
-
{{ t('settings.platformAlerts.failureKinds.empty') }}
|
|
174
|
-
</p>
|
|
175
|
+
/>
|
|
175
176
|
|
|
176
177
|
<div
|
|
177
178
|
v-for="(rule, index) in rules"
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
const props = defineProps<{ accountId: string }>()
|
|
17
17
|
|
|
18
18
|
const store = useAccountSettingsStore()
|
|
19
|
-
const
|
|
19
|
+
const actionToast = useActionToast()
|
|
20
20
|
const { present } = usePipelineErrorToast()
|
|
21
21
|
const { t } = useI18n()
|
|
22
22
|
|
|
@@ -132,11 +132,7 @@ async function save() {
|
|
|
132
132
|
try {
|
|
133
133
|
// `config` fully replaces the stored non-secret config, so carry the rest forward.
|
|
134
134
|
await store.save(props.accountId, { config: { ...store.view?.config, modelPolicy: policy } })
|
|
135
|
-
|
|
136
|
-
title: t('settings.modelPolicy.saved'),
|
|
137
|
-
icon: 'i-lucide-check',
|
|
138
|
-
color: 'success',
|
|
139
|
-
})
|
|
135
|
+
actionToast.success('settings.modelPolicy.saved')
|
|
140
136
|
} catch (e) {
|
|
141
137
|
present(e, 'settings.modelPolicy.saveFailed')
|
|
142
138
|
} finally {
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
const props = defineProps<{ accountId: string }>()
|
|
26
26
|
|
|
27
27
|
const store = useAccountSettingsStore()
|
|
28
|
-
const
|
|
28
|
+
const actionToast = useActionToast()
|
|
29
29
|
const { present } = usePipelineErrorToast()
|
|
30
30
|
const { t } = useI18n()
|
|
31
31
|
|
|
@@ -187,14 +187,12 @@ const ruleFaults = computed(() => failureKindRuleFaults(failureKindRules.value ?
|
|
|
187
187
|
|
|
188
188
|
async function save() {
|
|
189
189
|
if (!loaded.value) {
|
|
190
|
-
|
|
190
|
+
actionToast.error('settings.platformAlerts.notLoaded')
|
|
191
191
|
return
|
|
192
192
|
}
|
|
193
193
|
if (invalidFields.value.length > 0) {
|
|
194
|
-
|
|
195
|
-
title: t('settings.platformAlerts.invalidNumbers'),
|
|
194
|
+
actionToast.error('settings.platformAlerts.invalidNumbers', {
|
|
196
195
|
description: invalidFields.value.join(', '),
|
|
197
|
-
color: 'error',
|
|
198
196
|
})
|
|
199
197
|
return
|
|
200
198
|
}
|
|
@@ -203,14 +201,12 @@ async function save() {
|
|
|
203
201
|
// two faults describe themselves differently because they are fixed differently — a row
|
|
204
202
|
// number is useless advice for a list that is simply too long.
|
|
205
203
|
if (hasFailureKindRuleFaults(ruleFaults.value)) {
|
|
206
|
-
|
|
207
|
-
title: t('settings.platformAlerts.failureKinds.invalidTitle'),
|
|
204
|
+
actionToast.error('settings.platformAlerts.failureKinds.invalidTitle', {
|
|
208
205
|
description: ruleFaults.value.tooMany
|
|
209
206
|
? t('settings.platformAlerts.failureKinds.tooManyRules', { max: MAX_FAILURE_KIND_RULES })
|
|
210
207
|
: t('settings.platformAlerts.failureKinds.invalidRows', {
|
|
211
208
|
rows: ruleFaults.value.rows.join(', '),
|
|
212
209
|
}),
|
|
213
|
-
color: 'error',
|
|
214
210
|
})
|
|
215
211
|
return
|
|
216
212
|
}
|
|
@@ -229,11 +225,7 @@ async function save() {
|
|
|
229
225
|
await store.save(props.accountId, {
|
|
230
226
|
config: { ...store.view?.config, platformAlerts: settings },
|
|
231
227
|
})
|
|
232
|
-
|
|
233
|
-
title: t('settings.platformAlerts.saved'),
|
|
234
|
-
icon: 'i-lucide-check',
|
|
235
|
-
color: 'success',
|
|
236
|
-
})
|
|
228
|
+
actionToast.success('settings.platformAlerts.saved')
|
|
237
229
|
} catch (e) {
|
|
238
230
|
present(e, 'settings.platformAlerts.saveFailed')
|
|
239
231
|
} finally {
|
|
@@ -14,12 +14,13 @@ import type {
|
|
|
14
14
|
} from '~/types/merge'
|
|
15
15
|
import RiskPolicyCreateForm from '~/components/settings/RiskPolicyCreateForm.vue'
|
|
16
16
|
import RiskPolicyEditorRow from '~/components/settings/RiskPolicyEditorRow.vue'
|
|
17
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
17
18
|
|
|
18
19
|
const props = defineProps<{ accountId: string }>()
|
|
19
20
|
|
|
20
21
|
const { t } = useI18n()
|
|
21
22
|
const store = useAccountRiskPoliciesStore()
|
|
22
|
-
const
|
|
23
|
+
const actionToast = useActionToast()
|
|
23
24
|
const { present } = usePipelineErrorToast()
|
|
24
25
|
const { confirm } = useConfirm()
|
|
25
26
|
|
|
@@ -47,11 +48,7 @@ async function save(policy: RiskPolicyLibraryEntry, patch: UpdateRiskPolicyInput
|
|
|
47
48
|
busy.value = policy.id
|
|
48
49
|
try {
|
|
49
50
|
await store.update(props.accountId, policy.id, patch)
|
|
50
|
-
toast.
|
|
51
|
-
title: t('settings.riskPolicy.toast.saved'),
|
|
52
|
-
icon: 'i-lucide-check',
|
|
53
|
-
color: 'success',
|
|
54
|
-
})
|
|
51
|
+
actionToast.success('settings.riskPolicy.toast.saved')
|
|
55
52
|
} catch (e) {
|
|
56
53
|
present(e, 'settings.riskPolicy.toast.saveFailed')
|
|
57
54
|
} finally {
|
|
@@ -84,11 +81,7 @@ async function create(input: CreateRiskPolicyInput) {
|
|
|
84
81
|
creating.value = true
|
|
85
82
|
try {
|
|
86
83
|
await store.create(props.accountId, input)
|
|
87
|
-
toast.
|
|
88
|
-
title: t('settings.riskPolicy.toast.created'),
|
|
89
|
-
icon: 'i-lucide-check',
|
|
90
|
-
color: 'success',
|
|
91
|
-
})
|
|
84
|
+
actionToast.success('settings.riskPolicy.toast.created')
|
|
92
85
|
} catch (e) {
|
|
93
86
|
present(e, 'settings.riskPolicy.toast.createFailed')
|
|
94
87
|
} finally {
|
|
@@ -101,9 +94,15 @@ async function create(input: CreateRiskPolicyInput) {
|
|
|
101
94
|
<div class="space-y-4 text-sm" data-testid="account-risk-policy-panel">
|
|
102
95
|
<p class="text-2xs text-muted">{{ t('layout.accountRiskPolicies.intro') }}</p>
|
|
103
96
|
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
</
|
|
97
|
+
<div v-if="store.loading && policies.length === 0" class="space-y-2">
|
|
98
|
+
<USkeleton v-for="i in 2" :key="i" class="h-20 w-full" />
|
|
99
|
+
</div>
|
|
100
|
+
<EmptyState
|
|
101
|
+
v-else-if="policies.length === 0"
|
|
102
|
+
compact
|
|
103
|
+
icon="i-lucide-shield"
|
|
104
|
+
:title="t('layout.accountRiskPolicies.empty')"
|
|
105
|
+
/>
|
|
107
106
|
|
|
108
107
|
<RiskPolicyEditorRow
|
|
109
108
|
v-for="policy in policies"
|
|
@@ -16,7 +16,7 @@ import { onMounted, ref, watch } from 'vue'
|
|
|
16
16
|
const props = defineProps<{ accountId: string }>()
|
|
17
17
|
|
|
18
18
|
const store = useAccountSettingsStore()
|
|
19
|
-
const
|
|
19
|
+
const actionToast = useActionToast()
|
|
20
20
|
const { present } = usePipelineErrorToast()
|
|
21
21
|
const { t } = useI18n()
|
|
22
22
|
|
|
@@ -48,7 +48,7 @@ async function save() {
|
|
|
48
48
|
...(forbid.value ? { allowInitiatorPat: false } : { allowInitiatorPat: undefined }),
|
|
49
49
|
},
|
|
50
50
|
})
|
|
51
|
-
|
|
51
|
+
actionToast.success('settings.runCredentialPolicy.saved')
|
|
52
52
|
} catch (e) {
|
|
53
53
|
present(e, 'settings.runCredentialPolicy.saveFailed')
|
|
54
54
|
} finally {
|
|
@@ -9,6 +9,7 @@ import AccountPlatformAlertSettings from '~/components/layout/AccountPlatformAle
|
|
|
9
9
|
import AccountRunCredentialSettings from '~/components/layout/AccountRunCredentialSettings.vue'
|
|
10
10
|
import SecretInput from '~/components/common/SecretInput.vue'
|
|
11
11
|
import IconButton from '~/components/common/IconButton.vue'
|
|
12
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
12
13
|
|
|
13
14
|
// Team settings for an org account: the member roster (with combinable admin /
|
|
14
15
|
// developer / product roles), pending email invitations, and the per-account
|
|
@@ -19,7 +20,7 @@ const props = defineProps<{ accountId: string }>()
|
|
|
19
20
|
const accounts = useAccountsStore()
|
|
20
21
|
const uiMode = useUiModeStore()
|
|
21
22
|
const auth = useAuthStore()
|
|
22
|
-
const
|
|
23
|
+
const actionToast = useActionToast()
|
|
23
24
|
const { present } = usePipelineErrorToast()
|
|
24
25
|
const { t, te } = useI18n()
|
|
25
26
|
const { confirmAction, toastDone } = useConfirmAction()
|
|
@@ -77,7 +78,7 @@ async function revokeSessions(userId: string, label: string) {
|
|
|
77
78
|
if (!(await confirmAction('revoke', label))) return
|
|
78
79
|
try {
|
|
79
80
|
await accounts.revokeMemberSessions(props.accountId, userId)
|
|
80
|
-
|
|
81
|
+
actionToast.success('layout.accountTeam.members.sessionsRevoked')
|
|
81
82
|
} catch (e) {
|
|
82
83
|
present(e, 'layout.accountTeam.errors.revokeSessions')
|
|
83
84
|
}
|
|
@@ -114,7 +115,7 @@ async function createOrganization() {
|
|
|
114
115
|
try {
|
|
115
116
|
await accounts.createOrg(name)
|
|
116
117
|
newOrgName.value = ''
|
|
117
|
-
|
|
118
|
+
actionToast.success('layout.accountTeam.org.created')
|
|
118
119
|
} catch (e) {
|
|
119
120
|
present(e, 'layout.accountTeam.errors.createOrg')
|
|
120
121
|
} finally {
|
|
@@ -136,12 +137,10 @@ async function sendInvite() {
|
|
|
136
137
|
inviteRoles.value.length ? inviteRoles.value : ['developer'],
|
|
137
138
|
)
|
|
138
139
|
inviteEmail.value = ''
|
|
139
|
-
|
|
140
|
-
title: t('layout.accountTeam.invite.created'),
|
|
140
|
+
actionToast.success('layout.accountTeam.invite.created', {
|
|
141
141
|
description: acceptUrl
|
|
142
142
|
? t('layout.accountTeam.invite.createdEmailed')
|
|
143
143
|
: t('layout.accountTeam.invite.createdShareLink'),
|
|
144
|
-
icon: 'i-lucide-mail-check',
|
|
145
144
|
})
|
|
146
145
|
} catch (e) {
|
|
147
146
|
present(e, 'layout.accountTeam.errors.sendInvite')
|
|
@@ -175,7 +174,7 @@ async function connectEmail() {
|
|
|
175
174
|
fromAddress: emailFrom.value.trim(),
|
|
176
175
|
})
|
|
177
176
|
emailApiKey.value = ''
|
|
178
|
-
|
|
177
|
+
actionToast.success('layout.accountTeam.email.connected')
|
|
179
178
|
} catch (e) {
|
|
180
179
|
present(e, 'layout.accountTeam.errors.connectEmail')
|
|
181
180
|
} finally {
|
|
@@ -261,8 +260,12 @@ async function disconnectEmail() {
|
|
|
261
260
|
@click="revokeSessions(m.userId, m.name || m.email || m.userId)"
|
|
262
261
|
/>
|
|
263
262
|
</li>
|
|
264
|
-
<li v-if="accounts.members.length === 0"
|
|
265
|
-
|
|
263
|
+
<li v-if="accounts.members.length === 0">
|
|
264
|
+
<EmptyState
|
|
265
|
+
compact
|
|
266
|
+
icon="i-lucide-users"
|
|
267
|
+
:title="t('layout.accountTeam.members.empty')"
|
|
268
|
+
/>
|
|
266
269
|
</li>
|
|
267
270
|
</ul>
|
|
268
271
|
</section>
|
|
@@ -28,7 +28,7 @@ const { t } = useI18n()
|
|
|
28
28
|
const accounts = useAccountsStore()
|
|
29
29
|
const workspace = useWorkspaceStore()
|
|
30
30
|
const ui = useUiStore()
|
|
31
|
-
const
|
|
31
|
+
const actionToast = useActionToast()
|
|
32
32
|
const { present } = usePipelineErrorToast()
|
|
33
33
|
const access = useWorkspaceAccess()
|
|
34
34
|
const { confirm } = useConfirm()
|
|
@@ -191,7 +191,7 @@ async function removeBoard() {
|
|
|
191
191
|
busy.value = true
|
|
192
192
|
try {
|
|
193
193
|
await workspace.remove(id)
|
|
194
|
-
|
|
194
|
+
actionToast.success('layout.boardSwitcher.toast.boardDeleted')
|
|
195
195
|
} catch (e) {
|
|
196
196
|
present(e, 'layout.boardSwitcher.toast.deleteBoardFailed')
|
|
197
197
|
} finally {
|
|
@@ -12,10 +12,12 @@ const board = useBoardStore()
|
|
|
12
12
|
// the sidebar/command entries with zero edits here.
|
|
13
13
|
const { toolbarItems, invoke: invokeNav } = useNavContributions()
|
|
14
14
|
const execution = useExecutionStore()
|
|
15
|
+
const views = useWorkViewStore()
|
|
16
|
+
const queue = useWorkspaceQueueStore()
|
|
15
17
|
const workspace = useWorkspaceStore()
|
|
16
18
|
const workspaceSettings = useWorkspaceSettingsStore()
|
|
17
19
|
const services = useServicesStore()
|
|
18
|
-
const
|
|
20
|
+
const actionToast = useActionToast()
|
|
19
21
|
const { present } = usePipelineErrorToast()
|
|
20
22
|
const access = useWorkspaceAccess()
|
|
21
23
|
const { t, n } = useI18n()
|
|
@@ -24,11 +26,7 @@ const { fitView, zoomIn, zoomOut, resetZoom } = useBoardFlow()
|
|
|
24
26
|
async function mountService(serviceId: string, title: string) {
|
|
25
27
|
try {
|
|
26
28
|
await services.mount(serviceId)
|
|
27
|
-
|
|
28
|
-
title: t('board.toolbar.serviceAdded', { title }),
|
|
29
|
-
icon: 'i-lucide-box',
|
|
30
|
-
color: 'success',
|
|
31
|
-
})
|
|
29
|
+
actionToast.success('board.toolbar.serviceAdded', { params: { title } })
|
|
32
30
|
} catch (e) {
|
|
33
31
|
present(e, 'board.toolbar.serviceAddFailed')
|
|
34
32
|
}
|
|
@@ -51,11 +49,7 @@ const mountableItems = computed(() =>
|
|
|
51
49
|
async function restoreService(id: string, title: string) {
|
|
52
50
|
try {
|
|
53
51
|
await board.restoreService(id)
|
|
54
|
-
toast.
|
|
55
|
-
title: t('board.toast.restored', { name: title }),
|
|
56
|
-
icon: 'i-lucide-archive-restore',
|
|
57
|
-
color: 'neutral',
|
|
58
|
-
})
|
|
52
|
+
actionToast.success('board.toast.restored', { params: { name: title } })
|
|
59
53
|
} catch (e) {
|
|
60
54
|
present(e, 'board.toast.restoreFailed')
|
|
61
55
|
}
|
|
@@ -112,19 +106,13 @@ const spendLabel = computed(() => {
|
|
|
112
106
|
}
|
|
113
107
|
return `${fmt(s.costSpent)} / ${fmt(s.costLimit)}`
|
|
114
108
|
})
|
|
115
|
-
|
|
109
|
+
/** The decisions this counts are in every service, so the queue opens on all of them. */
|
|
110
|
+
function openQueue() {
|
|
111
|
+
queue.clearFilter()
|
|
112
|
+
views.show('queue')
|
|
113
|
+
}
|
|
116
114
|
|
|
117
|
-
const
|
|
118
|
-
execution.openDecisions.map((d) => {
|
|
119
|
-
const b = board.getBlock(d.blockId)
|
|
120
|
-
return {
|
|
121
|
-
label: b?.title ?? t('common.block'),
|
|
122
|
-
description: d.decision.question,
|
|
123
|
-
icon: 'i-lucide-circle-help',
|
|
124
|
-
onSelect: () => ui.openDecision(d.instanceId, d.decision.id),
|
|
125
|
-
}
|
|
126
|
-
}),
|
|
127
|
-
)
|
|
115
|
+
const spendColor = computed(() => (spend.value?.exceeded ? 'error' : 'neutral'))
|
|
128
116
|
</script>
|
|
129
117
|
|
|
130
118
|
<template>
|
|
@@ -199,21 +187,23 @@ const decisionItems = computed(() =>
|
|
|
199
187
|
|
|
200
188
|
<USeparator orientation="vertical" class="mx-1 h-6" />
|
|
201
189
|
|
|
202
|
-
<!--
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
190
|
+
<!-- The decision count. It opens the queue view, which lists every decision with its Answer
|
|
191
|
+
button beside the rest of what needs a person (issue #2258). -->
|
|
192
|
+
<UButton
|
|
193
|
+
v-if="execution.pendingDecisionCount"
|
|
194
|
+
color="warning"
|
|
195
|
+
variant="soft"
|
|
196
|
+
size="sm"
|
|
197
|
+
icon="i-lucide-circle-help"
|
|
198
|
+
:title="t('views.openQueue')"
|
|
199
|
+
data-testid="decision-badge"
|
|
200
|
+
@click="openQueue"
|
|
201
|
+
>
|
|
202
|
+
{{ execution.pendingDecisionCount
|
|
203
|
+
}}<span class="hidden sm:inline"
|
|
204
|
+
> {{ t('board.toolbar.decisionWord', execution.pendingDecisionCount) }}</span
|
|
210
205
|
>
|
|
211
|
-
|
|
212
|
-
}}<span class="hidden sm:inline"
|
|
213
|
-
> {{ t('board.toolbar.decisionWord', execution.pendingDecisionCount) }}</span
|
|
214
|
-
>
|
|
215
|
-
</UButton>
|
|
216
|
-
</UDropdownMenu>
|
|
206
|
+
</UButton>
|
|
217
207
|
|
|
218
208
|
<!-- how every frame's swimlanes are ordered + grouped (an override; advanced tier) -->
|
|
219
209
|
<LaneViewControl />
|
|
@@ -33,10 +33,15 @@ import DefaultTestEnvBanner from '~/components/layout/DefaultTestEnvBanner.vue'
|
|
|
33
33
|
|
|
34
34
|
// The live-stream state the connection strip renders. Passed down rather than resolved here:
|
|
35
35
|
// the page owns the single stream instance, and creating another would open a second socket.
|
|
36
|
+
//
|
|
37
|
+
// `inFlow` is the queue view's placement: the same region, laid out in the page flow above the
|
|
38
|
+
// queue's columns instead of floating over a canvas, and without the toolbar, which is the
|
|
39
|
+
// canvas's own chrome (zoom, fit, lane view).
|
|
36
40
|
defineProps<{
|
|
37
41
|
connected: boolean
|
|
38
42
|
everConnected: boolean
|
|
39
43
|
connectionFailed: boolean
|
|
44
|
+
inFlow?: boolean
|
|
40
45
|
}>()
|
|
41
46
|
|
|
42
47
|
const ui = useUiStore()
|
|
@@ -44,7 +49,8 @@ const ui = useUiStore()
|
|
|
44
49
|
|
|
45
50
|
<template>
|
|
46
51
|
<div
|
|
47
|
-
class="pointer-events-none
|
|
52
|
+
class="pointer-events-none z-40 flex flex-col items-center gap-2 px-4"
|
|
53
|
+
:class="inFlow ? 'relative pt-3' : 'absolute inset-x-0 top-0 pt-3'"
|
|
48
54
|
>
|
|
49
55
|
<!-- Compact-viewport nav trigger: the SideBar is an off-canvas drawer below lg, so surface
|
|
50
56
|
a hamburger to open it. Out of the column's flow (it is a corner control, not a
|
|
@@ -62,7 +68,7 @@ const ui = useUiStore()
|
|
|
62
68
|
|
|
63
69
|
<!-- FIRST, and deliberately so: the toolbar is standing board chrome, and a tour step
|
|
64
70
|
anchors it. Below the advisories it would move every time one appears or clears. -->
|
|
65
|
-
<BoardToolbar />
|
|
71
|
+
<BoardToolbar v-if="!inFlow" />
|
|
66
72
|
|
|
67
73
|
<!-- Then the advisories, most urgent first. Ordering is by what the user loses by not
|
|
68
74
|
reading it now, not by how loud the card is.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// available actions (connected integrations, etc.) show.
|
|
8
8
|
|
|
9
9
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
10
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
10
11
|
|
|
11
12
|
interface Command {
|
|
12
13
|
id: string
|
|
@@ -273,9 +274,12 @@ function indexOf(cmd: Command) {
|
|
|
273
274
|
</div>
|
|
274
275
|
|
|
275
276
|
<div class="max-h-80 overflow-y-auto p-1.5">
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
<EmptyState
|
|
278
|
+
v-if="filtered.length === 0"
|
|
279
|
+
compact
|
|
280
|
+
icon="i-lucide-search-x"
|
|
281
|
+
:title="t('layout.commandBar.noMatches')"
|
|
282
|
+
/>
|
|
279
283
|
|
|
280
284
|
<div v-for="group in groups" :key="group.name" class="mb-1">
|
|
281
285
|
<SectionLabel as="p" class="px-2 pb-1 pt-2">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
|
3
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
3
4
|
|
|
4
5
|
// A slim top strip shown when the real-time WebSocket isn't delivering events, so a
|
|
5
6
|
// silently-frozen board (events stop arriving, nothing updates) is no longer indistinguishable
|
|
@@ -70,7 +71,7 @@ onBeforeUnmount(clearTimer)
|
|
|
70
71
|
aria-live="polite"
|
|
71
72
|
data-testid="stream-reconnecting"
|
|
72
73
|
>
|
|
73
|
-
<
|
|
74
|
+
<Spinner class="h-3.5 w-3.5" />
|
|
74
75
|
<span>{{ t('app.reconnecting') }}</span>
|
|
75
76
|
</div>
|
|
76
77
|
<div
|