From acb81f20e03275e18fefa20a3b5b63490a3a76de Mon Sep 17 00:00:00 2001 From: quangtuanitmo18 Date: Mon, 2 Mar 2026 02:45:32 +0300 Subject: [PATCH 1/7] fet: int api remove event --- src/App.vue | 3 + src/api/events/index.ts | 15 ++- src/api/events/queries.ts | 10 ++ src/components/event/EventActionsMenu.vue | 93 ++++++++++++++++ src/components/event/EventHeader.vue | 126 ++++++++++++++++++++-- src/components/event/Layout.vue | 14 +++ src/components/project/Overview.vue | 11 +- src/i18n/messages/en.json | 7 +- src/i18n/messages/ru.json | 7 +- src/main.ts | 2 +- src/store/modules/events/actionTypes.js | 5 + src/store/modules/events/index.ts | 18 +++- src/styles/base.css | 3 + 13 files changed, 300 insertions(+), 14 deletions(-) create mode 100644 src/components/event/EventActionsMenu.vue diff --git a/src/App.vue b/src/App.vue index ba923701f..74f643f91 100644 --- a/src/App.vue +++ b/src/App.vue @@ -6,6 +6,7 @@ > + @@ -15,11 +16,13 @@ import { setLanguage } from './i18n'; import { defineComponent } from 'vue'; import FeedbackButton from './components/utils/FeedbackButton.vue'; import notifier from 'codex-notifier'; +import { Popover } from '@codexteam/ui/vue'; export default defineComponent({ name: 'App', components: { FeedbackButton, + Popover, }, computed: { /** diff --git a/src/api/events/index.ts b/src/api/events/index.ts index 1296e257a..3820fe0f3 100644 --- a/src/api/events/index.ts +++ b/src/api/events/index.ts @@ -6,7 +6,8 @@ import { QUERY_EVENT, QUERY_EVENT_REPETITIONS_PORTION, QUERY_PROJECT_DAILY_EVENTS, - QUERY_CHART_DATA + QUERY_CHART_DATA, + MUTATION_REMOVE_EVENT } from './queries'; import * as api from '@/api'; import type { @@ -196,3 +197,15 @@ export async function fetchChartData( timezoneOffset, })).project.event.chartData; } + +/** + * Remove event and all related data (repetitions, daily events) + * @param projectId - project event is related to + * @param eventId — original event id to remove + */ +export async function removeEvent(projectId: string, eventId: string): Promise { + return (await api.callOld(MUTATION_REMOVE_EVENT, { + projectId, + eventId, + })).removeEvent; +} \ No newline at end of file diff --git a/src/api/events/queries.ts b/src/api/events/queries.ts index 1c7a7ab61..f8f950fb6 100644 --- a/src/api/events/queries.ts +++ b/src/api/events/queries.ts @@ -156,3 +156,13 @@ export const MUTATION_REMOVE_EVENT_ASSIGNEE = ` } } `; + +// language=GraphQL +/** + * GraphQL Mutation to remove an event and all related data + */ +export const MUTATION_REMOVE_EVENT = ` + mutation removeEvent($projectId: ID!, $eventId: ID!) { + removeEvent(projectId: $projectId, eventId: $eventId) + } +`; diff --git a/src/components/event/EventActionsMenu.vue b/src/components/event/EventActionsMenu.vue new file mode 100644 index 000000000..07a8f3cf7 --- /dev/null +++ b/src/components/event/EventActionsMenu.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/components/event/EventHeader.vue b/src/components/event/EventHeader.vue index 570fc80c6..51303d715 100644 --- a/src/components/event/EventHeader.vue +++ b/src/components/event/EventHeader.vue @@ -1,12 +1,22 @@ @@ -145,6 +150,10 @@ export default { this.$refs.eventsList.reloadDailyEvents(); } }, + + async eventDeleted() { + this.reloadDailyEvents(); + }, }, }; diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 24af27a5b..f67bc3e83 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -609,7 +609,12 @@ "title": "Event Hidden", "description": "We received this event but have hidden it because your subscription has expired.", "upgradeButton": "Pay and view event" - } + }, + "remove": "Remove event", + "removeConfirmation": "Are you sure you want to remove this event? All repetitions and related data will also be removed.", + "removeButton": "Remove", + "removeSuccess": "Event successfully removed", + "removeError": "Failed to remove the event" }, "common": { "workspace": "Workspace", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index c8e936963..f50c898b9 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -609,7 +609,12 @@ "title": "Событие скрыто", "description": "Мы получили это событие, но скрыли его, потому что ваша подписка закончилась.", "upgradeButton": "Оплатить и посмотреть событие" - } + }, + "remove": "Удалить событие", + "removeConfirmation": "Вы уверены, что хотите удалить это событие? Все повторения и связанные данные также будут удалены.", + "removeButton": "Удалить", + "removeSuccess": "Событие успешно удалено", + "removeError": "Не удалось удалить событие" }, "common": { "workspace": "Воркспейc", diff --git a/src/main.ts b/src/main.ts index 8f4ef828c..87cd505e5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,4 @@ +import '@codexteam/ui/styles'; import './styles/base.css'; import { createApp } from 'vue'; import 'virtual:svg-icons-register'; @@ -11,7 +12,6 @@ import * as api from './api/index'; import { REFRESH_TOKENS } from './store/modules/user/actionTypes'; import { RESET_STORE } from './store/methodsTypes'; -import '@codexteam/ui/styles'; const DEBOUNCE_TIMEOUT = 1000; diff --git a/src/store/modules/events/actionTypes.js b/src/store/modules/events/actionTypes.js index 82d0f49e1..a6a86594b 100644 --- a/src/store/modules/events/actionTypes.js +++ b/src/store/modules/events/actionTypes.js @@ -62,3 +62,8 @@ export const GET_CHART_DATA = 'GET_CHART_DATA'; * Get list project with dailyEvents portion */ export const FETCH_PROJECT_OVERVIEW = 'FETCH_PROJECT_OVERVIEW'; + +/** + * Remove a single event and all related data + */ +export const REMOVE_EVENT = 'REMOVE_EVENT'; diff --git a/src/store/modules/events/index.ts b/src/store/modules/events/index.ts index 98200153c..38bddebfe 100644 --- a/src/store/modules/events/index.ts +++ b/src/store/modules/events/index.ts @@ -9,7 +9,8 @@ import { TOGGLE_EVENT_MARK, UPDATE_EVENT_ASSIGNEE, VISIT_EVENT, - GET_CHART_DATA + GET_CHART_DATA, + REMOVE_EVENT } from './actionTypes'; import { RESET_STORE } from '../../methodsTypes'; import type { Module } from 'vuex'; @@ -416,6 +417,21 @@ const module: Module = { } }, + /** + * Remove event and all related data (repetitions, daily events) + * @param context - vuex action context (not used) + * @param context.commit - standard Vuex commit function + * @param payload - vuex action payload + * @param payload.projectId - project event is related to + * @param payload.eventId - original event id to remove + */ + async [REMOVE_EVENT]({ commit }, { projectId, eventId }: { + projectId: string; + eventId: string; + }): Promise { + return eventsApi.removeEvent(projectId, eventId); + }, + /** * Resets module state * @param commit - standard Vuex commit function diff --git a/src/styles/base.css b/src/styles/base.css index 053d44ae5..8a11409e3 100644 --- a/src/styles/base.css +++ b/src/styles/base.css @@ -295,4 +295,7 @@ body .cdx-notify { --accent--solid-hover: var(--color-indicator-medium-dark); --accent--bg-secondary: color-mod(var(--color-indicator-medium) alpha(10%)); --accent--text-solid-foreground: var(--color-text-main); + + /* Override codexteam/ui popover z-index to appear above app modals */ + --z-popover: 10000; } From 4710cc45518dac37e192627da2345350ba1beab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D1=82=D1=83=D0=B0=D0=BD?= Date: Mon, 2 Mar 2026 17:15:03 +0300 Subject: [PATCH 2/7] minor changes --- src/components/event/EventHeader.vue | 30 +++++++++++----------------- src/store/modules/events/index.ts | 2 +- src/styles/base.css | 4 ++++ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/components/event/EventHeader.vue b/src/components/event/EventHeader.vue index 51303d715..a14c9f133 100644 --- a/src/components/event/EventHeader.vue +++ b/src/components/event/EventHeader.vue @@ -8,14 +8,11 @@ > {{ formattedFullDate }} - -
- -
+
= { projectId: string; eventId: string; }): Promise { - return eventsApi.removeEvent(projectId, eventId); + return await eventsApi.removeEvent(projectId, eventId); }, /** diff --git a/src/styles/base.css b/src/styles/base.css index 8a11409e3..1373a1195 100644 --- a/src/styles/base.css +++ b/src/styles/base.css @@ -298,4 +298,8 @@ body .cdx-notify { /* Override codexteam/ui popover z-index to appear above app modals */ --z-popover: 10000; + + .codex-popover { + padding: 10px; + } } From 2f08658c8a53a794db4df139fce851e336bc6f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D1=82=D1=83=D0=B0=D0=BD?= Date: Tue, 10 Mar 2026 16:44:00 +0300 Subject: [PATCH 3/7] address review comments --- src/api/events/index.ts | 10 ++- src/components/event/EventActionsMenu.vue | 97 ++++------------------- src/components/event/EventHeader.vue | 68 ++++++++++------ src/store/modules/events/index.ts | 30 ++++++- 4 files changed, 95 insertions(+), 110 deletions(-) diff --git a/src/api/events/index.ts b/src/api/events/index.ts index 3820fe0f3..c46f0604c 100644 --- a/src/api/events/index.ts +++ b/src/api/events/index.ts @@ -203,9 +203,11 @@ export async function fetchChartData( * @param projectId - project event is related to * @param eventId — original event id to remove */ -export async function removeEvent(projectId: string, eventId: string): Promise { - return (await api.callOld(MUTATION_REMOVE_EVENT, { +export async function removeEvent(projectId: string, eventId: string): Promise> { + return await api.call<{ removeEvent: boolean }>(MUTATION_REMOVE_EVENT, { projectId, eventId, - })).removeEvent; -} \ No newline at end of file + }, undefined, { + allowErrors: true, + }); +} diff --git a/src/components/event/EventActionsMenu.vue b/src/components/event/EventActionsMenu.vue index 07a8f3cf7..7f70ac26c 100644 --- a/src/components/event/EventActionsMenu.vue +++ b/src/components/event/EventActionsMenu.vue @@ -1,93 +1,28 @@ - diff --git a/src/components/event/EventHeader.vue b/src/components/event/EventHeader.vue index a14c9f133..17ed93a69 100644 --- a/src/components/event/EventHeader.vue +++ b/src/components/event/EventHeader.vue @@ -9,6 +9,7 @@ {{ formattedFullDate }} { + this.hidePopover(); + this.confirmRemoveEvent(); + }, + }, + ]; + }, + /** * Open the "more options" context menu near the 3-dot button * * @param event - native click mouse event */ onMoreClick(event: MouseEvent) { + if (!this.isAdmin) { + return; + } + this.showPopover({ targetEl: event.currentTarget as HTMLElement, with: { component: EventActionsMenu, props: { - items: [ - { - title: this.$t('event.remove') as string, - icon: 'Trash', - danger: true, - onClick: () => { - this.hidePopover(); - this.confirmRemoveEvent(); - }, - }, - ], + items: this.eventActionsMenuItems(), }, }, align: { @@ -367,24 +386,27 @@ export default defineComponent({ actionType: ActionType.DELETION, continueButtonText: this.$t('event.removeButton').toString(), onConfirm: async () => { - try { - await this.$store.dispatch(REMOVE_EVENT, { - projectId, - eventId, - }); + const isRemoved = await this.$store.dispatch(REMOVE_EVENT, { + projectId, + eventId, + }); + + if (isRemoved) { notifier.show({ message: this.$t('event.removeSuccess').toString(), style: 'success', time: 5000, }); this.$emit('event-deleted'); - } catch { - notifier.show({ - message: this.$t('event.removeError').toString(), - style: 'error', - time: 5000, - }); + + return; } + + notifier.show({ + message: this.$t('event.removeError').toString(), + style: 'error', + time: 5000, + }); }, }); }, diff --git a/src/store/modules/events/index.ts b/src/store/modules/events/index.ts index 4e9cd8f1c..c9e7ecfbf 100644 --- a/src/store/modules/events/index.ts +++ b/src/store/modules/events/index.ts @@ -28,6 +28,12 @@ import { } from '@/types/events'; import type { User } from '@/types/user'; import type { EventChartItem } from '@/types/chart'; +import { useErrorTracker } from '@/hawk'; + +/** + * Error tracking composable + */ +const { track } = useErrorTracker(); /** * Mutations enum for this module @@ -425,11 +431,31 @@ const module: Module = { * @param payload.projectId - project event is related to * @param payload.eventId - original event id to remove */ - async [REMOVE_EVENT]({ commit }, { projectId, eventId }: { + async [REMOVE_EVENT](context, { projectId, eventId }: { projectId: string; eventId: string; }): Promise { - return await eventsApi.removeEvent(projectId, eventId); + const response = await eventsApi.removeEvent(projectId, eventId); + + if (response.errors?.length) { + response.errors.forEach((apiError) => { + const apiErrorDetails = { + message: apiError.message, + path: apiError.path ? apiError.path.join('.') : '', + code: String(apiError.extensions?.code ?? ''), + }; + + track(new Error(apiError.message), { + projectId, + eventId, + errorDetails: apiErrorDetails, + }); + }); + + return false; + } + + return Boolean(response.data?.removeEvent); }, /** From 377a3e4e03444acd6ff450f349c73922b777c2e1 Mon Sep 17 00:00:00 2001 From: Murod Khaydarov Date: Wed, 15 Apr 2026 23:26:37 +0300 Subject: [PATCH 4/7] Update JavaScript catcher version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e80a9a5ba..4b46a9eec 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "@codexteam/ui": "0.2.0-rc.4", - "@hawk.so/javascript": "3.2.20-catcher-ws-conn.8a0320b", + "@hawk.so/javascript": "3.2.21", "@hawk.so/types": "^0.5.9", "axios": "^1.12.2", "codex-notifier": "^1.1.2", diff --git a/yarn.lock b/yarn.lock index 27fdd2780..e146a6a78 100644 --- a/yarn.lock +++ b/yarn.lock @@ -649,10 +649,10 @@ "@eslint/core" "^0.17.0" levn "^0.4.1" -"@hawk.so/javascript@3.2.20-catcher-ws-conn.8a0320b": - version "3.2.20-catcher-ws-conn.8a0320b" - resolved "https://registry.yarnpkg.com/@hawk.so/javascript/-/javascript-3.2.20-catcher-ws-conn.8a0320b.tgz#cd3dfa63d8868ded45aa4f742969ac97f180d81f" - integrity sha512-YwRIdvc9/b/OXwQozDobDin0Vo/Kt8jV06/ry1XKr7B6rR8ex4yrR/wmtHnv08vwSggWUXr6JetTmH5MPgqm/A== +"@hawk.so/javascript@3.2.21": + version "3.2.21" + resolved "https://registry.yarnpkg.com/@hawk.so/javascript/-/javascript-3.2.21.tgz#7ff328a8398780a6eccd56bd83421abb5544b8a1" + integrity sha512-iUi0cJuuXUUlzk9x5bYdjn2sUqnfDetgG6+smvN7nFAT73rPWnLqLKoCDN+4xJCAWjt+VYTKHtjH85sDdRBXOQ== dependencies: error-stack-parser "^2.1.4" From 8077a51f6df2a88fadad8122789db501f653e093 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Thu, 16 Apr 2026 21:46:40 +0300 Subject: [PATCH 5/7] fix: show skeleton when reloading events list --- package.json | 2 +- src/components/project/EventItemSkeleton.vue | 6 ++++++ src/components/project/EventsList.vue | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b46a9eec..08943e1f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.so", - "version": "1.1.6", + "version": "1.1.7", "private": true, "scripts": { "dev": "vite", diff --git a/src/components/project/EventItemSkeleton.vue b/src/components/project/EventItemSkeleton.vue index 0162ecbc2..bab3ec7e3 100644 --- a/src/components/project/EventItemSkeleton.vue +++ b/src/components/project/EventItemSkeleton.vue @@ -26,6 +26,12 @@ export default { height: 16px; background-color: var(--color-bg-third); border-radius: 9px; + animation: skeleton-pulse 1.4s ease-in-out infinite; +} + +@keyframes skeleton-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } } .event-item-skeleton { diff --git a/src/components/project/EventsList.vue b/src/components/project/EventsList.vue index 86be21b63..c19b52d7b 100644 --- a/src/components/project/EventsList.vue +++ b/src/components/project/EventsList.vue @@ -372,6 +372,7 @@ export default { reloadDailyEvents() { this.dailyEventsNextCursor = null; this.noMore = false; + this.dailyEvents = []; this.loadMoreEvents(true); }, /** From 73a8e917aafab5d612e2dda77f6f34526a364264 Mon Sep 17 00:00:00 2001 From: quangtuanitmo18 Date: Sat, 25 Apr 2026 20:11:06 +0300 Subject: [PATCH 6/7] address comments review --- src/components/event/EventActionsMenu.vue | 106 +++++++++++++++++++--- src/components/event/EventHeader.vue | 64 +------------ src/components/event/Layout.vue | 14 --- src/components/project/Overview.vue | 22 +++-- src/i18n/messages/en.json | 5 +- src/i18n/messages/ru.json | 5 +- 6 files changed, 116 insertions(+), 100 deletions(-) diff --git a/src/components/event/EventActionsMenu.vue b/src/components/event/EventActionsMenu.vue index 7f70ac26c..a88db3ec3 100644 --- a/src/components/event/EventActionsMenu.vue +++ b/src/components/event/EventActionsMenu.vue @@ -1,24 +1,104 @@ -