diff --git a/app/resources/js/components/projects/BulkActionsBar.vue b/app/resources/js/components/projects/BulkActionsBar.vue index 4ea5166b..afdd6d1f 100644 --- a/app/resources/js/components/projects/BulkActionsBar.vue +++ b/app/resources/js/components/projects/BulkActionsBar.vue @@ -29,11 +29,11 @@ - Архивировать + Удалить @@ -92,11 +92,10 @@ const skipToastText = ref(''); const messages: Record = { pause: 'Приостановить выбранные проекты?', resume: 'Возобновить выбранные проекты?', - archive: - 'Архивировать выбранные проекты?\nДействие необратимо в Plan 5 (восстановление потребует ручного запроса).', + delete: 'Удалить выбранные проекты? Действие необратимо. Проекты со сделками будут пропущены.', }; -async function confirmAndRun(action: 'pause' | 'resume' | 'archive') { +async function confirmAndRun(action: 'pause' | 'resume' | 'delete') { if (!window.confirm(messages[action])) return; await runBulk({ action }); } diff --git a/app/resources/js/components/projects/ProjectCard.story.vue b/app/resources/js/components/projects/ProjectCard.story.vue index 8b757eca..c6b6ce71 100644 --- a/app/resources/js/components/projects/ProjectCard.story.vue +++ b/app/resources/js/components/projects/ProjectCard.story.vue @@ -9,7 +9,6 @@ const base = { daily_limit_target: 50, delivered_today: 32, is_active: true, - archived_at: null, sync_status: 'ok' as const, }; diff --git a/app/resources/js/components/projects/ProjectCard.vue b/app/resources/js/components/projects/ProjectCard.vue index 817567cf..2e102bc8 100644 --- a/app/resources/js/components/projects/ProjectCard.vue +++ b/app/resources/js/components/projects/ProjectCard.vue @@ -48,9 +48,9 @@ Синхронизировать - - - Архивировать + + + Удалить @@ -97,7 +97,7 @@ defineEmits<{ edit: [project: Project]; 'toggle-active': [project: Project]; 'sync-now': [project: Project]; - archive: [project: Project]; + delete: [project: Project]; }>(); const typeLabel = computed(() => ({ site: 'Сайт', call: 'Звонок', sms: 'СМС' })[props.project.signal_type]); diff --git a/app/resources/js/components/projects/ProjectDetailsDrawer.vue b/app/resources/js/components/projects/ProjectDetailsDrawer.vue index 47b6cc68..81195bfb 100644 --- a/app/resources/js/components/projects/ProjectDetailsDrawer.vue +++ b/app/resources/js/components/projects/ProjectDetailsDrawer.vue @@ -63,10 +63,10 @@ async function onPause(): Promise { async function onDelete(): Promise { if (!props.project) return; const ok = window.confirm( - 'Архивировать проект? Действие необратимо в Plan 5 (восстановление потребует ручного запроса).', + 'Удалить проект? Действие необратимо. Если по проекту есть сделки — удаление будет заблокировано.', ); if (!ok) return; - await store.archive(props.project.id); + await store.del(props.project.id); emit('close'); } diff --git a/app/resources/js/plugins/vuetify.ts b/app/resources/js/plugins/vuetify.ts index 02746fda..e15c09c8 100644 --- a/app/resources/js/plugins/vuetify.ts +++ b/app/resources/js/plugins/vuetify.ts @@ -168,6 +168,7 @@ const lucideMap: Record = { 'mdi-content-save-outline': Save, 'mdi-credit-card-outline': CreditCard, 'mdi-currency-rub': RussianRuble, + 'mdi-delete': Trash2, 'mdi-delete-outline': Trash2, 'mdi-dots-vertical': MoreVertical, 'mdi-download': Download, diff --git a/app/resources/js/stores/projectsStore.ts b/app/resources/js/stores/projectsStore.ts index c4c04b8c..bdd9281e 100644 --- a/app/resources/js/stores/projectsStore.ts +++ b/app/resources/js/stores/projectsStore.ts @@ -13,7 +13,6 @@ export interface Project { delivered_today: number; delivered_in_month?: number; is_active: boolean; - archived_at: string | null; region_mask?: number; region_mode?: string; regions?: number[]; // Plan 6 — subject codes 1..89; пустой массив = вся РФ @@ -65,7 +64,7 @@ export const useProjectsStore = defineStore('projects', () => { return data.data; } - async function archive(id: number) { + async function del(id: number) { await axios.delete(`/api/projects/${id}`); await fetch(); } @@ -94,7 +93,7 @@ export const useProjectsStore = defineStore('projects', () => { selectedIds.value.clear(); } - async function bulkAction(action: 'pause' | 'resume' | 'archive') { + async function bulkAction(action: 'pause' | 'resume' | 'delete') { const ids = Array.from(selectedIds.value); if (!ids.length) return; await axios.post('/api/projects/bulk', { action, ids }); @@ -103,7 +102,7 @@ export const useProjectsStore = defineStore('projects', () => { } interface BulkPayload { - action: 'pause' | 'resume' | 'archive' | 'update_regions' | 'update_days' | 'update_limit'; + action: 'pause' | 'resume' | 'delete' | 'update_regions' | 'update_days' | 'update_limit'; add?: number; remove?: number; // Plan 6.5 — update_regions оперирует кодами субъектов (1..89), не bitmask ФО. @@ -200,7 +199,7 @@ export const useProjectsStore = defineStore('projects', () => { fetch, create, update, - archive, + del, syncNow, toggleActive, toggleSelect, diff --git a/app/resources/js/views/ProjectsView.vue b/app/resources/js/views/ProjectsView.vue index ff5276c2..b8ba60c6 100644 --- a/app/resources/js/views/ProjectsView.vue +++ b/app/resources/js/views/ProjectsView.vue @@ -71,7 +71,7 @@ @edit="openEdit" @toggle-active="store.toggleActive" @sync-now="(p: Project) => store.syncNow(p.id)" - @archive="(p: Project) => store.archive(p.id)" + @delete="(p: Project) => store.del(p.id)" /> @@ -123,7 +123,6 @@ const typeFilters = [ const statusFilters = [ { title: 'Активные', value: 'active' }, { title: 'На паузе', value: 'paused' }, - { title: 'Архивные', value: 'archived' }, ]; let searchTimer: ReturnType | null = null; diff --git a/app/resources/js/views/projects/NewProjectDialog.story.vue b/app/resources/js/views/projects/NewProjectDialog.story.vue index b7afd52a..f84f33a4 100644 --- a/app/resources/js/views/projects/NewProjectDialog.story.vue +++ b/app/resources/js/views/projects/NewProjectDialog.story.vue @@ -25,7 +25,6 @@ const sampleProject = { daily_limit_target: 50, delivered_today: 12, is_active: true, - archived_at: null, sync_status: 'ok' as const, region_mask: 0, region_mode: 'include' as const, diff --git a/app/tests/Frontend/BulkActionsBar.spec.ts b/app/tests/Frontend/BulkActionsBar.spec.ts index da07913d..486bf71a 100644 --- a/app/tests/Frontend/BulkActionsBar.spec.ts +++ b/app/tests/Frontend/BulkActionsBar.spec.ts @@ -169,7 +169,7 @@ describe('BulkActionsBar — extended', () => { expect((wrapper.vm as unknown as { regionsOpen: boolean }).regionsOpen).toBe(true); }); - it('keeps existing pause/resume/archive buttons', async () => { + it('keeps existing pause/resume/delete buttons', async () => { setActivePinia(createPinia()); vi.mocked(axios.post).mockResolvedValue({ data: { updated: 1, skipped: [], warnings: [] } }); vi.mocked(axios.get).mockResolvedValue({ data: { data: [], meta: { total: 0 } } }); @@ -184,6 +184,6 @@ describe('BulkActionsBar — extended', () => { }); expect(wrapper.find('[data-testid="bulk-pause"]').exists()).toBe(true); expect(wrapper.find('[data-testid="bulk-resume"]').exists()).toBe(true); - expect(wrapper.find('[data-testid="bulk-archive"]').exists()).toBe(true); + expect(wrapper.find('[data-testid="bulk-delete"]').exists()).toBe(true); }); }); diff --git a/app/tests/Frontend/EditProjectDialog.spec.ts b/app/tests/Frontend/EditProjectDialog.spec.ts index 3b3899a2..470bd98a 100644 --- a/app/tests/Frontend/EditProjectDialog.spec.ts +++ b/app/tests/Frontend/EditProjectDialog.spec.ts @@ -23,7 +23,6 @@ const sampleProject = { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, sync_status: 'ok' as const, region_mask: 0, region_mode: 'include', diff --git a/app/tests/Frontend/ProjectCard.spec.ts b/app/tests/Frontend/ProjectCard.spec.ts index f5f9eb38..0df50650 100644 --- a/app/tests/Frontend/ProjectCard.spec.ts +++ b/app/tests/Frontend/ProjectCard.spec.ts @@ -12,7 +12,6 @@ const baseProject = { daily_limit_target: 50, delivered_today: 32, is_active: true, - archived_at: null, sync_status: 'ok' as const, }; diff --git a/app/tests/Frontend/ProjectCardRedesign.spec.ts b/app/tests/Frontend/ProjectCardRedesign.spec.ts index 9c7516a2..e15258c2 100644 --- a/app/tests/Frontend/ProjectCardRedesign.spec.ts +++ b/app/tests/Frontend/ProjectCardRedesign.spec.ts @@ -11,7 +11,6 @@ const sample = { is_active: true, daily_limit_target: 50, delivered_today: 12, - archived_at: null, sync_status: 'ok' as const, }; diff --git a/app/tests/Frontend/ProjectDetailsDrawer.spec.ts b/app/tests/Frontend/ProjectDetailsDrawer.spec.ts index e2fd9ff7..4e92d001 100644 --- a/app/tests/Frontend/ProjectDetailsDrawer.spec.ts +++ b/app/tests/Frontend/ProjectDetailsDrawer.spec.ts @@ -21,7 +21,6 @@ const sampleProject: Project = { daily_limit_target: 30, delivered_today: 0, is_active: true, - archived_at: null, region_mask: 0, region_mode: 'include', regions: [], @@ -152,10 +151,10 @@ describe('ProjectDetailsDrawer', () => { expect(wrapper.get('[data-testid="pdd-pause"]').text()).toContain('Приостановить'); }); - it('Delete: confirm=true → archive + close emit', async () => { + it('Delete: confirm=true → del + close emit', async () => { const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } }); const store = useProjectsStore(); - const spy = vi.spyOn(store, 'archive').mockResolvedValueOnce(undefined); + const spy = vi.spyOn(store, 'del').mockResolvedValueOnce(undefined); vi.stubGlobal('confirm', () => true); await wrapper.get('[data-testid="pdd-delete"]').trigger('click'); @@ -166,10 +165,10 @@ describe('ProjectDetailsDrawer', () => { vi.unstubAllGlobals(); }); - it('Delete: confirm=false → no archive, no close', async () => { + it('Delete: confirm=false → no del, no close', async () => { const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } }); const store = useProjectsStore(); - const spy = vi.spyOn(store, 'archive').mockResolvedValueOnce(undefined); + const spy = vi.spyOn(store, 'del').mockResolvedValueOnce(undefined); vi.stubGlobal('confirm', () => false); await wrapper.get('[data-testid="pdd-delete"]').trigger('click'); diff --git a/app/tests/Frontend/ProjectsView.spec.ts b/app/tests/Frontend/ProjectsView.spec.ts index 1e198b64..06ccbe62 100644 --- a/app/tests/Frontend/ProjectsView.spec.ts +++ b/app/tests/Frontend/ProjectsView.spec.ts @@ -52,7 +52,6 @@ describe('ProjectsView', () => { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, sync_status: 'ok', }, ], @@ -82,7 +81,6 @@ describe('ProjectsView', () => { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, sync_status: 'ok', }, { @@ -93,7 +91,6 @@ describe('ProjectsView', () => { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, sync_status: 'ok', }, ], @@ -127,7 +124,6 @@ describe('ProjectsView × ProjectDetailsDrawer integration', () => { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, sync_status: 'ok' as const, }; const projectB = { @@ -138,7 +134,6 @@ describe('ProjectsView × ProjectDetailsDrawer integration', () => { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, sync_status: 'ok' as const, }; diff --git a/app/tests/Frontend/ProjectsView.toolbar.spec.ts b/app/tests/Frontend/ProjectsView.toolbar.spec.ts index 4b748974..b46021a2 100644 --- a/app/tests/Frontend/ProjectsView.toolbar.spec.ts +++ b/app/tests/Frontend/ProjectsView.toolbar.spec.ts @@ -20,7 +20,6 @@ const mountView = async () => { daily_limit_target: 100, delivered_today: 0, is_active: true, - archived_at: null, region_mask: 1, region_mode: 'include', delivery_days_mask: 31, @@ -34,7 +33,6 @@ const mountView = async () => { daily_limit_target: 100, delivered_today: 0, is_active: true, - archived_at: null, region_mask: 1, region_mode: 'include', delivery_days_mask: 31, diff --git a/app/tests/Frontend/projectsStore.spec.ts b/app/tests/Frontend/projectsStore.spec.ts index 387ad11b..9df4cc85 100644 --- a/app/tests/Frontend/projectsStore.spec.ts +++ b/app/tests/Frontend/projectsStore.spec.ts @@ -66,6 +66,14 @@ describe('projectsStore (no polling)', () => { expect(store.selectedIds.has(1)).toBe(false); }); + it('del() calls DELETE /api/projects/{id}', async () => { + const store = useProjectsStore(); + vi.spyOn(axios, 'delete').mockResolvedValue({ data: null }); + vi.spyOn(axios, 'get').mockResolvedValue({ data: { data: [], meta: { total: 0 } } }); + await store.del(7); + expect(axios.delete).toHaveBeenCalledWith('/api/projects/7'); + }); + it('bulkAction sends array of ids and clears selection', async () => { (axios.post as unknown as ReturnType).mockResolvedValue({ data: { updated: 2 } }); (axios.get as unknown as ReturnType).mockResolvedValue({ @@ -104,7 +112,6 @@ describe('projectsStore (polling)', () => { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, region_mask: 0, region_mode: 'include', delivery_days_mask: 127, @@ -133,7 +140,6 @@ describe('projectsStore (polling)', () => { daily_limit_target: 10, delivered_today: 0, is_active: true, - archived_at: null, region_mask: 0, region_mode: 'include', delivery_days_mask: 127,