aegon-dangerious 1.0.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.
Files changed (32) hide show
  1. package/build-report/components/AiPromptGenerateDialog.vue +349 -0
  2. package/build-report/components/CapsuleScrollbar.vue +145 -0
  3. package/build-report/components/ChapterTitleScroll.vue +292 -0
  4. package/build-report/components/PromptField.vue +314 -0
  5. package/build-report/components/RecentReportsTable.vue +408 -0
  6. package/build-report/components/ReportChapterPreview.vue +437 -0
  7. package/build-report/components/ReportChapterWorkspace.vue +923 -0
  8. package/build-report/components/ReportCollapsibleSection.vue +132 -0
  9. package/build-report/components/ReportConfigPanel.vue +609 -0
  10. package/build-report/components/ReportGenerateSettingsPanel.vue +403 -0
  11. package/build-report/components/ReportGeneratingPanel.vue +908 -0
  12. package/build-report/components/ReportHistoryList.vue +157 -0
  13. package/build-report/components/ReportHistoryPanel.vue +140 -0
  14. package/build-report/components/ReportPromptPanel.vue +445 -0
  15. package/build-report/components/ReportSectionConfirmPopover.vue +232 -0
  16. package/build-report/components/ReportSourceFileSummary.vue +544 -0
  17. package/build-report/components/ReportSourcesMoreOverlay.vue +279 -0
  18. package/build-report/components/ReportSourcesPanel.vue +1055 -0
  19. package/build-report/components/ReportTemplateDetail.vue +176 -0
  20. package/build-report/components/ReportTemplateEditorFormPanel.vue +602 -0
  21. package/build-report/components/ReportTemplatePicker.vue +802 -0
  22. package/build-report/components/ReportTemplatePromptField.vue +314 -0
  23. package/build-report/components/ReportWorkflowSidebar.vue +104 -0
  24. package/build-report/components/reportEdit.vue +334 -0
  25. package/build-report/index.vue +646 -0
  26. package/build-report/report-list.vue +407 -0
  27. package/build-report/report-template-data.ts +248 -0
  28. package/build-report/styles/report-workflow-dialog.css +33 -0
  29. package/build-report/useReportWorkflow.ts +343 -0
  30. package/build-report/utils/report-edit-route.ts +109 -0
  31. package/build-report/utils/scroll.ts +361 -0
  32. package/package.json +12 -0
@@ -0,0 +1,343 @@
1
+ import { computed, reactive, ref, watch } from 'vue'
2
+ import type { ReportTemplateCard } from './report-template-data'
3
+
4
+ export const FILE_SIZE_LIMIT_KB = 100 * 1024
5
+ export const SOURCE_VISIBLE_LIMIT = 5
6
+
7
+ export type SourceFilterTab = 'all' | 'selected' | 'unselected'
8
+
9
+ export interface PendingFile {
10
+ id: string
11
+ name: string
12
+ sizeKb: number
13
+ }
14
+
15
+ export interface SourceFile {
16
+ id: string
17
+ name: string
18
+ selected: boolean
19
+ }
20
+
21
+ export const MOCK_PENDING_FILES: PendingFile[] = [
22
+ { id: 'p1', name: 'file_4.docx', sizeKb: 107782 },
23
+ { id: 'p2', name: 'file_3.xlsx', sizeKb: 530 },
24
+ { id: 'p3', name: 'file_1.pdf', sizeKb: 325 },
25
+ ]
26
+
27
+ export const MOCK_SOURCE_FILES: SourceFile[] = [
28
+ { id: 's1', name: '香港物業市場回顧及前瞻2024.pdf', selected: true },
29
+ { id: 's2', name: '2024年香港住宅物業價格與租金走勢分析.pdf', selected: true },
30
+ { id: 's3', name: '香港商業地產市場年度觀察報告2024.pdf', selected: true },
31
+ { id: 's4', name: '香港房地產市場2024年回顧與展望.docx', selected: false },
32
+ { id: 's5', name: '香港住宅租金指數2024年數據匯總.xlsx', selected: false },
33
+ { id: 's6', name: '香港物業市場季度監測報告2024Q4.pdf', selected: false },
34
+ ]
35
+
36
+ export type ReportEditorRole = 'initiator' | 'editor'
37
+
38
+ export interface ReportEditor {
39
+ id: string
40
+ name: string
41
+ employeeId: string
42
+ department: string
43
+ role: ReportEditorRole
44
+ }
45
+
46
+ export interface UmsEditorCandidate {
47
+ id: string
48
+ name: string
49
+ employeeId: string
50
+ department: string
51
+ }
52
+
53
+ export const CURRENT_USER_ID = '8000121'
54
+
55
+ export const MOCK_REPORT_EDITORS: ReportEditor[] = [
56
+ { id: '8000121', name: '陳大文', employeeId: '8000121', department: 'CMM', role: 'initiator' },
57
+ { id: '8000122', name: '陳大大', employeeId: '8000122', department: 'CCMC', role: 'editor' },
58
+ { id: '8000123', name: '張小小', employeeId: '8000123', department: 'CMM', role: 'editor' },
59
+ ]
60
+
61
+ export const MOCK_UMS_EDITOR_USERS: UmsEditorCandidate[] = [
62
+ { id: '9000101', name: '張偉', employeeId: '9000101', department: 'CMM' },
63
+ { id: '9000102', name: '李明', employeeId: '9000102', department: 'CCMC' },
64
+ { id: '9000103', name: '王芳', employeeId: '9000103', department: 'CMM' },
65
+ { id: '9000104', name: '陳強', employeeId: '9000104', department: 'RMD' },
66
+ { id: '9000105', name: '劉洋', employeeId: '9000105', department: 'CMM' },
67
+ ]
68
+
69
+ export type ReportModelStep = 'select' | 'detail' | 'generating' | 'prompt'
70
+ export type PromptRightPanel = 'prompt' | 'settings'
71
+
72
+ export interface PromptChapterForm {
73
+ prompt: string
74
+ enableBodyContent: boolean
75
+ bodyWordLimit: number
76
+ enableTable: boolean
77
+ tableTitle: string
78
+ tableRowLabels: string
79
+ tableColumnLabels: string
80
+ tableContent: string
81
+ enableSourceChart: boolean
82
+ chartDescription: string
83
+ }
84
+
85
+ export function createDefaultPromptChapterForm(): PromptChapterForm {
86
+ return {
87
+ prompt: '',
88
+ enableBodyContent: false,
89
+ bodyWordLimit: 500,
90
+ enableTable: true,
91
+ tableTitle: '',
92
+ tableRowLabels: '',
93
+ tableColumnLabels: '',
94
+ tableContent: '',
95
+ enableSourceChart: true,
96
+ chartDescription: '',
97
+ }
98
+ }
99
+
100
+ export interface ReportDraft {
101
+ industry: string
102
+ researchFocus: string
103
+ year: string
104
+ reportName: string
105
+ language: string
106
+ wordsPerChapter: string
107
+ model: string
108
+ searchMode: string
109
+ weight: number
110
+ rerank: boolean
111
+ rerankTopN: number
112
+ rerankThreshold: number
113
+ sourceCount: number
114
+ }
115
+
116
+ const STORAGE_KEY = 'gen-ai-report-workflow-draft'
117
+ const TEMPLATE_STORAGE_KEY = 'gen-ai-report-workflow-template'
118
+
119
+ const draft = reactive<ReportDraft>({
120
+ industry: '',
121
+ researchFocus: '',
122
+ year: String(new Date().getFullYear()),
123
+ reportName: '',
124
+ language: 'zh-Hant',
125
+ wordsPerChapter: '',
126
+ model: 'gpt-666',
127
+ searchMode: 'Hybrid Search',
128
+ weight: 0.5,
129
+ rerank: true,
130
+ rerankTopN: 5,
131
+ rerankThreshold: 0.5,
132
+ sourceCount: 0,
133
+ })
134
+
135
+ const selectedTemplate = ref<ReportTemplateCard | null>(null)
136
+ const modelStep = ref<ReportModelStep>('select')
137
+ const editViewOpen = ref(false)
138
+ const promptRightPanel = ref<PromptRightPanel>('prompt')
139
+ const generationDone = ref(false)
140
+ const generatedChapterBodies = ref<string[]>([])
141
+ const sourceFiles = ref<SourceFile[]>([...MOCK_SOURCE_FILES])
142
+ const pendingFiles = ref<PendingFile[]>([])
143
+ const reportEditors = ref<ReportEditor[]>([...MOCK_REPORT_EDITORS])
144
+ const promptChapterForm = reactive<PromptChapterForm>(createDefaultPromptChapterForm())
145
+ const chapterEditMode = ref(false)
146
+ let chapterEditSnapshot: string | null = null
147
+
148
+ function serializeChapterEditState() {
149
+ return JSON.stringify({
150
+ prompt: { ...promptChapterForm },
151
+ sources: sourceFiles.value.map((file) => ({ id: file.id, selected: file.selected })),
152
+ })
153
+ }
154
+
155
+ function restoreChapterEditSnapshot(snapshot: string) {
156
+ const parsed = JSON.parse(snapshot) as {
157
+ prompt: PromptChapterForm
158
+ sources: Array<{ id: string; selected: boolean }>
159
+ }
160
+ Object.assign(promptChapterForm, parsed.prompt)
161
+ for (const file of sourceFiles.value) {
162
+ const saved = parsed.sources.find((item) => item.id === file.id)
163
+ if (saved) file.selected = saved.selected
164
+ }
165
+ }
166
+
167
+ function isChapterEditDirty() {
168
+ if (!chapterEditSnapshot) return false
169
+ return serializeChapterEditState() !== chapterEditSnapshot
170
+ }
171
+
172
+ function enterChapterEditMode() {
173
+ chapterEditSnapshot = serializeChapterEditState()
174
+ chapterEditMode.value = true
175
+ }
176
+
177
+ function commitChapterEdit() {
178
+ chapterEditSnapshot = serializeChapterEditState()
179
+ chapterEditMode.value = false
180
+ }
181
+
182
+ function exitChapterEditMode(revert = false) {
183
+ if (revert && chapterEditSnapshot) {
184
+ restoreChapterEditSnapshot(chapterEditSnapshot)
185
+ } else if (chapterEditMode.value) {
186
+ chapterEditSnapshot = serializeChapterEditState()
187
+ }
188
+ chapterEditMode.value = false
189
+ }
190
+
191
+ function resetChapterEditMode() {
192
+ chapterEditSnapshot = null
193
+ chapterEditMode.value = false
194
+ }
195
+
196
+ function saveDraftToSession() {
197
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ ...draft }))
198
+ }
199
+
200
+ function loadDraftFromSession() {
201
+ const raw = sessionStorage.getItem(STORAGE_KEY)
202
+ if (!raw) return
203
+ try {
204
+ const parsed = JSON.parse(raw) as Partial<ReportDraft>
205
+ Object.assign(draft, parsed)
206
+ } catch {
207
+ // ignore invalid cache
208
+ }
209
+ }
210
+
211
+ function saveTemplateToSession() {
212
+ if (!selectedTemplate.value) {
213
+ sessionStorage.removeItem(TEMPLATE_STORAGE_KEY)
214
+ return
215
+ }
216
+ sessionStorage.setItem(TEMPLATE_STORAGE_KEY, JSON.stringify(selectedTemplate.value))
217
+ }
218
+
219
+ function loadTemplateFromSession() {
220
+ const raw = sessionStorage.getItem(TEMPLATE_STORAGE_KEY)
221
+ if (!raw) return
222
+ try {
223
+ selectedTemplate.value = JSON.parse(raw) as ReportTemplateCard
224
+ } catch {
225
+ sessionStorage.removeItem(TEMPLATE_STORAGE_KEY)
226
+ }
227
+ }
228
+
229
+ watch(
230
+ sourceFiles,
231
+ () => {
232
+ draft.sourceCount = sourceFiles.value.filter((file) => file.selected).length
233
+ saveDraftToSession()
234
+ },
235
+ { deep: true },
236
+ )
237
+
238
+ export function useReportWorkflow() {
239
+ const hasTemplate = computed(() => !!selectedTemplate.value)
240
+
241
+ const currentUserEditor = computed(
242
+ () => reportEditors.value.find((editor) => editor.id === CURRENT_USER_ID) ?? null,
243
+ )
244
+ const isReportInitiator = computed(() => currentUserEditor.value?.role === 'initiator')
245
+ const isReportEditor = computed(() => currentUserEditor.value?.role === 'editor')
246
+ const canManageReportEditors = computed(() => isReportInitiator.value)
247
+ const canEditReport = computed(() => isReportInitiator.value || isReportEditor.value)
248
+
249
+ function patchDraft(partial: Partial<ReportDraft>) {
250
+ Object.assign(draft, partial)
251
+ saveDraftToSession()
252
+ }
253
+
254
+ function setSelectedTemplate(template: ReportTemplateCard | null) {
255
+ selectedTemplate.value = template
256
+ saveTemplateToSession()
257
+ }
258
+
259
+ function setModelStep(step: ReportModelStep) {
260
+ modelStep.value = step
261
+ if (step !== 'prompt') {
262
+ promptRightPanel.value = 'prompt'
263
+ resetChapterEditMode()
264
+ }
265
+ }
266
+
267
+ function showPromptSettingsPanel() {
268
+ exitChapterEditMode(true)
269
+ promptRightPanel.value = 'settings'
270
+ }
271
+
272
+ function showPromptEditorPanel() {
273
+ promptRightPanel.value = 'prompt'
274
+ }
275
+
276
+ function openReportEdit(step: ReportModelStep = 'select') {
277
+ editViewOpen.value = true
278
+ setModelStep(step)
279
+ if (step === 'prompt') {
280
+ promptRightPanel.value = 'prompt'
281
+ }
282
+ }
283
+
284
+ function closeReportEdit() {
285
+ editViewOpen.value = false
286
+ }
287
+
288
+ function setGeneratedChapterBodies(bodies: string[]) {
289
+ generatedChapterBodies.value = bodies
290
+ }
291
+
292
+ function markGenerationSuccess() {
293
+ generationDone.value = true
294
+ editViewOpen.value = true
295
+ modelStep.value = 'prompt'
296
+ promptRightPanel.value = 'prompt'
297
+ }
298
+
299
+ function resetWorkflow() {
300
+ selectedTemplate.value = null
301
+ modelStep.value = 'select'
302
+ generationDone.value = false
303
+ }
304
+
305
+ return {
306
+ draft,
307
+ selectedTemplate,
308
+ modelStep,
309
+ editViewOpen,
310
+ promptRightPanel,
311
+ generationDone,
312
+ generatedChapterBodies,
313
+ sourceFiles,
314
+ pendingFiles,
315
+ reportEditors,
316
+ currentUserEditor,
317
+ isReportInitiator,
318
+ isReportEditor,
319
+ canManageReportEditors,
320
+ canEditReport,
321
+ hasTemplate,
322
+ promptChapterForm,
323
+ chapterEditMode,
324
+ enterChapterEditMode,
325
+ commitChapterEdit,
326
+ exitChapterEditMode,
327
+ isChapterEditDirty,
328
+ resetChapterEditMode,
329
+ saveDraftToSession,
330
+ loadDraftFromSession,
331
+ loadTemplateFromSession,
332
+ patchDraft,
333
+ setSelectedTemplate,
334
+ setModelStep,
335
+ showPromptSettingsPanel,
336
+ showPromptEditorPanel,
337
+ openReportEdit,
338
+ closeReportEdit,
339
+ setGeneratedChapterBodies,
340
+ markGenerationSuccess,
341
+ resetWorkflow,
342
+ }
343
+ }
@@ -0,0 +1,109 @@
1
+ import type { RouteLocationNormalizedLoaded, Router } from 'vue-router'
2
+ import { resolveReportTemplateById, type ReportTemplateCard } from '../report-template-data'
3
+ import type { ReportModelStep, PromptRightPanel } from '../useReportWorkflow'
4
+
5
+ export const BUILD_REPORT_PATH = '/gen-ai/build-report'
6
+ export const REPORT_EDIT_PAGE_NAME = 'reportEdit'
7
+
8
+ const VALID_STEPS = new Set<ReportModelStep>(['select', 'detail', 'generating', 'prompt'])
9
+
10
+ export function isReportEditRoute(route: RouteLocationNormalizedLoaded) {
11
+ return route.query.pageName === REPORT_EDIT_PAGE_NAME
12
+ }
13
+
14
+ export function parseReportEditStep(route: RouteLocationNormalizedLoaded): ReportModelStep | null {
15
+ if (!isReportEditRoute(route)) return null
16
+ const step = route.query.step
17
+ if (step === 'generate-settings') return 'prompt'
18
+ if (typeof step === 'string' && VALID_STEPS.has(step as ReportModelStep)) {
19
+ return step as ReportModelStep
20
+ }
21
+ return 'select'
22
+ }
23
+
24
+ export function parsePromptRightPanel(route: RouteLocationNormalizedLoaded): PromptRightPanel {
25
+ if (route.query.step === 'generate-settings' || route.query.rightPanel === 'settings') {
26
+ return 'settings'
27
+ }
28
+ return 'prompt'
29
+ }
30
+
31
+ export function buildReportEditQuery(step: ReportModelStep, templateId?: string, rightPanel?: PromptRightPanel) {
32
+ const query: Record<string, string> = {
33
+ pageName: REPORT_EDIT_PAGE_NAME,
34
+ step,
35
+ }
36
+ if (templateId) query.templateId = templateId
37
+ if (step === 'prompt' && rightPanel === 'settings') query.rightPanel = 'settings'
38
+ return query
39
+ }
40
+
41
+ export function navigateToReportEdit(
42
+ router: Router,
43
+ step: ReportModelStep = 'select',
44
+ templateId?: string,
45
+ replace = false,
46
+ rightPanel?: PromptRightPanel,
47
+ ) {
48
+ const location = { path: BUILD_REPORT_PATH, query: buildReportEditQuery(step, templateId, rightPanel) }
49
+ return replace ? router.replace(location) : router.push(location)
50
+ }
51
+
52
+ export function exitReportEdit(router: Router) {
53
+ return router.replace({ path: BUILD_REPORT_PATH })
54
+ }
55
+
56
+ interface ReportEditRouteSyncActions {
57
+ openReportEdit: (step: ReportModelStep) => void
58
+ closeReportEdit: () => void
59
+ setSelectedTemplate: (template: ReportTemplateCard | null) => void
60
+ loadTemplateFromSession: () => void
61
+ showPromptSettingsPanel: () => void
62
+ showPromptEditorPanel: () => void
63
+ }
64
+
65
+ export function createReportEditRouteSyncActions(
66
+ workflow: ReturnType<typeof import('../useReportWorkflow').useReportWorkflow>,
67
+ ): ReportEditRouteSyncActions {
68
+ return {
69
+ openReportEdit: workflow.openReportEdit,
70
+ closeReportEdit: workflow.closeReportEdit,
71
+ setSelectedTemplate: workflow.setSelectedTemplate,
72
+ loadTemplateFromSession: workflow.loadTemplateFromSession,
73
+ showPromptSettingsPanel: workflow.showPromptSettingsPanel,
74
+ showPromptEditorPanel: workflow.showPromptEditorPanel,
75
+ }
76
+ }
77
+
78
+ export function syncWorkflowFromReportEditRoute(
79
+ route: RouteLocationNormalizedLoaded,
80
+ actions: ReportEditRouteSyncActions,
81
+ ) {
82
+ const step = parseReportEditStep(route)
83
+ if (!step) {
84
+ actions.closeReportEdit()
85
+ return
86
+ }
87
+
88
+ actions.openReportEdit(step)
89
+
90
+ const rightPanel = parsePromptRightPanel(route)
91
+ if (step === 'prompt' && rightPanel === 'settings') {
92
+ actions.showPromptSettingsPanel()
93
+ } else if (step === 'prompt') {
94
+ actions.showPromptEditorPanel()
95
+ }
96
+
97
+ const templateId = route.query.templateId
98
+ if (typeof templateId === 'string') {
99
+ const found = resolveReportTemplateById(templateId)
100
+ if (found) {
101
+ actions.setSelectedTemplate(found)
102
+ return
103
+ }
104
+ }
105
+
106
+ if (step === 'detail' || step === 'generating' || step === 'prompt') {
107
+ actions.loadTemplateFromSession()
108
+ }
109
+ }