From 291988ae56376064e0856bb612fca6f5abc4cb18 Mon Sep 17 00:00:00 2001 From: Varenik-vkusny Date: Thu, 9 Apr 2026 14:30:18 +0500 Subject: [PATCH 1/2] Arch refactoring --- components.d.ts | 37 +- local_build_log.txt | Bin 0 -> 3512 bytes src/App.vue | 6 +- src/components/chat/ChartDisplay.vue | 8 +- src/components/chat/ChatInputArea.vue | 2 +- src/components/chat/ChatMessageItem.vue | 252 +------ src/components/chat/ChatMessageList.vue | 2 +- src/components/chat/JiraDeleteCard.vue | 6 +- src/components/chat/JiraTaskCard.vue | 13 +- src/components/github/GithubCommitDetail.vue | 4 +- src/components/github/GithubCommitList.vue | 2 +- src/components/github/GithubHeader.vue | 2 +- src/components/landing/FinalCTA.vue | 4 +- src/components/landing/IntegrationOrbit.vue | 6 +- src/components/landing/LandingPricing.vue | 4 +- src/components/landing/NeuralStream.vue | 4 +- src/components/{ => layout}/CockpitLayout.vue | 14 +- .../{ => layout}/GlobalErrorModal.vue | 4 +- src/components/{ => layout}/Navbar.vue | 6 +- src/components/{ => layout}/ProfileDrawer.vue | 6 +- src/components/main/AgentMainContent.vue | 5 +- src/components/main/RadarMainContent.vue | 10 +- src/components/meetings/MeetingsDetail.vue | 4 +- src/components/meetings/MeetingsHistory.vue | 2 +- src/components/sidebar/AppSidebar.vue | 220 +----- src/components/sidebar/ChatSidebar.vue | 191 +++++ .../{ui => widgets}/ActionItemList.vue | 0 .../{ui => widgets}/AnalysisCard.vue | 0 .../{ui => widgets}/AnimatedBeam.vue | 0 .../{ui => widgets}/AnimatedContent.vue | 0 .../{ui => widgets}/AuthParticles.vue | 0 .../{ui => widgets}/DashboardSkeleton.vue | 0 .../{ui => widgets}/DashboardWidget.vue | 0 .../{ui => widgets}/DetailHeader.vue | 0 src/components/{ui => widgets}/EmptyState.vue | 4 +- .../{ui => widgets}/IntegrationLock.vue | 4 +- .../PostOnboardingTutorial.vue | 0 .../{ui => widgets}/ProposalsWidget.vue | 4 +- .../{ui => widgets}/SupportModal.vue | 0 src/components/{ui => widgets}/VoicePulse.vue | 0 src/composables/useChatSessions.ts | 77 ++ src/composables/useChatTaskActions.ts | 234 ++++++ src/mocks/handlers.ts | 670 +----------------- src/mocks/handlers/auth.handlers.ts | 63 ++ src/mocks/handlers/chat.handlers.ts | 253 +++++++ src/mocks/handlers/github.handlers.ts | 45 ++ src/mocks/handlers/jira.handlers.ts | 77 ++ src/mocks/handlers/meetings.handlers.ts | 76 ++ src/mocks/handlers/proposals.handlers.ts | 125 ++++ src/mocks/handlers/utils.ts | 31 + src/router/index.ts | 7 +- src/stores/chat.ts | 55 +- src/stores/dashboard.ts | 53 +- src/stores/github.ts | 49 +- src/types/chat.ts | 51 ++ src/types/dashboard.ts | 50 ++ src/types/github.ts | 48 ++ src/types/index.ts | 4 + src/types/jira.ts | 21 + src/views/AuthView.vue | 4 +- src/views/GithubView.vue | 2 +- src/views/HomeView.vue | 2 +- src/views/LoginV2View.vue | 13 - src/views/PricingView.vue | 2 +- src/views/PrivacyView.vue | 2 +- src/views/ProposalsView.vue | 6 +- src/views/TermsView.vue | 2 +- 67 files changed, 1484 insertions(+), 1364 deletions(-) create mode 100644 local_build_log.txt rename src/components/{ => layout}/CockpitLayout.vue (88%) rename src/components/{ => layout}/GlobalErrorModal.vue (97%) rename src/components/{ => layout}/Navbar.vue (98%) rename src/components/{ => layout}/ProfileDrawer.vue (97%) create mode 100644 src/components/sidebar/ChatSidebar.vue rename src/components/{ui => widgets}/ActionItemList.vue (100%) rename src/components/{ui => widgets}/AnalysisCard.vue (100%) rename src/components/{ui => widgets}/AnimatedBeam.vue (100%) rename src/components/{ui => widgets}/AnimatedContent.vue (100%) rename src/components/{ui => widgets}/AuthParticles.vue (100%) rename src/components/{ui => widgets}/DashboardSkeleton.vue (100%) rename src/components/{ui => widgets}/DashboardWidget.vue (100%) rename src/components/{ui => widgets}/DetailHeader.vue (100%) rename src/components/{ui => widgets}/EmptyState.vue (97%) rename src/components/{ui => widgets}/IntegrationLock.vue (97%) rename src/components/{ui => widgets}/PostOnboardingTutorial.vue (100%) rename src/components/{ui => widgets}/ProposalsWidget.vue (99%) rename src/components/{ui => widgets}/SupportModal.vue (100%) rename src/components/{ui => widgets}/VoicePulse.vue (100%) create mode 100644 src/composables/useChatSessions.ts create mode 100644 src/composables/useChatTaskActions.ts create mode 100644 src/mocks/handlers/auth.handlers.ts create mode 100644 src/mocks/handlers/chat.handlers.ts create mode 100644 src/mocks/handlers/github.handlers.ts create mode 100644 src/mocks/handlers/jira.handlers.ts create mode 100644 src/mocks/handlers/meetings.handlers.ts create mode 100644 src/mocks/handlers/proposals.handlers.ts create mode 100644 src/mocks/handlers/utils.ts create mode 100644 src/types/chat.ts create mode 100644 src/types/dashboard.ts create mode 100644 src/types/github.ts create mode 100644 src/types/index.ts create mode 100644 src/types/jira.ts delete mode 100644 src/views/LoginV2View.vue diff --git a/components.d.ts b/components.d.ts index 60ef14c..e764e7a 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,7 +11,7 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { - ActionItemList: typeof import('./src/components/ui/ActionItemList.vue')['default'] + ActionItemList: typeof import('./src/components/widgets/ActionItemList.vue')['default'] AgentMainContent: typeof import('./src/components/main/AgentMainContent.vue')['default'] Alert: typeof import('./src/components/ui/alert/Alert.vue')['default'] AlertDescription: typeof import('./src/components/ui/alert/AlertDescription.vue')['default'] @@ -25,13 +25,13 @@ declare module 'vue' { AlertDialogTitle: typeof import('./src/components/ui/alert-dialog/AlertDialogTitle.vue')['default'] AlertDialogTrigger: typeof import('./src/components/ui/alert-dialog/AlertDialogTrigger.vue')['default'] AlertTitle: typeof import('./src/components/ui/alert/AlertTitle.vue')['default'] - AnalysisCard: typeof import('./src/components/ui/AnalysisCard.vue')['default'] - AnimatedBeam: typeof import('./src/components/ui/AnimatedBeam.vue')['default'] - AnimatedContent: typeof import('./src/components/ui/AnimatedContent.vue')['default'] + AnalysisCard: typeof import('./src/components/widgets/AnalysisCard.vue')['default'] + AnimatedBeam: typeof import('./src/components/widgets/AnimatedBeam.vue')['default'] + AnimatedContent: typeof import('./src/components/widgets/AnimatedContent.vue')['default'] AppSidebar: typeof import('./src/components/sidebar/AppSidebar.vue')['default'] ArtifactPanel: typeof import('./src/components/chat/ArtifactPanel.vue')['default'] AudioStreamHero: typeof import('./src/components/landing/AudioStreamHero.vue')['default'] - AuthParticles: typeof import('./src/components/ui/AuthParticles.vue')['default'] + AuthParticles: typeof import('./src/components/widgets/AuthParticles.vue')['default'] Avatar: typeof import('./src/components/ui/avatar/Avatar.vue')['default'] AvatarFallback: typeof import('./src/components/ui/avatar/AvatarFallback.vue')['default'] AvatarImage: typeof import('./src/components/ui/avatar/AvatarImage.vue')['default'] @@ -54,7 +54,8 @@ declare module 'vue' { ChatMessageItem: typeof import('./src/components/chat/ChatMessageItem.vue')['default'] ChatMessageList: typeof import('./src/components/chat/ChatMessageList.vue')['default'] ChatModals: typeof import('./src/components/modals/ChatModals.vue')['default'] - CockpitLayout: typeof import('./src/components/CockpitLayout.vue')['default'] + ChatSidebar: typeof import('./src/components/sidebar/ChatSidebar.vue')['default'] + CockpitLayout: typeof import('./src/components/layout/CockpitLayout.vue')['default'] Command: typeof import('./src/components/ui/command/Command.vue')['default'] CommandDialog: typeof import('./src/components/ui/command/CommandDialog.vue')['default'] CommandEmpty: typeof import('./src/components/ui/command/CommandEmpty.vue')['default'] @@ -67,9 +68,9 @@ declare module 'vue' { ConfirmModal: typeof import('./src/components/modals/ConfirmModal.vue')['default'] CreateProposalModal: typeof import('./src/components/proposals/CreateProposalModal.vue')['default'] DashboardHeader: typeof import('./src/components/dashboard/DashboardHeader.vue')['default'] - DashboardSkeleton: typeof import('./src/components/ui/DashboardSkeleton.vue')['default'] - DashboardWidget: typeof import('./src/components/ui/DashboardWidget.vue')['default'] - DetailHeader: typeof import('./src/components/ui/DetailHeader.vue')['default'] + DashboardSkeleton: typeof import('./src/components/widgets/DashboardSkeleton.vue')['default'] + DashboardWidget: typeof import('./src/components/widgets/DashboardWidget.vue')['default'] + DetailHeader: typeof import('./src/components/widgets/DetailHeader.vue')['default'] Dialog: typeof import('./src/components/ui/dialog/Dialog.vue')['default'] DialogClose: typeof import('./src/components/ui/dialog/DialogClose.vue')['default'] DialogContent: typeof import('./src/components/ui/dialog/DialogContent.vue')['default'] @@ -93,7 +94,7 @@ declare module 'vue' { DropdownMenuSubContent: typeof import('./src/components/ui/dropdown-menu/DropdownMenuSubContent.vue')['default'] DropdownMenuSubTrigger: typeof import('./src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue')['default'] DropdownMenuTrigger: typeof import('./src/components/ui/dropdown-menu/DropdownMenuTrigger.vue')['default'] - EmptyState: typeof import('./src/components/ui/EmptyState.vue')['default'] + EmptyState: typeof import('./src/components/widgets/EmptyState.vue')['default'] FinalCTA: typeof import('./src/components/landing/FinalCTA.vue')['default'] FormControl: typeof import('./src/components/ui/form/FormControl.vue')['default'] FormDescription: typeof import('./src/components/ui/form/FormDescription.vue')['default'] @@ -105,7 +106,7 @@ declare module 'vue' { GithubHeader: typeof import('./src/components/github/GithubHeader.vue')['default'] GitHubReposModal: typeof import('./src/components/modals/GitHubReposModal.vue')['default'] GithubSidebar: typeof import('./src/components/sidebar/GithubSidebar.vue')['default'] - GlobalErrorModal: typeof import('./src/components/GlobalErrorModal.vue')['default'] + GlobalErrorModal: typeof import('./src/components/layout/GlobalErrorModal.vue')['default'] HeroMockup: typeof import('./src/components/landing/HeroMockup.vue')['default'] IMaterialSymbolsAdd: typeof import('~icons/material-symbols/add')['default'] IMaterialSymbolsCheck: typeof import('~icons/material-symbols/check')['default'] @@ -116,7 +117,7 @@ declare module 'vue' { IMaterialSymbolsMoreVert: typeof import('~icons/material-symbols/more-vert')['default'] IMaterialSymbolsPerson: typeof import('~icons/material-symbols/person')['default'] Input: typeof import('./src/components/ui/input/Input.vue')['default'] - IntegrationLock: typeof import('./src/components/ui/IntegrationLock.vue')['default'] + IntegrationLock: typeof import('./src/components/widgets/IntegrationLock.vue')['default'] IntegrationOrbit: typeof import('./src/components/landing/IntegrationOrbit.vue')['default'] IntegrationSuccessModal: typeof import('./src/components/modals/IntegrationSuccessModal.vue')['default'] JiraDeleteCard: typeof import('./src/components/chat/JiraDeleteCard.vue')['default'] @@ -129,24 +130,24 @@ declare module 'vue' { MeetingsDetail: typeof import('./src/components/meetings/MeetingsDetail.vue')['default'] MeetingsHistory: typeof import('./src/components/meetings/MeetingsHistory.vue')['default'] MeetingsSidebar: typeof import('./src/components/sidebar/MeetingsSidebar.vue')['default'] - Navbar: typeof import('./src/components/Navbar.vue')['default'] + Navbar: typeof import('./src/components/layout/Navbar.vue')['default'] NeuralStream: typeof import('./src/components/landing/NeuralStream.vue')['default'] NotionConfigModal: typeof import('./src/components/modals/NotionConfigModal.vue')['default'] PinnedContextBar: typeof import('./src/components/chat/PinnedContextBar.vue')['default'] Popover: typeof import('./src/components/ui/popover/Popover.vue')['default'] PopoverContent: typeof import('./src/components/ui/popover/PopoverContent.vue')['default'] PopoverTrigger: typeof import('./src/components/ui/popover/PopoverTrigger.vue')['default'] - PostOnboardingTutorial: typeof import('./src/components/ui/PostOnboardingTutorial.vue')['default'] + PostOnboardingTutorial: typeof import('./src/components/widgets/PostOnboardingTutorial.vue')['default'] PricingMatrixModal: typeof import('./src/components/modals/PricingMatrixModal.vue')['default'] ProfileDetailsForm: typeof import('./src/components/profile/ProfileDetailsForm.vue')['default'] - ProfileDrawer: typeof import('./src/components/ProfileDrawer.vue')['default'] + ProfileDrawer: typeof import('./src/components/layout/ProfileDrawer.vue')['default'] ProfileIntegrationsList: typeof import('./src/components/profile/ProfileIntegrationsList.vue')['default'] ProfileSecurityForm: typeof import('./src/components/profile/ProfileSecurityForm.vue')['default'] ProposalApprovalModal: typeof import('./src/components/modals/ProposalApprovalModal.vue')['default'] ProposalDetail: typeof import('./src/components/proposals/ProposalDetail.vue')['default'] ProposalsList: typeof import('./src/components/proposals/ProposalsList.vue')['default'] ProposalsSidebar: typeof import('./src/components/sidebar/ProposalsSidebar.vue')['default'] - ProposalsWidget: typeof import('./src/components/ui/ProposalsWidget.vue')['default'] + ProposalsWidget: typeof import('./src/components/widgets/ProposalsWidget.vue')['default'] RadarMainContent: typeof import('./src/components/main/RadarMainContent.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] @@ -172,7 +173,7 @@ declare module 'vue' { SheetTrigger: typeof import('./src/components/ui/sheet/SheetTrigger.vue')['default'] SiteFooter: typeof import('./src/components/layout/SiteFooter.vue')['default'] Sonner: typeof import('./src/components/ui/sonner/Sonner.vue')['default'] - SupportModal: typeof import('./src/components/ui/SupportModal.vue')['default'] + SupportModal: typeof import('./src/components/widgets/SupportModal.vue')['default'] Switch: typeof import('./src/components/ui/switch/Switch.vue')['default'] TaskCard: typeof import('./src/components/dashboard/TaskCard.vue')['default'] TaskModal: typeof import('./src/components/dashboard/TaskModal.vue')['default'] @@ -183,7 +184,7 @@ declare module 'vue' { TooltipTrigger: typeof import('./src/components/ui/tooltip/TooltipTrigger.vue')['default'] TourCursor: typeof import('./src/components/tutorial/TourCursor.vue')['default'] TourOverlay: typeof import('./src/components/tutorial/TourOverlay.vue')['default'] - VoicePulse: typeof import('./src/components/ui/VoicePulse.vue')['default'] + VoicePulse: typeof import('./src/components/widgets/VoicePulse.vue')['default'] WaitlistModal: typeof import('./src/components/modals/WaitlistModal.vue')['default'] } } diff --git a/local_build_log.txt b/local_build_log.txt new file mode 100644 index 0000000000000000000000000000000000000000..3d2180d28c53c7de6d4811f395db77c0df8089b1 GIT binary patch literal 3512 zcmcJS-ESLJ5XI-gPe_$`MZBy`64o39v)8+cohS)J*6!Yqxie>G&YgSx&!>C#nSE!8%`La7&8%Y!+p-^Q)6(im?8r`S zY!e$H_sn|Mw_mJ8zTn+w&f9(ak+IVLMArv2d4IvmBgVJs4`_Fonb4ZsJERJ1c4!r} zPXdM$tVB!;bmz>^(K^I-2Th4>@Jm2B4)RTOTBqkrdyBn2bk|Y~`xcM3dAh82i7g~* z-^L&~W^M?+F?NL~314Y(gwy4?qfhWgGb4}=ksk*?Qmmv66mIPa)`V`(iUHcoWdz)^ zud4A2-9hV7>#J71J|Q<?JNAjy42~%G1K98eOapAY_kFa^==FJfE2UN9 zZ4X;JSX5+&_6KVU#&$#h8^+^LtZ1`V*~smFTDgFC}Xl6?k z8Eb^F9!Wv*9xc**KcG3TV$ekMx83DW{#>PB^D!<$^$eWa;m1U$urqs(M`Cc4X@13w zSIo`rXQGhf?HF0@h*4n8gNoUaH=Y^SaC;8*uqFmO8y5Ubs5MQKdc$cfkotWH^_id0Lx z4ZVF=DhjHEt}8+Il(u`(tWCXuyIv#xLMPketZGexrjqs~AP}2X6Eft+yp=Q0q3TEU z`33SrYHrFJRZFo_=)9&*>9x^WAd~UPry1`PB&DrnR6YuCX8Z8N$6r=sLK*Awd}-tR z%GL%N&Vn`Z<}6njtk76IJ`zA2)|=#uqzBZ`h%=S&{5;jvN`S5?Ux zXO1G`XHItE%<+DG=48Q-b!Sc;qv+oV8+R2cS@US!Hc~Znq$}=M@gNCVn}>QTwkzT_ zUlV%}cCSY@!^iAK@su^4>(`0t#dG|s`l{;gbv}a|;=GW{+Kk)B4*b{+Cw3RSv8Jz9 z>3Zc1IaT$Nh-Bauv&-tVP~}qn*2y8>><1}78CA)9*xTfk(YI?41B&wzZtn2^7SGjK z%IE9di>`{q`gpF!Qr7M&o*v6>dt#46EVn`=*TwTC8CRtgD^#^;j-#q>uVzCY-bKY&&u&>0_Em0PHzPO6hOAxA@TjQ+9#u7+^l7-O=nmE8 z*7%q`<@c#|S<%^e+d8qz>+_rz^2_e~{|B$5@16(Git|gA;3jn z;_d#R^?B=y)*-BZ&i&&MCjQFvf|k$dF4nwRBvfaezlyu=0)Nq-vkUak<6~&+ABo>- J6LRQP>t8}$N_+qS literal 0 HcmV?d00001 diff --git a/src/App.vue b/src/App.vue index 0be827f..59c0945 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,11 +2,11 @@ import { computed, ref, onMounted, onUnmounted } from "vue"; import { Icon } from "@iconify/vue"; import { useRoute, useRouter } from "vue-router"; -import Navbar from "./components/Navbar.vue"; +import Navbar from "./components/layout/Navbar.vue"; import WaitlistModal from "./components/modals/WaitlistModal.vue"; -import GlobalErrorModal from "./components/GlobalErrorModal.vue"; +import GlobalErrorModal from "./components/layout/GlobalErrorModal.vue"; import DevTools from "./mocks/DevTools.vue"; -import SupportModal from "./components/ui/SupportModal.vue"; +import SupportModal from "./components/widgets/SupportModal.vue"; import { Toaster } from "@/components/ui/sonner"; import { useAuthStore } from "./stores/auth"; import { useDashboardStore } from "./stores/dashboard"; diff --git a/src/components/chat/ChartDisplay.vue b/src/components/chat/ChartDisplay.vue index 6197b31..2b0ccac 100644 --- a/src/components/chat/ChartDisplay.vue +++ b/src/components/chat/ChartDisplay.vue @@ -35,13 +35,7 @@ use([ LegendComponent, ]) -interface ChartConfig { - type: 'bar' | 'pie' | 'line' - title?: string - xData?: string[] - yData?: number[] - data?: { name: string; value: number }[] -} +import type { ChartConfig } from '@/types/chat'; const props = defineProps<{ chart: ChartConfig }>() diff --git a/src/components/chat/ChatInputArea.vue b/src/components/chat/ChatInputArea.vue index c633148..d754bec 100644 --- a/src/components/chat/ChatInputArea.vue +++ b/src/components/chat/ChatInputArea.vue @@ -11,7 +11,7 @@ import { } from '@/components/ui/dropdown-menu'; import { Textarea } from '@/components/ui/textarea'; import { useVoiceInput } from '@/composables/useVoiceInput'; -import VoicePulse from '@/components/ui/VoicePulse.vue'; +import VoicePulse from '@/components/widgets/VoicePulse.vue'; import { toast } from 'vue-sonner'; const chatStore = useChatStore(); diff --git a/src/components/chat/ChatMessageItem.vue b/src/components/chat/ChatMessageItem.vue index a5926c8..db72d6c 100644 --- a/src/components/chat/ChatMessageItem.vue +++ b/src/components/chat/ChatMessageItem.vue @@ -6,7 +6,6 @@ import { useI18n } from 'vue-i18n'; import JiraTaskCard from './JiraTaskCard.vue'; import JiraDeleteCard from './JiraDeleteCard.vue'; import ChartDisplay from './ChartDisplay.vue'; -import { apiClient } from '@/api/client'; import { DropdownMenu, DropdownMenuContent, @@ -15,19 +14,18 @@ import { DropdownMenuSeparator } from '@/components/ui/dropdown-menu'; import { useChatStore } from '@/stores/chat'; -import { useDashboardStore } from '@/stores/dashboard'; -import { toast } from 'vue-sonner'; -import { highlightCode } from '@/composables/useCodeHighlight' +import { highlightCode } from '@/composables/useCodeHighlight'; import { PanelRight } from 'lucide-vue-next'; import { detectArtifactType } from '@/composables/useArtifactPanel'; -import { usePinnedContext } from '@/composables/usePinnedContext' -import { useChat } from '@/composables/useChat' +import { usePinnedContext } from '@/composables/usePinnedContext'; +import { useChat } from '@/composables/useChat'; +import { useChatTaskActions } from '@/composables/useChatTaskActions'; +import { useDashboardStore } from '@/stores/dashboard'; marked.use({ gfm: true, breaks: true }); const chatStore = useChatStore(); const dashboardStore = useDashboardStore(); - const { currentSessionId } = useChat(); const { addPin } = usePinnedContext(currentSessionId); @@ -39,232 +37,28 @@ const props = defineProps<{ canUseAutoTasks: boolean; }>(); -interface JiraTask { - id: string; - title: string; - issueType?: string; - priority?: string; - assignee?: string; - description?: string; - labels?: string[]; - deadline?: string; - sprintId?: number | null; -} - -const localJiraTasks = ref([]); -const creatingIds = ref>(new Set()); -const createdIds = ref>(new Set()); -const expandedTaskId = ref(null); - -interface DeleteTask { - issue_key: string; - summary: string; -} - -const localDeleteTasks = ref([]); -const deletingKeys = ref>(new Set()); -const deletedKeys = ref>(new Set()); +const { + localJiraTasks, creatingIds, createdIds, expandedTaskId, isCreatingAll, + updateTask, createTask, createAllTasks, pinTask, + localDeleteTasks, deletingKeys, deletedKeys, + showBulkDeleteModal, bulkDeleteInput, isBulkDeleting, + deleteSingleTask, skipDeleteTask, confirmBulkDelete, deleteAllTasks, +} = useChatTaskActions(props.msg); -// Bulk delete confirmation modal state -const showBulkDeleteModal = ref(false); -const bulkDeleteInput = ref(''); -const isBulkDeleting = ref(false); -const isCreatingAll = ref(false); const isApproving = ref(false); const bulkDeletePhrase = computed(() => `delete ${localDeleteTasks.value.filter(t => !deletedKeys.value.has(t.issue_key)).length} tasks` ); -watch(() => props.msg.jiraTasks, (tasks) => { - if (!tasks) return; - localJiraTasks.value = tasks.map((t: any, idx: number) => ({ - id: t.id || `legacy-${props.msg.id}-${idx}`, - title: t.title, - issueType: t.issue_type || t.issueType || 'Task', - priority: t.priority || 'Medium', - assignee: t.assignee || '', - description: t.description || '', - labels: t.labels || [], - deadline: t.deadline || '', - sprintId: t.sprintId ?? t.sprint_id ?? null, - })); - // Restore created state persisted to DB - createdIds.value = new Set( - tasks.filter((t: any) => t.created && t.id).map((t: any) => t.id) - ); -}, { immediate: true }); - -watch(() => props.msg.deleteTasks, (tasks) => { - if (!tasks) return; - localDeleteTasks.value = tasks.map((t: any) => ({ - issue_key: t.issue_key, - summary: t.summary || '', - })); - // Restore deleted state persisted to DB - deletedKeys.value = new Set( - tasks.filter((t: any) => t.deleted).map((t: any) => t.issue_key) - ); -}, { immediate: true }); - -// True when msg.id is a real DB integer, not the local fallback 'msg-N' -const hasRealMessageId = () => - props.msg.id != null && !String(props.msg.id).startsWith('msg-'); - -const deleteSingleTask = async (issue_key: string) => { - deletingKeys.value.add(issue_key); - try { - await apiClient.post('/api/agents/delete-issues', { issue_keys: [issue_key] }); - deletedKeys.value.add(issue_key); - toast.success(`${issue_key} deleted`); - if (hasRealMessageId()) { - apiClient.patch(`/api/messages/${props.msg.id}/task-actions`, { - deleted_issue_keys: [issue_key], - }).catch(() => {}); - } - dashboardStore.invalidateCache(); - dashboardStore.fetchDashboard().catch(() => {}); - } catch { - toast.error(`Failed to delete ${issue_key}`); - } finally { - deletingKeys.value.delete(issue_key); - } -}; - -const skipDeleteTask = (issue_key: string) => { - localDeleteTasks.value = localDeleteTasks.value.filter(t => t.issue_key !== issue_key); -}; - -const openBulkDeleteModal = () => { - bulkDeleteInput.value = ''; - showBulkDeleteModal.value = true; -}; - -const confirmBulkDelete = async () => { - const pending = localDeleteTasks.value.filter(t => !deletedKeys.value.has(t.issue_key)); - const pendingKeys = pending.map(t => t.issue_key); - isBulkDeleting.value = true; - try { - await apiClient.post('/api/agents/delete-issues', { - issue_keys: pendingKeys, - }); - pending.forEach(t => deletedKeys.value.add(t.issue_key)); - toast.success(`Deleted ${pending.length} tasks`); - if (hasRealMessageId()) { - apiClient.patch(`/api/messages/${props.msg.id}/task-actions`, { - deleted_issue_keys: pendingKeys, - }).catch(() => {}); - } - dashboardStore.invalidateCache(); - dashboardStore.fetchDashboard().catch(() => {}); - } catch { - toast.error('Failed to delete tasks'); - } finally { - isBulkDeleting.value = false; - showBulkDeleteModal.value = false; - bulkDeleteInput.value = ''; - } -}; - -const deleteAllTasks = async () => { - const pending = localDeleteTasks.value.filter(t => !deletedKeys.value.has(t.issue_key)); - if (pending.length > 3) { - openBulkDeleteModal(); - } else { - // Direct delete for ≤3 tasks - const pendingKeys = pending.map(t => t.issue_key); - isBulkDeleting.value = true; - try { - await apiClient.post('/api/agents/delete-issues', { - issue_keys: pendingKeys, - }); - pending.forEach(t => deletedKeys.value.add(t.issue_key)); - toast.success(`Deleted ${pending.length} task${pending.length > 1 ? 's' : ''}`); - if (hasRealMessageId()) { - apiClient.patch(`/api/messages/${props.msg.id}/task-actions`, { - deleted_issue_keys: pendingKeys, - }).catch(() => {}); - } - dashboardStore.invalidateCache(); - dashboardStore.fetchDashboard().catch(() => {}); - } catch { - toast.error('Failed to delete tasks'); - } finally { - isBulkDeleting.value = false; - } - } -}; - -const updateTask = (id: string, patch: Partial) => { - const task = localJiraTasks.value.find(t => t.id === id); - if (task) Object.assign(task, patch); -}; - -const createTask = async (id: string) => { - const task = localJiraTasks.value.find(t => t.id === id); - if (!task) return; - creatingIds.value.add(id); - try { - await apiClient.post('/api/jira/tasks', { - summary: task.title, - priority: task.priority || 'Medium', - type: task.issueType || 'Task', - assignee: task.assignee || undefined, - description: task.description || '', - project_key: dashboardStore.selectedProjectId || undefined, - sprint_id: task.sprintId || undefined, - }); - createdIds.value.add(id); - toast.success('Task created in Jira'); - if (hasRealMessageId()) { - apiClient.patch(`/api/messages/${props.msg.id}/task-actions`, { - created_task_ids: [id], - }).catch(() => {}); - } - } catch { - toast.error('Failed to create task'); - } finally { - creatingIds.value.delete(id); - } -}; - -const createAllTasks = async () => { - const pending = localJiraTasks.value.filter(t => !createdIds.value.has(t.id)); - isCreatingAll.value = true; - try { - for (const task of pending) { - await createTask(task.id); - } - // Single authoritative refresh after all tasks are in Jira. - // Each createTask() triggers dashboardStore.fetchDashboard() but the - // _inflightDashboard dedup guard prevents concurrent fetches. - // This final call ensures the board reflects all created tasks. - dashboardStore.invalidateCache(); - dashboardStore.fetchDashboard().catch(() => {}); - } finally { - isCreatingAll.value = false; - } -}; - -const pinTask = (task: JiraTask) => { - addPin({ - type: 'jira', - label: task.title, - issueKey: task.id, - issueTitle: task.title - }); - toast.success('Task pinned to session'); -}; - -const pinFile = (file: { name: string, content: string, mime_type?: string }) => { +const pinFile = (file: { name: string; content: string; mime_type?: string }) => { addPin({ type: 'file', label: file.name, filename: file.name, content: file.content, - mimeType: file.mime_type + mimeType: file.mime_type, }); - toast.success('File pinned to session'); }; @@ -766,25 +560,25 @@ function openInPanel() { :key="task.id" :task="task" :initial-expanded="localJiraTasks.length === 1 || expandedTaskId === task.id" - :is-created="createdIds.has(task.id)" - :is-creating="creatingIds.has(task.id)" + :is-created="createdIds.has(task.id as string)" + :is-creating="creatingIds.has(task.id as string)" :project-key="dashboardStore.selectedProjectId || undefined" - @update="patch => updateTask(task.id, patch)" - @create="createTask(task.id)" + @update="patch => updateTask(task.id as string, patch)" + @create="createTask(task.id as string)" @pin="pinTask(task)" @skip="() => {}" - @toggle="localJiraTasks.length > 1 && (expandedTaskId = expandedTaskId === task.id ? null : task.id)" + @toggle="localJiraTasks.length > 1 && (expandedTaskId = expandedTaskId === (task.id as string) ? null : (task.id as string))" /> @@ -801,14 +595,14 @@ function openInPanel() { /> diff --git a/src/components/chat/ChatMessageList.vue b/src/components/chat/ChatMessageList.vue index 9c43b7e..1fa7d8a 100644 --- a/src/components/chat/ChatMessageList.vue +++ b/src/components/chat/ChatMessageList.vue @@ -2,7 +2,7 @@ import { ref, onMounted, nextTick, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import ChatMessageItem from './ChatMessageItem.vue'; -import EmptyState from '@/components/ui/EmptyState.vue'; +import EmptyState from '@/components/widgets/EmptyState.vue'; const props = defineProps<{ messages: any[]; diff --git a/src/components/chat/JiraDeleteCard.vue b/src/components/chat/JiraDeleteCard.vue index d0f695e..58e2d80 100644 --- a/src/components/chat/JiraDeleteCard.vue +++ b/src/components/chat/JiraDeleteCard.vue @@ -1,10 +1,6 @@