@cat-factory/app 0.284.0 → 0.285.1
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/app/components/followUp/FollowUpWindow.vue +49 -9
- package/app/components/settings/KubernetesEngineForm.vue +1 -1
- package/app/components/settings/KubernetesEnvironmentForm.vue +1 -1
- package/app/composables/api/followUps.ts +11 -3
- package/app/stores/followUps.spec.ts +94 -0
- package/app/stores/followUps.ts +17 -3
- package/app/stores/ui/k3sDeepLink.spec.ts +5 -5
- package/app/types/execution.ts +1 -0
- package/i18n/locales/de.json +6 -1
- package/i18n/locales/en.json +6 -1
- package/i18n/locales/es.json +6 -1
- package/i18n/locales/fr.json +6 -1
- package/i18n/locales/he.json +6 -1
- package/i18n/locales/it.json +6 -1
- package/i18n/locales/ja.json +6 -1
- package/i18n/locales/pl.json +6 -1
- package/i18n/locales/tr.json +6 -1
- package/i18n/locales/uk.json +6 -1
- package/package.json +6 -6
|
@@ -10,7 +10,7 @@ import { useResultView } from '~/composables/useResultView'
|
|
|
10
10
|
import { useExecutionStore } from '~/stores/execution'
|
|
11
11
|
import { useBoardStore } from '~/stores/board'
|
|
12
12
|
import { useFollowUpsStore } from '~/stores/followUps'
|
|
13
|
-
import type { FollowUpItem } from '~/types/execution'
|
|
13
|
+
import type { FollowUpItem, FollowUpResolution } from '~/types/execution'
|
|
14
14
|
import { FOLLOW_UP_COMPANION_META } from '~/utils/catalog'
|
|
15
15
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
16
16
|
|
|
@@ -56,14 +56,23 @@ async function onQueue(item: FollowUpItem) {
|
|
|
56
56
|
const id = execId()
|
|
57
57
|
if (id) await followUps.queueItem(id, item.id).catch(() => {})
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Answer a question, either way.
|
|
61
|
+
*
|
|
62
|
+
* `answered` buys the Coder another pass to apply what you said. `closed` records the reply as a
|
|
63
|
+
* RULING: the item is decided and the gate clears exactly the same, but no pass is spent and the
|
|
64
|
+
* Coder is told the topic is settled so it stops re-raising it. Two buttons rather than one with a
|
|
65
|
+
* toggle, because the choice is not a preference about this window: it is what the answer IS, and
|
|
66
|
+
* the person typing it is the only one who knows.
|
|
67
|
+
*/
|
|
68
|
+
async function onAnswer(item: FollowUpItem, resolution: FollowUpResolution = 'answered') {
|
|
60
69
|
const id = execId()
|
|
61
70
|
const answer = (drafts[item.id] ?? '').trim()
|
|
62
71
|
if (!id || !answer) return
|
|
63
72
|
// Clear the draft only once the answer is actually recorded: clearing first would make a failed
|
|
64
73
|
// send cost the typed answer, and would also leave the unsaved guard below with nothing to protect.
|
|
65
74
|
await followUps
|
|
66
|
-
.answerItem(id, item.id, answer)
|
|
75
|
+
.answerItem(id, item.id, answer, resolution)
|
|
67
76
|
.then(() => {
|
|
68
77
|
delete drafts[item.id]
|
|
69
78
|
})
|
|
@@ -103,6 +112,7 @@ const STATUS_LABEL_KEYS: Record<FollowUpItem['status'], string> = {
|
|
|
103
112
|
filed: 'followUp.status.filed',
|
|
104
113
|
queued: 'followUp.status.queued',
|
|
105
114
|
answered: 'followUp.status.answered',
|
|
115
|
+
closed: 'followUp.status.closed',
|
|
106
116
|
dismissed: 'followUp.status.dismissed',
|
|
107
117
|
}
|
|
108
118
|
|
|
@@ -114,8 +124,14 @@ const STATUS_META: Record<
|
|
|
114
124
|
filed: { badge: 'success', text: 'text-emerald-300' },
|
|
115
125
|
queued: { badge: 'info', text: 'text-sky-300' },
|
|
116
126
|
answered: { badge: 'info', text: 'text-sky-300' },
|
|
127
|
+
closed: { badge: 'neutral', text: 'text-slate-300' },
|
|
117
128
|
dismissed: { badge: 'neutral', text: 'text-slate-400' },
|
|
118
129
|
}
|
|
130
|
+
|
|
131
|
+
/** Whether a decided item carries a recorded reply to show ('answered' or 'closed'). */
|
|
132
|
+
function hasRecordedAnswer(item: FollowUpItem): boolean {
|
|
133
|
+
return (item.status === 'answered' || item.status === 'closed') && !!item.answer
|
|
134
|
+
}
|
|
119
135
|
</script>
|
|
120
136
|
|
|
121
137
|
<template>
|
|
@@ -197,13 +213,19 @@ const STATUS_META: Record<
|
|
|
197
213
|
{{ item.ticketExternalId ?? t('followUp.viewIssue') }}
|
|
198
214
|
</a>
|
|
199
215
|
</p>
|
|
200
|
-
<p
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
216
|
+
<p v-if="hasRecordedAnswer(item)" class="mt-1 text-[11px] text-slate-300">
|
|
217
|
+
<span class="text-slate-500">
|
|
218
|
+
{{
|
|
219
|
+
item.status === 'closed' ? t('followUp.yourRuling') : t('followUp.yourAnswer')
|
|
220
|
+
}}
|
|
221
|
+
</span>
|
|
205
222
|
{{ item.answer }}
|
|
206
223
|
</p>
|
|
224
|
+
<!-- A decision that was made and then thrown away when the budget ran out. It must
|
|
225
|
+
not read like one the Coder acted on. -->
|
|
226
|
+
<p v-if="item.sendBackDropped" class="mt-1 text-[11px] text-amber-300">
|
|
227
|
+
{{ t('followUp.sendBackDropped') }}
|
|
228
|
+
</p>
|
|
207
229
|
|
|
208
230
|
<!-- Actions (only while the item is still undecided) -->
|
|
209
231
|
<div v-if="item.status === 'pending'" class="mt-2.5">
|
|
@@ -215,7 +237,10 @@ const STATUS_META: Record<
|
|
|
215
237
|
:placeholder="t('followUp.answerPlaceholder')"
|
|
216
238
|
class="w-full resize-y rounded-md border border-slate-700 bg-slate-950/60 px-2.5 py-1.5 text-[12px] text-slate-100 placeholder:text-slate-600 focus:border-sky-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/60"
|
|
217
239
|
/>
|
|
218
|
-
|
|
240
|
+
<!-- Wraps, like the follow-up row below: three buttons whose labels are two
|
|
241
|
+
words each in English are one long line in most of the other locales, and
|
|
242
|
+
the result window is a narrow panel. -->
|
|
243
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
219
244
|
<UButton
|
|
220
245
|
size="xs"
|
|
221
246
|
color="primary"
|
|
@@ -226,6 +251,21 @@ const STATUS_META: Record<
|
|
|
226
251
|
>
|
|
227
252
|
{{ t('followUp.actions.answerAndSend') }}
|
|
228
253
|
</UButton>
|
|
254
|
+
<UButton
|
|
255
|
+
size="xs"
|
|
256
|
+
color="neutral"
|
|
257
|
+
variant="subtle"
|
|
258
|
+
:loading="followUps.isActing(item.id)"
|
|
259
|
+
:disabled="!(drafts[item.id] ?? '').trim() || !access.canExecuteRuns.value"
|
|
260
|
+
:title="
|
|
261
|
+
access.canExecuteRuns.value
|
|
262
|
+
? t('followUp.actions.answerAndCloseHint')
|
|
263
|
+
: t('access.noRunExecute')
|
|
264
|
+
"
|
|
265
|
+
@click="onAnswer(item, 'closed')"
|
|
266
|
+
>
|
|
267
|
+
{{ t('followUp.actions.answerAndClose') }}
|
|
268
|
+
</UButton>
|
|
229
269
|
<UButton
|
|
230
270
|
size="xs"
|
|
231
271
|
color="neutral"
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
getFollowUpsContract,
|
|
6
6
|
queueFollowUpContract,
|
|
7
7
|
} from '@cat-factory/contracts'
|
|
8
|
+
import type { FollowUpResolution } from '~/types/execution'
|
|
8
9
|
import type { ApiContext } from './context'
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -34,12 +35,19 @@ export function followUpsApi({ send, ws }: ApiContext) {
|
|
|
34
35
|
pathParams: { executionId, itemId },
|
|
35
36
|
}),
|
|
36
37
|
|
|
37
|
-
// Answer a question item
|
|
38
|
-
|
|
38
|
+
// Answer a question item: 'answered' folds it into the Coder's next pass, 'closed' records a
|
|
39
|
+
// ruling that buys no pass (the Coder still sees it, as something already settled).
|
|
40
|
+
answerFollowUp: (
|
|
41
|
+
workspaceId: string,
|
|
42
|
+
executionId: string,
|
|
43
|
+
itemId: string,
|
|
44
|
+
answer: string,
|
|
45
|
+
resolution: FollowUpResolution = 'answered',
|
|
46
|
+
) =>
|
|
39
47
|
send(answerFollowUpContract, {
|
|
40
48
|
pathPrefix: ws(workspaceId),
|
|
41
49
|
pathParams: { executionId, itemId },
|
|
42
|
-
body: { answer },
|
|
50
|
+
body: { answer, resolution },
|
|
43
51
|
}),
|
|
44
52
|
|
|
45
53
|
// Dismiss a follow-up / question item without acting on it.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
+
import type { ExecutionInstance } from '~/types/execution'
|
|
3
|
+
import { useExecutionStore } from '~/stores/execution'
|
|
4
|
+
import { useFollowUpsStore } from '~/stores/followUps'
|
|
5
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
6
|
+
|
|
7
|
+
// This store imports `useApi` by path rather than taking the Nuxt auto-import, so the setup file's
|
|
8
|
+
// inert `vi.stubGlobal('useApi', …)` does not reach it; the module is mocked instead.
|
|
9
|
+
const api = {
|
|
10
|
+
answerFollowUp: vi.fn(),
|
|
11
|
+
dismissFollowUp: vi.fn(),
|
|
12
|
+
fileFollowUp: vi.fn(),
|
|
13
|
+
queueFollowUp: vi.fn(),
|
|
14
|
+
}
|
|
15
|
+
vi.mock('~/composables/useApi', () => ({ useApi: () => api }))
|
|
16
|
+
|
|
17
|
+
// A pipeline may place MORE THAN ONE follow-up-enabled Coder, and the engine decides each item on
|
|
18
|
+
// the step that surfaced it (`FollowUpGateController.loadFollowUpItem` routes by item id, and says
|
|
19
|
+
// in as many words that it may not pick "the first enabled step"). The optimistic echo has to
|
|
20
|
+
// agree: the response is the OWNING step's state, so echoing it anywhere else paints one Coder's
|
|
21
|
+
// items, statuses and dropped-send-back stamps over another's until the stream repairs it.
|
|
22
|
+
|
|
23
|
+
const item = (id: string, over: Record<string, unknown> = {}) => ({
|
|
24
|
+
id,
|
|
25
|
+
kind: 'question',
|
|
26
|
+
title: `Question ${id}`,
|
|
27
|
+
detail: '',
|
|
28
|
+
status: 'pending',
|
|
29
|
+
createdAt: 0,
|
|
30
|
+
updatedAt: 0,
|
|
31
|
+
...over,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
/** Two Coder steps, each with its own surfaced item. */
|
|
35
|
+
const run = (rev: number): ExecutionInstance =>
|
|
36
|
+
({
|
|
37
|
+
id: 'e1',
|
|
38
|
+
blockId: 'b1',
|
|
39
|
+
rev,
|
|
40
|
+
currentStep: 1,
|
|
41
|
+
steps: [
|
|
42
|
+
{
|
|
43
|
+
agentKind: 'coder',
|
|
44
|
+
followUps: { enabled: true, loops: 0, maxLoops: 3, items: [item('fu_1')] },
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
agentKind: 'coder',
|
|
48
|
+
followUps: { enabled: true, loops: 0, maxLoops: 3, items: [item('fu_2')] },
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}) as unknown as ExecutionInstance
|
|
52
|
+
|
|
53
|
+
const stepState = (index: number) => useExecutionStore().getInstance('e1')!.steps[index]!.followUps!
|
|
54
|
+
|
|
55
|
+
describe('followUps store: the optimistic echo lands on the item’s OWN step', () => {
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
vi.clearAllMocks()
|
|
58
|
+
useWorkspaceStore().workspaceId = 'ws1'
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('echoes a second Coder’s answer onto that Coder, leaving the first untouched', async () => {
|
|
62
|
+
// What the server returns for `fu_2`: the SECOND step's state, with the item now ruled on.
|
|
63
|
+
const answered = {
|
|
64
|
+
enabled: true,
|
|
65
|
+
loops: 0,
|
|
66
|
+
maxLoops: 3,
|
|
67
|
+
items: [item('fu_2', { status: 'closed', answer: 'The brief stands.' })],
|
|
68
|
+
}
|
|
69
|
+
api.answerFollowUp.mockResolvedValue(answered)
|
|
70
|
+
const execution = useExecutionStore()
|
|
71
|
+
execution.hydrate([run(1)], 'ws1')
|
|
72
|
+
|
|
73
|
+
await useFollowUpsStore().answerItem('e1', 'fu_2', 'The brief stands.', 'closed')
|
|
74
|
+
|
|
75
|
+
expect(stepState(1).items[0]!.status).toBe('closed')
|
|
76
|
+
// Routed by the FIRST enabled step this was `closed` too, and the first Coder's own card
|
|
77
|
+
// reported a decision nobody made on it.
|
|
78
|
+
expect(stepState(0).items.map((i) => i.id)).toEqual(['fu_1'])
|
|
79
|
+
expect(stepState(0).items[0]!.status).toBe('pending')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('leaves both steps alone when the response names no step it can find', async () => {
|
|
83
|
+
// A run whose cached copy predates the item (the stream has not delivered it yet) must not
|
|
84
|
+
// fall back to painting the response somewhere: doing nothing is what the stream repairs.
|
|
85
|
+
api.dismissFollowUp.mockResolvedValue({ enabled: true, loops: 0, maxLoops: 3, items: [] })
|
|
86
|
+
const execution = useExecutionStore()
|
|
87
|
+
execution.hydrate([run(1)], 'ws1')
|
|
88
|
+
|
|
89
|
+
await useFollowUpsStore().dismissItem('e1', 'fu_unknown')
|
|
90
|
+
|
|
91
|
+
expect(stepState(0).items[0]!.status).toBe('pending')
|
|
92
|
+
expect(stepState(1).items[0]!.status).toBe('pending')
|
|
93
|
+
})
|
|
94
|
+
})
|
package/app/stores/followUps.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ref } from 'vue'
|
|
|
3
3
|
import { useApi } from '~/composables/useApi'
|
|
4
4
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
5
|
import { useExecutionStore } from '~/stores/execution'
|
|
6
|
+
import type { FollowUpResolution } from '~/types/execution'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* The Follow-up companion action surface. The live item state lives on the run's Coder step
|
|
@@ -49,7 +50,13 @@ export const useFollowUpsStore = defineStore('followUps', () => {
|
|
|
49
50
|
executionId,
|
|
50
51
|
() => call(workspace.requireId()),
|
|
51
52
|
(state, instance) => {
|
|
52
|
-
|
|
53
|
+
// Routed by ITEM ID, matching what the engine does with the same request
|
|
54
|
+
// (`FollowUpGateController.loadFollowUpItem`): a pipeline may carry more than one
|
|
55
|
+
// follow-up-enabled Coder step, and the response is that step's state. Echoing it onto
|
|
56
|
+
// "the first enabled step" would paint a later Coder's items, statuses and dropped-
|
|
57
|
+
// send-back stamps over an earlier Coder's own, so the window shows the wrong step's
|
|
58
|
+
// list until the stream repairs it and the earlier card reports decisions it never had.
|
|
59
|
+
const step = instance.steps.find((s) => s.followUps?.items.some((i) => i.id === itemId))
|
|
53
60
|
if (step && state && typeof state === 'object') {
|
|
54
61
|
step.followUps = state as typeof step.followUps
|
|
55
62
|
}
|
|
@@ -69,8 +76,15 @@ export const useFollowUpsStore = defineStore('followUps', () => {
|
|
|
69
76
|
const queueItem = (executionId: string, itemId: string) =>
|
|
70
77
|
act(executionId, itemId, (ws) => api.queueFollowUp(ws, executionId, itemId))
|
|
71
78
|
|
|
72
|
-
const answerItem = (
|
|
73
|
-
|
|
79
|
+
const answerItem = (
|
|
80
|
+
executionId: string,
|
|
81
|
+
itemId: string,
|
|
82
|
+
answer: string,
|
|
83
|
+
resolution: FollowUpResolution = 'answered',
|
|
84
|
+
) =>
|
|
85
|
+
act(executionId, itemId, (ws) =>
|
|
86
|
+
api.answerFollowUp(ws, executionId, itemId, answer, resolution),
|
|
87
|
+
)
|
|
74
88
|
|
|
75
89
|
const dismissItem = (executionId: string, itemId: string) =>
|
|
76
90
|
act(executionId, itemId, (ws) => api.dismissFollowUp(ws, executionId, itemId))
|
|
@@ -16,7 +16,7 @@ function openWith(search: string): void {
|
|
|
16
16
|
|
|
17
17
|
const K3S_LINK =
|
|
18
18
|
'?infraSetup=local-k3s&label=Local+k3s&apiServerUrl=https%3A%2F%2F127.0.0.1%3A6443' +
|
|
19
|
-
'&namespaceTemplate=cf-env
|
|
19
|
+
'&namespaceTemplate=cf-env-pr%7B%7BpullNumber%7D%7D&hostTemplate=%7B%7Bnamespace%7D%7D.127.0.0.1.nip.io' +
|
|
20
20
|
'&scheme=http&insecureSkipTlsVerify=1'
|
|
21
21
|
|
|
22
22
|
/** The link a cluster published on a NON-default host port produces. */
|
|
@@ -25,7 +25,7 @@ const CUSTOM_PORT_LINK = `${K3S_LINK}&ingressPort=18080`
|
|
|
25
25
|
/** The link the CLI emits when it could NOT establish that the cluster serves ingress URLs. */
|
|
26
26
|
const NO_INGRESS_LINK =
|
|
27
27
|
'?infraSetup=local-k3s&label=Local+k3s&apiServerUrl=https%3A%2F%2F127.0.0.1%3A6443' +
|
|
28
|
-
'&namespaceTemplate=cf-env
|
|
28
|
+
'&namespaceTemplate=cf-env-pr%7B%7BpullNumber%7D%7D&insecureSkipTlsVerify=1'
|
|
29
29
|
|
|
30
30
|
describe('consumeK3sSetupDeepLink', () => {
|
|
31
31
|
beforeEach(() => {
|
|
@@ -45,8 +45,8 @@ describe('consumeK3sSetupDeepLink', () => {
|
|
|
45
45
|
expect(ui.k3sSetupPrefill.value).toEqual({
|
|
46
46
|
label: 'Local k3s',
|
|
47
47
|
apiServerUrl: 'https://127.0.0.1:6443',
|
|
48
|
-
namespaceTemplate: 'cf-env-{{pullNumber}}',
|
|
49
|
-
hostTemplate: '{{
|
|
48
|
+
namespaceTemplate: 'cf-env-pr{{pullNumber}}',
|
|
49
|
+
hostTemplate: '{{namespace}}.127.0.0.1.nip.io',
|
|
50
50
|
ingressPort: '',
|
|
51
51
|
urlScheme: 'http',
|
|
52
52
|
insecureSkipTlsVerify: true,
|
|
@@ -61,7 +61,7 @@ describe('consumeK3sSetupDeepLink', () => {
|
|
|
61
61
|
ui.consumeK3sSetupDeepLink()
|
|
62
62
|
|
|
63
63
|
expect(ui.k3sSetupPrefill.value?.ingressPort).toBe('18080')
|
|
64
|
-
expect(ui.k3sSetupPrefill.value?.hostTemplate).toBe('{{
|
|
64
|
+
expect(ui.k3sSetupPrefill.value?.hostTemplate).toBe('{{namespace}}.127.0.0.1.nip.io')
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
it('strips the ingress-port param too, so a reload does not re-seed it', () => {
|
package/app/types/execution.ts
CHANGED
package/i18n/locales/de.json
CHANGED
|
@@ -6478,16 +6478,21 @@
|
|
|
6478
6478
|
"suggested": "Vorgeschlagen:",
|
|
6479
6479
|
"viewIssue": "Issue ansehen",
|
|
6480
6480
|
"yourAnswer": "Deine Antwort:",
|
|
6481
|
-
"
|
|
6481
|
+
"yourRuling": "Deine Entscheidung:",
|
|
6482
|
+
"sendBackDropped": "Nie an den Coder gesendet: das Budget für Rückläufe war bereits aufgebraucht.",
|
|
6483
|
+
"answerPlaceholder": "Beantworte diese Frage und sende sie an den Coder zurück oder schließe sie als erledigt ab…",
|
|
6482
6484
|
"status": {
|
|
6483
6485
|
"pending": "Braucht eine Entscheidung",
|
|
6484
6486
|
"filed": "Als Issue eingereicht",
|
|
6485
6487
|
"queued": "An Coder gesendet",
|
|
6486
6488
|
"answered": "Beantwortet",
|
|
6489
|
+
"closed": "Entschieden",
|
|
6487
6490
|
"dismissed": "Verworfen"
|
|
6488
6491
|
},
|
|
6489
6492
|
"actions": {
|
|
6490
6493
|
"answerAndSend": "Antworten & zurücksenden",
|
|
6494
|
+
"answerAndClose": "Antworten & abschließen",
|
|
6495
|
+
"answerAndCloseHint": "Als erledigt festhalten, ohne einen weiteren Coder-Durchlauf zu verbrauchen.",
|
|
6491
6496
|
"dismiss": "Verwerfen",
|
|
6492
6497
|
"fileAsIssue": "Als Issue einreichen",
|
|
6493
6498
|
"sendToCoder": "An Coder senden"
|
package/i18n/locales/en.json
CHANGED
|
@@ -6199,16 +6199,21 @@
|
|
|
6199
6199
|
"suggested": "Suggested:",
|
|
6200
6200
|
"viewIssue": "View issue",
|
|
6201
6201
|
"yourAnswer": "Your answer:",
|
|
6202
|
-
"
|
|
6202
|
+
"yourRuling": "Your ruling:",
|
|
6203
|
+
"sendBackDropped": "Never sent to the Coder: the send-back budget was already spent.",
|
|
6204
|
+
"answerPlaceholder": "Answer this question, then send it back to the Coder or close it as settled…",
|
|
6203
6205
|
"status": {
|
|
6204
6206
|
"pending": "Needs a decision",
|
|
6205
6207
|
"filed": "Filed as issue",
|
|
6206
6208
|
"queued": "Sent to Coder",
|
|
6207
6209
|
"answered": "Answered",
|
|
6210
|
+
"closed": "Ruled on",
|
|
6208
6211
|
"dismissed": "Dismissed"
|
|
6209
6212
|
},
|
|
6210
6213
|
"actions": {
|
|
6211
6214
|
"answerAndSend": "Answer & send back",
|
|
6215
|
+
"answerAndClose": "Answer & close",
|
|
6216
|
+
"answerAndCloseHint": "Record this as settled without spending another Coder pass.",
|
|
6212
6217
|
"dismiss": "Dismiss",
|
|
6213
6218
|
"fileAsIssue": "File as issue",
|
|
6214
6219
|
"sendToCoder": "Send to Coder"
|
package/i18n/locales/es.json
CHANGED
|
@@ -5901,16 +5901,21 @@
|
|
|
5901
5901
|
"suggested": "Sugerido:",
|
|
5902
5902
|
"viewIssue": "Ver incidencia",
|
|
5903
5903
|
"yourAnswer": "Tu respuesta:",
|
|
5904
|
-
"
|
|
5904
|
+
"yourRuling": "Tu decisión:",
|
|
5905
|
+
"sendBackDropped": "Nunca se envió al Coder: el presupuesto de reenvíos ya estaba agotado.",
|
|
5906
|
+
"answerPlaceholder": "Responde a esta pregunta y devuélvela al Coder o ciérrala como zanjada…",
|
|
5905
5907
|
"status": {
|
|
5906
5908
|
"pending": "Requiere una decisión",
|
|
5907
5909
|
"filed": "Registrado como incidencia",
|
|
5908
5910
|
"queued": "Enviado al Coder",
|
|
5909
5911
|
"answered": "Respondido",
|
|
5912
|
+
"closed": "Resuelto",
|
|
5910
5913
|
"dismissed": "Descartado"
|
|
5911
5914
|
},
|
|
5912
5915
|
"actions": {
|
|
5913
5916
|
"answerAndSend": "Responder y devolver",
|
|
5917
|
+
"answerAndClose": "Responder y cerrar",
|
|
5918
|
+
"answerAndCloseHint": "Registrarlo como zanjado sin gastar otra pasada del Coder.",
|
|
5914
5919
|
"dismiss": "Descartar",
|
|
5915
5920
|
"fileAsIssue": "Registrar como incidencia",
|
|
5916
5921
|
"sendToCoder": "Enviar al Coder"
|
package/i18n/locales/fr.json
CHANGED
|
@@ -5901,16 +5901,21 @@
|
|
|
5901
5901
|
"suggested": "Suggestion :",
|
|
5902
5902
|
"viewIssue": "Voir le ticket",
|
|
5903
5903
|
"yourAnswer": "Votre réponse :",
|
|
5904
|
-
"
|
|
5904
|
+
"yourRuling": "Votre décision :",
|
|
5905
|
+
"sendBackDropped": "Jamais transmis au Coder : le budget de renvois était déjà épuisé.",
|
|
5906
|
+
"answerPlaceholder": "Répondez à cette question, puis renvoyez-la au Coder ou clôturez-la comme réglée…",
|
|
5905
5907
|
"status": {
|
|
5906
5908
|
"pending": "Nécessite une décision",
|
|
5907
5909
|
"filed": "Enregistré comme ticket",
|
|
5908
5910
|
"queued": "Envoyé au Coder",
|
|
5909
5911
|
"answered": "Répondu",
|
|
5912
|
+
"closed": "Tranché",
|
|
5910
5913
|
"dismissed": "Rejeté"
|
|
5911
5914
|
},
|
|
5912
5915
|
"actions": {
|
|
5913
5916
|
"answerAndSend": "Répondre et renvoyer",
|
|
5917
|
+
"answerAndClose": "Répondre et clore",
|
|
5918
|
+
"answerAndCloseHint": "Enregistrer comme réglé sans dépenser une passe supplémentaire du Coder.",
|
|
5914
5919
|
"dismiss": "Rejeter",
|
|
5915
5920
|
"fileAsIssue": "Enregistrer comme ticket",
|
|
5916
5921
|
"sendToCoder": "Envoyer au Coder"
|
package/i18n/locales/he.json
CHANGED
|
@@ -5901,16 +5901,21 @@
|
|
|
5901
5901
|
"suggested": "מוצע:",
|
|
5902
5902
|
"viewIssue": "הצג ניושן",
|
|
5903
5903
|
"yourAnswer": "התשובה שלך:",
|
|
5904
|
-
"
|
|
5904
|
+
"yourRuling": "ההכרעה שלך:",
|
|
5905
|
+
"sendBackDropped": "מעולם לא נשלח ל-Coder: תקציב ההחזרות כבר אזל.",
|
|
5906
|
+
"answerPlaceholder": "ענה על שאלה זו, ולאחר מכן שלח אותה בחזרה ל-Coder או סגור אותה כמוסדרת…",
|
|
5905
5907
|
"status": {
|
|
5906
5908
|
"pending": "דורש החלטה",
|
|
5907
5909
|
"filed": "הוגש כניושן",
|
|
5908
5910
|
"queued": "נשלח ל-Coder",
|
|
5909
5911
|
"answered": "נענה",
|
|
5912
|
+
"closed": "הוכרע",
|
|
5910
5913
|
"dismissed": "נדחה"
|
|
5911
5914
|
},
|
|
5912
5915
|
"actions": {
|
|
5913
5916
|
"answerAndSend": "ענה ושלח בחזרה",
|
|
5917
|
+
"answerAndClose": "לענות ולסגור",
|
|
5918
|
+
"answerAndCloseHint": "לתעד כמוסדר בלי לבזבז מעבר נוסף של ה-Coder.",
|
|
5914
5919
|
"dismiss": "דחה",
|
|
5915
5920
|
"fileAsIssue": "הגש כניושן",
|
|
5916
5921
|
"sendToCoder": "שלח ל-Coder"
|
package/i18n/locales/it.json
CHANGED
|
@@ -6478,16 +6478,21 @@
|
|
|
6478
6478
|
"suggested": "Suggerito:",
|
|
6479
6479
|
"viewIssue": "Visualizza issue",
|
|
6480
6480
|
"yourAnswer": "La tua risposta:",
|
|
6481
|
-
"
|
|
6481
|
+
"yourRuling": "La tua decisione:",
|
|
6482
|
+
"sendBackDropped": "Mai inviato al Coder: il budget dei rinvii era già esaurito.",
|
|
6483
|
+
"answerPlaceholder": "Rispondi a questa domanda, poi inviala al Coder o chiudila come risolta…",
|
|
6482
6484
|
"status": {
|
|
6483
6485
|
"pending": "Richiede una decisione",
|
|
6484
6486
|
"filed": "Registrato come issue",
|
|
6485
6487
|
"queued": "Inviato al Coder",
|
|
6486
6488
|
"answered": "Risposto",
|
|
6489
|
+
"closed": "Deciso",
|
|
6487
6490
|
"dismissed": "Ignorato"
|
|
6488
6491
|
},
|
|
6489
6492
|
"actions": {
|
|
6490
6493
|
"answerAndSend": "Rispondi e invia",
|
|
6494
|
+
"answerAndClose": "Rispondi e chiudi",
|
|
6495
|
+
"answerAndCloseHint": "Registralo come risolto senza spendere un altro passaggio del Coder.",
|
|
6491
6496
|
"dismiss": "Ignora",
|
|
6492
6497
|
"fileAsIssue": "Registra come issue",
|
|
6493
6498
|
"sendToCoder": "Invia al Coder"
|
package/i18n/locales/ja.json
CHANGED
|
@@ -5901,16 +5901,21 @@
|
|
|
5901
5901
|
"suggested": "提案:",
|
|
5902
5902
|
"viewIssue": "issueを表示",
|
|
5903
5903
|
"yourAnswer": "あなたの回答:",
|
|
5904
|
-
"
|
|
5904
|
+
"yourRuling": "あなたの裁定:",
|
|
5905
|
+
"sendBackDropped": "Coderには送信されませんでした: 差し戻しの予算をすでに使い切っていました。",
|
|
5906
|
+
"answerPlaceholder": "この質問に回答し、Coderに返送するか、決着済みとして確定してください…",
|
|
5905
5907
|
"status": {
|
|
5906
5908
|
"pending": "判断が必要",
|
|
5907
5909
|
"filed": "issueとして登録済み",
|
|
5908
5910
|
"queued": "Coderに送信済み",
|
|
5909
5911
|
"answered": "回答済み",
|
|
5912
|
+
"closed": "裁定済み",
|
|
5910
5913
|
"dismissed": "却下済み"
|
|
5911
5914
|
},
|
|
5912
5915
|
"actions": {
|
|
5913
5916
|
"answerAndSend": "回答して返送",
|
|
5917
|
+
"answerAndClose": "回答して確定",
|
|
5918
|
+
"answerAndCloseHint": "Coderの再実行を消費せずに決着済みとして記録します。",
|
|
5914
5919
|
"dismiss": "却下",
|
|
5915
5920
|
"fileAsIssue": "issueとして登録",
|
|
5916
5921
|
"sendToCoder": "Coderに送信"
|
package/i18n/locales/pl.json
CHANGED
|
@@ -5901,16 +5901,21 @@
|
|
|
5901
5901
|
"suggested": "Sugerowane:",
|
|
5902
5902
|
"viewIssue": "Zobacz zgłoszenie",
|
|
5903
5903
|
"yourAnswer": "Twoja odpowiedź:",
|
|
5904
|
-
"
|
|
5904
|
+
"yourRuling": "Twoje rozstrzygnięcie:",
|
|
5905
|
+
"sendBackDropped": "Nigdy nie trafiło do Codera: budżet zawrotek był już wyczerpany.",
|
|
5906
|
+
"answerPlaceholder": "Odpowiedz na to pytanie, a następnie odeślij je do Codera albo zamknij jako rozstrzygnięte…",
|
|
5905
5907
|
"status": {
|
|
5906
5908
|
"pending": "Wymaga decyzji",
|
|
5907
5909
|
"filed": "Zgłoszone jako zgłoszenie",
|
|
5908
5910
|
"queued": "Wysłane do Codera",
|
|
5909
5911
|
"answered": "Odpowiedziano",
|
|
5912
|
+
"closed": "Rozstrzygnięte",
|
|
5910
5913
|
"dismissed": "Odrzucone"
|
|
5911
5914
|
},
|
|
5912
5915
|
"actions": {
|
|
5913
5916
|
"answerAndSend": "Odpowiedz i odeślij",
|
|
5917
|
+
"answerAndClose": "Odpowiedz i zamknij",
|
|
5918
|
+
"answerAndCloseHint": "Zapisz jako rozstrzygnięte bez zużywania kolejnego przebiegu Codera.",
|
|
5914
5919
|
"dismiss": "Odrzuć",
|
|
5915
5920
|
"fileAsIssue": "Zgłoś jako zgłoszenie",
|
|
5916
5921
|
"sendToCoder": "Wyślij do Codera"
|
package/i18n/locales/tr.json
CHANGED
|
@@ -5901,16 +5901,21 @@
|
|
|
5901
5901
|
"suggested": "Önerilen:",
|
|
5902
5902
|
"viewIssue": "Sorunu görüntüle",
|
|
5903
5903
|
"yourAnswer": "Cevabınız:",
|
|
5904
|
-
"
|
|
5904
|
+
"yourRuling": "Kararınız:",
|
|
5905
|
+
"sendBackDropped": "Coder'a hiç gönderilmedi: geri gönderim bütçesi çoktan tükenmişti.",
|
|
5906
|
+
"answerPlaceholder": "Bu soruyu cevaplayın, ardından Coder'a geri gönderin veya çözülmüş olarak kapatın…",
|
|
5905
5907
|
"status": {
|
|
5906
5908
|
"pending": "Karar gerekiyor",
|
|
5907
5909
|
"filed": "Sorun olarak kaydedildi",
|
|
5908
5910
|
"queued": "Coder'a gönderildi",
|
|
5909
5911
|
"answered": "Cevaplandı",
|
|
5912
|
+
"closed": "Karara bağlandı",
|
|
5910
5913
|
"dismissed": "Reddedildi"
|
|
5911
5914
|
},
|
|
5912
5915
|
"actions": {
|
|
5913
5916
|
"answerAndSend": "Cevapla ve geri gönder",
|
|
5917
|
+
"answerAndClose": "Yanıtla ve kapat",
|
|
5918
|
+
"answerAndCloseHint": "Yeni bir Coder turu harcamadan çözülmüş olarak kaydet.",
|
|
5914
5919
|
"dismiss": "Reddet",
|
|
5915
5920
|
"fileAsIssue": "Sorun olarak kaydet",
|
|
5916
5921
|
"sendToCoder": "Coder'a gönder"
|
package/i18n/locales/uk.json
CHANGED
|
@@ -5901,16 +5901,21 @@
|
|
|
5901
5901
|
"suggested": "Пропозиція:",
|
|
5902
5902
|
"viewIssue": "Переглянути тікет",
|
|
5903
5903
|
"yourAnswer": "Ваша відповідь:",
|
|
5904
|
-
"
|
|
5904
|
+
"yourRuling": "Ваше рішення:",
|
|
5905
|
+
"sendBackDropped": "Ніколи не надіслано до Coder: бюджет повернень уже вичерпано.",
|
|
5906
|
+
"answerPlaceholder": "Дайте відповідь на це запитання, а потім поверніть його до Coder або закрийте як вирішене…",
|
|
5905
5907
|
"status": {
|
|
5906
5908
|
"pending": "Потребує рішення",
|
|
5907
5909
|
"filed": "Зареєстровано як тікет",
|
|
5908
5910
|
"queued": "Надіслано Coder",
|
|
5909
5911
|
"answered": "Відповіли",
|
|
5912
|
+
"closed": "Вирішено",
|
|
5910
5913
|
"dismissed": "Відхилено"
|
|
5911
5914
|
},
|
|
5912
5915
|
"actions": {
|
|
5913
5916
|
"answerAndSend": "Відповісти й повернути",
|
|
5917
|
+
"answerAndClose": "Відповісти й закрити",
|
|
5918
|
+
"answerAndCloseHint": "Записати як вирішене, не витрачаючи ще один прохід Coder.",
|
|
5914
5919
|
"dismiss": "Відхилити",
|
|
5915
5920
|
"fileAsIssue": "Зареєструвати як тікет",
|
|
5916
5921
|
"sendToCoder": "Надіслати Coder"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.285.1",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@cat-factory/contracts": "0.328.0",
|
|
21
22
|
"@modular-frontend/core": "0.6.0",
|
|
22
23
|
"@modular-vue/core": "^1.5.0",
|
|
23
24
|
"@modular-vue/journeys": "^1.4.0",
|
|
24
25
|
"@modular-vue/nuxt": "^0.4.1",
|
|
25
26
|
"@modular-vue/runtime": "^1.4.1",
|
|
26
27
|
"@modular-vue/vue": "^1.4.1",
|
|
27
|
-
"@nuxt/ui": "^4.
|
|
28
|
+
"@nuxt/ui": "^4.11.0",
|
|
28
29
|
"@nuxtjs/i18n": "^10.6.0",
|
|
29
30
|
"@pinia/nuxt": "^1.0.2",
|
|
30
31
|
"@toad-contracts/core": "0.4.0",
|
|
@@ -39,18 +40,17 @@
|
|
|
39
40
|
"pinia-plugin-persistedstate": "^4.7.1",
|
|
40
41
|
"valibot": "^1.4.2",
|
|
41
42
|
"vue": "3.5.41",
|
|
42
|
-
"wretch": "^3.0.9"
|
|
43
|
-
"@cat-factory/contracts": "0.326.0"
|
|
43
|
+
"wretch": "^3.0.9"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|
|
47
|
-
"happy-dom": "^20.11.
|
|
47
|
+
"happy-dom": "^20.11.6",
|
|
48
48
|
"msw": "^2.15.0",
|
|
49
49
|
"nuxt": "^4.5.2",
|
|
50
50
|
"typescript": "^6.0.3",
|
|
51
51
|
"vitest": "^4.1.11",
|
|
52
52
|
"vue-i18n-extract": "^2.0.7",
|
|
53
|
-
"vue-tsc": "^3.3.
|
|
53
|
+
"vue-tsc": "^3.3.11"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"nuxt": "^4.5.2"
|