@cat-factory/app 0.311.1 → 0.311.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -5
- package/app/app.config.ts +6 -18
- 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/bootstrap/AdoptionReviewModal.vue +4 -7
- 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/AsyncViewError.vue +1 -1
- 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 +15 -9
- 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 +3 -11
- 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 +1 -1
- 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/ResultWindowShell.vue +1 -1
- 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/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/useNavContributions.ts +4 -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/pages/index.vue +3 -2
- 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/utils/bootstrapSteps.ts +11 -2
- package/app/utils/pipelineRender.ts +21 -8
- package/i18n/locales/de.json +12 -30
- package/i18n/locales/en.json +12 -30
- package/i18n/locales/es.json +12 -30
- package/i18n/locales/fr.json +12 -30
- package/i18n/locales/he.json +12 -30
- package/i18n/locales/it.json +12 -30
- package/i18n/locales/ja.json +12 -30
- package/i18n/locales/pl.json +12 -30
- package/i18n/locales/tr.json +12 -30
- package/i18n/locales/uk.json +12 -30
- package/package.json +1 -1
|
@@ -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'">
|
|
@@ -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 {
|