@cat-factory/app 0.138.1 → 0.139.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.
@@ -10,6 +10,7 @@
10
10
  import { computed, reactive, watch } from 'vue'
11
11
  import ClarificationItem from '~/components/common/ClarificationItem.vue'
12
12
  import { INITIATIVE_STATUS_LABEL_KEYS } from '~/utils/initiative'
13
+ import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
13
14
 
14
15
  const board = useBoardStore()
15
16
  const initiatives = useInitiativesStore()
@@ -103,122 +104,98 @@ const onProceed = () => flushThen((id) => initiatives.proceedPlanning(id))
103
104
  </script>
104
105
 
105
106
  <template>
106
- <Teleport to="body">
107
- <div
108
- v-if="open"
109
- class="fixed inset-0 z-50 flex max-h-[100dvh] items-stretch justify-center bg-slate-950/70 backdrop-blur-sm"
110
- @click.self="close"
111
- >
107
+ <ResultWindowShell
108
+ :open="open"
109
+ icon="i-lucide-messages-square"
110
+ icon-class="bg-indigo-500/15 text-indigo-300"
111
+ :title="initiative?.title ?? block?.title ?? t('initiative.planning.title')"
112
+ :subtitle="t('initiative.planning.subtitle')"
113
+ width="3xl"
114
+ testid="initiative-planning-window"
115
+ @close="close"
116
+ >
117
+ <template v-if="initiative" #header-extras>
118
+ <UBadge color="primary" variant="subtle" size="sm">
119
+ {{ t(INITIATIVE_STATUS_LABEL_KEYS[initiative.status]) }}
120
+ </UBadge>
121
+ </template>
122
+
123
+ <div class="min-h-0 flex-1 overflow-y-auto px-5 py-4">
124
+ <!-- No entity yet -->
112
125
  <div
113
- class="m-4 flex w-full max-w-3xl flex-col overflow-hidden rounded-2xl border border-slate-800 bg-slate-900 shadow-2xl"
114
- role="dialog"
115
- aria-modal="true"
116
- data-testid="initiative-planning-window"
126
+ v-if="!initiative"
127
+ class="flex h-full flex-col items-center justify-center gap-2 text-center text-slate-400"
117
128
  >
118
- <!-- Header -->
119
- <header class="flex items-center gap-3 border-b border-slate-800 px-5 py-3">
120
- <span
121
- class="flex h-8 w-8 items-center justify-center rounded-lg bg-indigo-500/15 text-indigo-300"
122
- >
123
- <UIcon name="i-lucide-messages-square" class="h-4 w-4" />
124
- </span>
125
- <div class="min-w-0 flex-1">
126
- <h2 class="truncate text-sm font-semibold text-slate-100">
127
- {{ initiative?.title ?? block?.title ?? t('initiative.planning.title') }}
128
- </h2>
129
- <p class="truncate text-[11px] text-slate-400">
130
- {{ t('initiative.planning.subtitle') }}
131
- </p>
132
- </div>
133
- <UBadge v-if="initiative" color="primary" variant="subtle" size="sm">
134
- {{ t(INITIATIVE_STATUS_LABEL_KEYS[initiative.status]) }}
135
- </UBadge>
136
- <button
137
- class="rounded-md p-1.5 text-slate-400 hover:bg-slate-800 hover:text-slate-200"
138
- @click="close"
139
- >
140
- <UIcon name="i-lucide-x" class="h-4 w-4" />
141
- </button>
142
- </header>
143
-
144
- <div class="min-h-0 flex-1 overflow-y-auto px-5 py-4">
145
- <!-- No entity yet -->
146
- <div
147
- v-if="!initiative"
148
- class="flex h-full flex-col items-center justify-center gap-2 text-center text-slate-400"
149
- >
150
- <UIcon name="i-lucide-messages-square" class="h-8 w-8 opacity-40" />
151
- <p class="text-sm">{{ t('initiative.planning.empty') }}</p>
152
- </div>
153
-
154
- <template v-else>
155
- <p class="mb-4 text-[13px] leading-relaxed text-slate-300">
156
- {{ t('initiative.planning.intro') }}
157
- </p>
158
-
159
- <!-- Converged / no pending questions -->
160
- <div
161
- v-if="converged || questions.length === 0"
162
- class="rounded-lg border border-slate-800 bg-slate-950/40 p-4 text-center text-[13px] text-slate-400"
163
- data-testid="initiative-planning-converged"
164
- >
165
- {{ t('initiative.planning.converged') }}
166
- </div>
167
-
168
- <!-- Interview questions — the shared clarification surface (answer / not-relevant /
169
- recommend), reused with the requirements-review window. -->
170
- <ul v-else class="space-y-4">
171
- <li v-for="q in questions" :key="q.key" data-testid="initiative-planning-question">
172
- <ClarificationItem
173
- v-model:answer="drafts[q.key]"
174
- :prompt="q.question"
175
- :dismissed="q.status === 'dismissed'"
176
- :recommendation="q.recommendation"
177
- :recommending="!!q.id && initiatives.recommending.has(q.id)"
178
- :answer-placeholder="t('initiative.planning.answerPlaceholder')"
179
- @persist="persist(q)"
180
- @dismiss="setStatus(q, 'dismissed')"
181
- @reopen="setStatus(q, 'open')"
182
- @recommend="recommend(q)"
183
- @use-recommendation="useRecommendation(q)"
184
- />
185
- </li>
186
- </ul>
187
- </template>
129
+ <UIcon name="i-lucide-messages-square" class="h-8 w-8 opacity-40" />
130
+ <p class="text-sm">{{ t('initiative.planning.empty') }}</p>
131
+ </div>
132
+
133
+ <template v-else>
134
+ <p class="mb-4 text-[13px] leading-relaxed text-slate-300">
135
+ {{ t('initiative.planning.intro') }}
136
+ </p>
137
+
138
+ <!-- Converged / no pending questions -->
139
+ <div
140
+ v-if="converged || questions.length === 0"
141
+ class="rounded-lg border border-slate-800 bg-slate-950/40 p-4 text-center text-[13px] text-slate-400"
142
+ data-testid="initiative-planning-converged"
143
+ >
144
+ {{ t('initiative.planning.converged') }}
188
145
  </div>
189
146
 
190
- <!-- Action rail -->
191
- <footer
192
- v-if="initiative && !converged && questions.length > 0"
193
- class="flex items-center justify-between gap-3 border-t border-slate-800 px-5 py-3"
147
+ <!-- Interview questions — the shared clarification surface (answer / not-relevant /
148
+ recommend), reused with the requirements-review window. -->
149
+ <ul v-else class="space-y-4">
150
+ <li v-for="q in questions" :key="q.key" data-testid="initiative-planning-question">
151
+ <ClarificationItem
152
+ v-model:answer="drafts[q.key]"
153
+ :prompt="q.question"
154
+ :dismissed="q.status === 'dismissed'"
155
+ :recommendation="q.recommendation"
156
+ :recommending="!!q.id && initiatives.recommending.has(q.id)"
157
+ :answer-placeholder="t('initiative.planning.answerPlaceholder')"
158
+ @persist="persist(q)"
159
+ @dismiss="setStatus(q, 'dismissed')"
160
+ @reopen="setStatus(q, 'open')"
161
+ @recommend="recommend(q)"
162
+ @use-recommendation="useRecommendation(q)"
163
+ />
164
+ </li>
165
+ </ul>
166
+ </template>
167
+ </div>
168
+
169
+ <!-- Action rail -->
170
+ <footer
171
+ v-if="initiative && !converged && questions.length > 0"
172
+ class="flex items-center justify-between gap-3 border-t border-slate-800 px-5 py-3"
173
+ >
174
+ <p class="text-[11px] text-slate-500">
175
+ {{ t('initiative.planning.hint') }}
176
+ </p>
177
+ <div class="flex items-center gap-2">
178
+ <UButton
179
+ color="neutral"
180
+ variant="ghost"
181
+ size="sm"
182
+ :loading="resuming"
183
+ data-testid="initiative-planning-proceed"
184
+ @click="onProceed"
185
+ >
186
+ {{ t('initiative.planning.proceed') }}
187
+ </UButton>
188
+ <UButton
189
+ color="primary"
190
+ size="sm"
191
+ :loading="resuming"
192
+ :disabled="!allAnswered"
193
+ data-testid="initiative-planning-continue"
194
+ @click="onContinue"
194
195
  >
195
- <p class="text-[11px] text-slate-500">
196
- {{ t('initiative.planning.hint') }}
197
- </p>
198
- <div class="flex items-center gap-2">
199
- <UButton
200
- color="neutral"
201
- variant="ghost"
202
- size="sm"
203
- :loading="resuming"
204
- data-testid="initiative-planning-proceed"
205
- @click="onProceed"
206
- >
207
- {{ t('initiative.planning.proceed') }}
208
- </UButton>
209
- <UButton
210
- color="primary"
211
- size="sm"
212
- :loading="resuming"
213
- :disabled="!allAnswered"
214
- data-testid="initiative-planning-continue"
215
- @click="onContinue"
216
- >
217
- {{ t('initiative.planning.continue') }}
218
- </UButton>
219
- </div>
220
- </footer>
196
+ {{ t('initiative.planning.continue') }}
197
+ </UButton>
221
198
  </div>
222
- </div>
223
- </Teleport>
199
+ </footer>
200
+ </ResultWindowShell>
224
201
  </template>