@cat-factory/app 0.42.0 → 0.43.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/app/components/board/RecurringPipelineModal.vue +16 -1
- package/app/components/layout/IntegrationsHub.vue +2 -1
- package/app/components/panels/inspector/TaskExecution.vue +2 -0
- package/app/components/settings/IssueTrackerPanel.vue +96 -6
- package/app/stores/tracker.ts +2 -0
- package/package.json +2 -2
|
@@ -32,8 +32,9 @@ const saving = ref(false)
|
|
|
32
32
|
const recurrence = ref<Recurrence>(defaultRecurrence())
|
|
33
33
|
|
|
34
34
|
// Tracker config (only relevant when the tech-debt pipeline is picked).
|
|
35
|
-
const trackerKind = ref<'github' | 'jira' | null>(null)
|
|
35
|
+
const trackerKind = ref<'github' | 'jira' | 'linear' | null>(null)
|
|
36
36
|
const jiraProjectKey = ref('')
|
|
37
|
+
const linearTeamId = ref('')
|
|
37
38
|
|
|
38
39
|
function defaultRecurrence(): Recurrence {
|
|
39
40
|
return {
|
|
@@ -77,6 +78,7 @@ watch(open, (isOpen) => {
|
|
|
77
78
|
saving.value = false
|
|
78
79
|
trackerKind.value = tracker.settings.tracker
|
|
79
80
|
jiraProjectKey.value = tracker.settings.jiraProjectKey ?? ''
|
|
81
|
+
linearTeamId.value = tracker.settings.linearTeamId ?? ''
|
|
80
82
|
})
|
|
81
83
|
|
|
82
84
|
const canAdd = computed(() => name.value.trim().length > 0 && pipelineId.value.length > 0)
|
|
@@ -92,6 +94,7 @@ async function add() {
|
|
|
92
94
|
await tracker.save({
|
|
93
95
|
tracker: trackerKind.value,
|
|
94
96
|
jiraProjectKey: trackerKind.value === 'jira' ? jiraProjectKey.value.trim() : null,
|
|
97
|
+
linearTeamId: trackerKind.value === 'linear' ? linearTeamId.value.trim() : null,
|
|
95
98
|
})
|
|
96
99
|
}
|
|
97
100
|
await recurring.create({
|
|
@@ -188,10 +191,22 @@ async function add() {
|
|
|
188
191
|
>
|
|
189
192
|
Jira
|
|
190
193
|
</UButton>
|
|
194
|
+
<UButton
|
|
195
|
+
size="xs"
|
|
196
|
+
:color="trackerKind === 'linear' ? 'primary' : 'neutral'"
|
|
197
|
+
:variant="trackerKind === 'linear' ? 'solid' : 'subtle'"
|
|
198
|
+
icon="i-lucide-square-kanban"
|
|
199
|
+
@click="trackerKind = 'linear'"
|
|
200
|
+
>
|
|
201
|
+
Linear
|
|
202
|
+
</UButton>
|
|
191
203
|
</div>
|
|
192
204
|
<UFormField v-if="trackerKind === 'jira'" label="Jira project key">
|
|
193
205
|
<UInput v-model="jiraProjectKey" placeholder="e.g. ENG" class="w-full" />
|
|
194
206
|
</UFormField>
|
|
207
|
+
<UFormField v-if="trackerKind === 'linear'" label="Linear team id">
|
|
208
|
+
<UInput v-model="linearTeamId" placeholder="team_…" class="w-full" />
|
|
209
|
+
</UFormField>
|
|
195
210
|
</div>
|
|
196
211
|
|
|
197
212
|
<p class="text-[11px] text-slate-500">
|
|
@@ -29,10 +29,11 @@ const workspace = useWorkspaceStore()
|
|
|
29
29
|
// nothing becomes unreachable.
|
|
30
30
|
const personalHubReachable = computed(() => !!auth.user)
|
|
31
31
|
|
|
32
|
-
// The selected filing tracker, as a badge label ("GitHub Issues" / "Jira").
|
|
32
|
+
// The selected filing tracker, as a badge label ("GitHub Issues" / "Jira" / "Linear").
|
|
33
33
|
const trackerLabel = computed(() => {
|
|
34
34
|
if (tracker.settings.tracker === 'github') return 'GitHub Issues'
|
|
35
35
|
if (tracker.settings.tracker === 'jira') return 'Jira'
|
|
36
|
+
if (tracker.settings.tracker === 'linear') return 'Linear'
|
|
36
37
|
return undefined
|
|
37
38
|
})
|
|
38
39
|
|
|
@@ -139,6 +139,7 @@ async function resetRun() {
|
|
|
139
139
|
:loading="stopping"
|
|
140
140
|
:disabled="resetting"
|
|
141
141
|
title="Stop the run but keep it (readable + retryable)"
|
|
142
|
+
data-testid="run-stop"
|
|
142
143
|
@click="stopRun"
|
|
143
144
|
>
|
|
144
145
|
Stop
|
|
@@ -152,6 +153,7 @@ async function resetRun() {
|
|
|
152
153
|
:loading="resetting"
|
|
153
154
|
:disabled="stopping"
|
|
154
155
|
title="Discard this run and reset the task to planned"
|
|
156
|
+
data-testid="run-reset"
|
|
155
157
|
@click="resetRun"
|
|
156
158
|
>
|
|
157
159
|
Reset
|
|
@@ -25,6 +25,7 @@ const toast = useToast()
|
|
|
25
25
|
// --- filing tracker + writeback (one Save, persisted on tracker settings) -----
|
|
26
26
|
const trackerKind = ref<TrackerKind | null>(null)
|
|
27
27
|
const jiraProjectKey = ref('')
|
|
28
|
+
const linearTeamId = ref('')
|
|
28
29
|
const commentOnPrOpen = ref(false)
|
|
29
30
|
const resolveOnMerge = ref(false)
|
|
30
31
|
const saving = ref(false)
|
|
@@ -32,6 +33,7 @@ const saving = ref(false)
|
|
|
32
33
|
function hydrate() {
|
|
33
34
|
trackerKind.value = tracker.settings.tracker
|
|
34
35
|
jiraProjectKey.value = tracker.settings.jiraProjectKey ?? ''
|
|
36
|
+
linearTeamId.value = tracker.settings.linearTeamId ?? ''
|
|
35
37
|
commentOnPrOpen.value = tracker.settings.writebackCommentOnPrOpen
|
|
36
38
|
resolveOnMerge.value = tracker.settings.writebackResolveOnMerge
|
|
37
39
|
}
|
|
@@ -48,17 +50,22 @@ watch(() => tracker.settings, hydrate)
|
|
|
48
50
|
// Per-source live state (available = usable now; enabled = offered to the workspace).
|
|
49
51
|
const github = computed(() => tasks.descriptorFor('github'))
|
|
50
52
|
const jira = computed(() => tasks.descriptorFor('jira'))
|
|
53
|
+
const linear = computed(() => tasks.descriptorFor('linear'))
|
|
51
54
|
|
|
52
55
|
// A tracker can only file where it can authenticate: GitHub rides the installed
|
|
53
|
-
// App, Jira
|
|
54
|
-
// won't file until set up), but we surface the gap inline.
|
|
56
|
+
// App, Jira/Linear need a connection. Selecting an unusable tracker is allowed (it
|
|
57
|
+
// just won't file until set up), but we surface the gap inline.
|
|
55
58
|
const githubAvailable = computed(() => github.value?.available ?? false)
|
|
56
59
|
const jiraConnected = computed(() => tasks.isConnected('jira'))
|
|
60
|
+
const linearConnected = computed(() => tasks.isConnected('linear'))
|
|
57
61
|
|
|
58
|
-
// Jira needs a project key to file into; block Save on
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
+
// Jira needs a project key and Linear needs a team id to file into; block Save on
|
|
63
|
+
// an empty one when that tracker is picked.
|
|
64
|
+
const canSave = computed(() => {
|
|
65
|
+
if (trackerKind.value === 'jira') return jiraProjectKey.value.trim().length > 0
|
|
66
|
+
if (trackerKind.value === 'linear') return linearTeamId.value.trim().length > 0
|
|
67
|
+
return true
|
|
68
|
+
})
|
|
62
69
|
|
|
63
70
|
async function save() {
|
|
64
71
|
if (!canSave.value) return
|
|
@@ -67,6 +74,7 @@ async function save() {
|
|
|
67
74
|
await tracker.save({
|
|
68
75
|
tracker: trackerKind.value,
|
|
69
76
|
jiraProjectKey: trackerKind.value === 'jira' ? jiraProjectKey.value.trim() : null,
|
|
77
|
+
linearTeamId: trackerKind.value === 'linear' ? linearTeamId.value.trim() : null,
|
|
70
78
|
writebackCommentOnPrOpen: commentOnPrOpen.value,
|
|
71
79
|
writebackResolveOnMerge: resolveOnMerge.value,
|
|
72
80
|
})
|
|
@@ -197,6 +205,15 @@ const STATUS_UI: Record<
|
|
|
197
205
|
>
|
|
198
206
|
Jira
|
|
199
207
|
</UButton>
|
|
208
|
+
<UButton
|
|
209
|
+
icon="i-lucide-square-kanban"
|
|
210
|
+
size="sm"
|
|
211
|
+
:color="trackerKind === 'linear' ? 'primary' : 'neutral'"
|
|
212
|
+
:variant="trackerKind === 'linear' ? 'solid' : 'subtle'"
|
|
213
|
+
@click="trackerKind = 'linear'"
|
|
214
|
+
>
|
|
215
|
+
Linear
|
|
216
|
+
</UButton>
|
|
200
217
|
</div>
|
|
201
218
|
|
|
202
219
|
<!-- Inline readiness hints for the picked tracker. -->
|
|
@@ -210,6 +227,14 @@ const STATUS_UI: Record<
|
|
|
210
227
|
<button class="underline" @click="ui.openTaskConnect('jira')">Connect it</button> to file
|
|
211
228
|
and link issues.
|
|
212
229
|
</p>
|
|
230
|
+
<p
|
|
231
|
+
v-else-if="trackerKind === 'linear' && !linearConnected"
|
|
232
|
+
class="text-[11px] text-amber-400"
|
|
233
|
+
>
|
|
234
|
+
Linear isn't connected yet.
|
|
235
|
+
<button class="underline" @click="ui.openTaskConnect('linear')">Connect it</button> to file
|
|
236
|
+
and link issues.
|
|
237
|
+
</p>
|
|
213
238
|
|
|
214
239
|
<UFormField v-if="trackerKind === 'jira'" label="Jira project key" class="w-48">
|
|
215
240
|
<UInput v-model="jiraProjectKey" placeholder="ENG" size="sm" class="w-full" />
|
|
@@ -217,6 +242,15 @@ const STATUS_UI: Record<
|
|
|
217
242
|
<span class="text-[11px] text-slate-500">New tickets are filed under this project.</span>
|
|
218
243
|
</template>
|
|
219
244
|
</UFormField>
|
|
245
|
+
|
|
246
|
+
<UFormField v-if="trackerKind === 'linear'" label="Linear team id" class="w-64">
|
|
247
|
+
<UInput v-model="linearTeamId" placeholder="team_…" size="sm" class="w-full" />
|
|
248
|
+
<template #help>
|
|
249
|
+
<span class="text-[11px] text-slate-500">
|
|
250
|
+
New issues are created under this team (Linear requires a team to create an issue).
|
|
251
|
+
</span>
|
|
252
|
+
</template>
|
|
253
|
+
</UFormField>
|
|
220
254
|
</section>
|
|
221
255
|
|
|
222
256
|
<!-- 2. Linking sources ---------------------------------------------------->
|
|
@@ -344,6 +378,62 @@ const STATUS_UI: Record<
|
|
|
344
378
|
:ui="{ description: 'text-[11px]' }"
|
|
345
379
|
/>
|
|
346
380
|
</div>
|
|
381
|
+
|
|
382
|
+
<!-- Linear source -->
|
|
383
|
+
<div class="rounded-lg border border-slate-800 bg-slate-800/40 px-3 py-2.5">
|
|
384
|
+
<div class="flex items-center justify-between gap-2">
|
|
385
|
+
<div class="flex min-w-0 items-center gap-2.5">
|
|
386
|
+
<UIcon name="i-lucide-square-kanban" class="h-5 w-5 shrink-0 text-slate-300" />
|
|
387
|
+
<div class="min-w-0">
|
|
388
|
+
<div class="text-sm font-medium text-slate-200">Linear</div>
|
|
389
|
+
<div class="text-[11px] text-slate-500">
|
|
390
|
+
{{ linearConnected ? 'Connected.' : 'Connect with a Linear personal API key.' }}
|
|
391
|
+
</div>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
<div class="flex shrink-0 items-center gap-2">
|
|
395
|
+
<UButton
|
|
396
|
+
v-if="linearConnected"
|
|
397
|
+
size="xs"
|
|
398
|
+
color="neutral"
|
|
399
|
+
variant="ghost"
|
|
400
|
+
icon="i-lucide-stethoscope"
|
|
401
|
+
:loading="tasks.checking === 'linear'"
|
|
402
|
+
@click="checkSetup('linear')"
|
|
403
|
+
>
|
|
404
|
+
Check setup
|
|
405
|
+
</UButton>
|
|
406
|
+
<USwitch
|
|
407
|
+
v-if="linear?.available"
|
|
408
|
+
:model-value="linear?.enabled ?? false"
|
|
409
|
+
:loading="togglingSource === 'linear'"
|
|
410
|
+
@update:model-value="(v: boolean) => toggleSource('linear', v)"
|
|
411
|
+
/>
|
|
412
|
+
<UButton
|
|
413
|
+
v-else
|
|
414
|
+
size="xs"
|
|
415
|
+
color="neutral"
|
|
416
|
+
variant="soft"
|
|
417
|
+
icon="i-lucide-plug"
|
|
418
|
+
@click="ui.openTaskConnect('linear')"
|
|
419
|
+
>
|
|
420
|
+
Connect
|
|
421
|
+
</UButton>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
<UAlert
|
|
425
|
+
v-if="tasks.diagnostics.linear"
|
|
426
|
+
class="mt-2.5"
|
|
427
|
+
:color="STATUS_UI[tasks.diagnostics.linear.status].color"
|
|
428
|
+
variant="subtle"
|
|
429
|
+
:icon="STATUS_UI[tasks.diagnostics.linear.status].icon"
|
|
430
|
+
:description="
|
|
431
|
+
tasks.diagnostics.linear.message +
|
|
432
|
+
(tasks.diagnostics.linear.detail ? ` ${tasks.diagnostics.linear.detail}` : '')
|
|
433
|
+
"
|
|
434
|
+
:ui="{ description: 'text-[11px]' }"
|
|
435
|
+
/>
|
|
436
|
+
</div>
|
|
347
437
|
</section>
|
|
348
438
|
|
|
349
439
|
<!-- 3. Writeback ---------------------------------------------------------->
|
package/app/stores/tracker.ts
CHANGED
|
@@ -14,6 +14,7 @@ export const useTrackerStore = defineStore('tracker', () => {
|
|
|
14
14
|
const settings = ref<TrackerSettings>({
|
|
15
15
|
tracker: null,
|
|
16
16
|
jiraProjectKey: null,
|
|
17
|
+
linearTeamId: null,
|
|
17
18
|
writebackCommentOnPrOpen: false,
|
|
18
19
|
writebackResolveOnMerge: false,
|
|
19
20
|
updatedAt: 0,
|
|
@@ -23,6 +24,7 @@ export const useTrackerStore = defineStore('tracker', () => {
|
|
|
23
24
|
settings.value = value ?? {
|
|
24
25
|
tracker: null,
|
|
25
26
|
jiraProjectKey: null,
|
|
27
|
+
linearTeamId: null,
|
|
26
28
|
writebackCommentOnPrOpen: false,
|
|
27
29
|
writebackResolveOnMerge: false,
|
|
28
30
|
updatedAt: 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
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",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"pinia-plugin-persistedstate": "^4.7.1",
|
|
35
35
|
"vue": "^3.5.38",
|
|
36
36
|
"wretch": "^3.0.9",
|
|
37
|
-
"@cat-factory/contracts": "0.
|
|
37
|
+
"@cat-factory/contracts": "0.41.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@toad-contracts/testing": "0.3.1",
|