Skip to content

Commit 3c0f215

Browse files
Merge pull request #846 from codex-team/feat-remove-event
feat: int api remove event
2 parents fd6ee5f + 40598a5 commit 3c0f215

12 files changed

Lines changed: 280 additions & 12 deletions

File tree

src/App.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
>
77
<router-view />
88
<FeedbackButton />
9+
<Popover />
910
</div>
1011
</template>
1112

@@ -15,11 +16,13 @@ import { setLanguage } from './i18n';
1516
import { defineComponent } from 'vue';
1617
import FeedbackButton from './components/utils/FeedbackButton.vue';
1718
import notifier from 'codex-notifier';
19+
import { Popover } from '@codexteam/ui/vue';
1820
1921
export default defineComponent({
2022
name: 'App',
2123
components: {
2224
FeedbackButton,
25+
Popover,
2326
},
2427
computed: {
2528
/**

src/api/events/index.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
QUERY_EVENT,
77
QUERY_EVENT_REPETITIONS_PORTION,
88
QUERY_PROJECT_DAILY_EVENTS,
9-
QUERY_CHART_DATA
9+
QUERY_CHART_DATA,
10+
MUTATION_REMOVE_EVENT
1011
} from './queries';
1112
import * as api from '@/api';
1213
import type {
@@ -199,3 +200,17 @@ export async function fetchChartData(
199200
timezoneOffset,
200201
})).project.event.chartData;
201202
}
203+
204+
/**
205+
* Remove event and all related data (repetitions, daily events)
206+
* @param projectId - project event is related to
207+
* @param eventId — original event id to remove
208+
*/
209+
export async function removeEvent(projectId: string, eventId: string): Promise<APIResponse<{ removeEvent: boolean }>> {
210+
return await api.call<{ removeEvent: boolean }>(MUTATION_REMOVE_EVENT, {
211+
projectId,
212+
eventId,
213+
}, undefined, {
214+
allowErrors: true,
215+
});
216+
}

src/api/events/queries.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,13 @@ export const MUTATION_REMOVE_EVENT_ASSIGNEE = `
157157
}
158158
}
159159
`;
160+
161+
// language=GraphQL
162+
/**
163+
* GraphQL Mutation to remove an event and all related data
164+
*/
165+
export const MUTATION_REMOVE_EVENT = `
166+
mutation removeEvent($projectId: ID!, $eventId: ID!) {
167+
removeEvent(projectId: $projectId, eventId: $eventId)
168+
}
169+
`;
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<template>
2+
<ContextMenu
3+
class="event-actions-menu"
4+
:items="menuItems"
5+
/>
6+
</template>
7+
8+
<script setup lang="ts">
9+
import { REMOVE_EVENT } from '@/store/modules/events/actionTypes';
10+
import { ContextMenu, type ContextMenuItem } from '@codexteam/ui/vue';
11+
import notifier from 'codex-notifier';
12+
import { computed, getCurrentInstance } from 'vue';
13+
import { useRouter } from 'vue-router';
14+
import { useStore } from 'vuex';
15+
import { i18n } from '../../i18n';
16+
import { ActionType } from '../utils/ConfirmationWindow/types';
17+
18+
interface Props {
19+
/**
20+
* Project id the event belongs to
21+
*/
22+
projectId: string;
23+
/**
24+
* Original event id to remove
25+
*/
26+
eventId: string;
27+
/**
28+
* Callback to close popover
29+
*/
30+
onClose?: () => void;
31+
}
32+
33+
const props = defineProps<Props>();
34+
35+
const store = useStore();
36+
const router = useRouter();
37+
const instance = getCurrentInstance();
38+
39+
/**
40+
* Show confirmation dialog and remove event on confirm
41+
*/
42+
function confirmRemoveEvent(): void {
43+
if (!instance?.appContext.config.globalProperties.$confirm) {
44+
return;
45+
}
46+
47+
instance.appContext.config.globalProperties.$confirm.open({
48+
description: i18n.global.t('event.removeConfirmation').toString(),
49+
actionType: ActionType.DELETION,
50+
continueButtonText: i18n.global.t('event.removeButton').toString(),
51+
onConfirm: async () => {
52+
const isRemoved = await store.dispatch(REMOVE_EVENT, {
53+
projectId: props.projectId,
54+
eventId: props.eventId,
55+
});
56+
57+
if (isRemoved) {
58+
notifier.show({
59+
message: i18n.global.t('event.removeSuccess').toString(),
60+
style: 'success',
61+
time: 5000,
62+
});
63+
router.push({
64+
name: 'project-overview',
65+
params: { projectId: props.projectId },
66+
query: { reload: String(Date.now()) },
67+
});
68+
69+
return;
70+
}
71+
72+
notifier.show({
73+
message: i18n.global.t('errors.Something went wrong').toString(),
74+
style: 'error',
75+
time: 5000,
76+
});
77+
},
78+
});
79+
}
80+
81+
/**
82+
* Actions available in event context menu
83+
*/
84+
const menuItems = computed<ContextMenuItem[]>(() => {
85+
return [
86+
{
87+
type: 'default',
88+
title: i18n.global.t('event.remove') as string,
89+
icon: 'Trash',
90+
onActivate: () => {
91+
props.onClose?.();
92+
confirmRemoveEvent();
93+
},
94+
},
95+
];
96+
});
97+
</script>
98+
99+
<style scoped>
100+
.event-actions-menu {
101+
min-width: 140px;
102+
}
103+
</style>

src/components/event/EventHeader.vue

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
<template>
22
<div class="event-header">
33
<div class="event-layout__container">
4-
<span
5-
v-if="!loading"
6-
class="event-header__date"
7-
>
8-
{{ formattedFullDate }}
9-
</span>
4+
<div class="event-header__top-right">
5+
<span
6+
v-if="!loading"
7+
class="event-header__date"
8+
>
9+
{{ formattedFullDate }}
10+
</span>
11+
<CdxButton
12+
v-if="isAdmin"
13+
class="event-header__button--more"
14+
secondary
15+
icon="EtcHorisontal"
16+
@click="onMoreClick"
17+
/>
18+
</div>
1019

1120
<div
1221
v-if="workspace"
@@ -130,10 +139,14 @@ import { Workspace } from '@/types/workspaces';
130139
import { projectBadges } from '../../mixins/projectBadges';
131140
import ProjectBadge from '../project/ProjectBadge.vue';
132141
import { JavaScriptAddons } from '@hawk.so/types';
142+
import { Button as CdxButton, usePopover } from '@codexteam/ui/vue';
143+
import EventActionsMenu from './EventActionsMenu.vue';
144+
import Icon from '../utils/Icon.vue';
133145
134146
export default defineComponent({
135147
name: 'EventHeader',
136148
components: {
149+
CdxButton,
137150
TabBar,
138151
ViewedBy,
139152
UiButton,
@@ -153,6 +166,14 @@ export default defineComponent({
153166
validator: prop => typeof prop === 'object' || prop === null,
154167
},
155168
},
169+
setup() {
170+
const { showPopover, hide } = usePopover();
171+
172+
return {
173+
showPopover,
174+
hidePopover: hide,
175+
};
176+
},
156177
data() {
157178
return {
158179
/**
@@ -253,6 +274,13 @@ export default defineComponent({
253274
return this.$store.getters.getWorkspaceByProjectId(this.projectId);
254275
},
255276
277+
/**
278+
* Is current user admin in workspace with this project
279+
*/
280+
isAdmin(): boolean {
281+
return this.workspace ? this.$store.getters.isCurrentUserAdmin(this.workspace.id) : false;
282+
},
283+
256284
/**
257285
* Computed property that returns formatted full date for event timestamp
258286
*/
@@ -302,6 +330,33 @@ export default defineComponent({
302330
window.open(this.event.taskManagerItem.url, '_blank', 'noopener');
303331
}
304332
},
333+
334+
/**
335+
* Open the "more options" context menu near the 3-dot button
336+
*
337+
* @param event - native click mouse event
338+
*/
339+
onMoreClick(event: MouseEvent) {
340+
if (!this.isAdmin) {
341+
return;
342+
}
343+
344+
this.showPopover({
345+
targetEl: event.currentTarget as HTMLElement,
346+
with: {
347+
component: EventActionsMenu,
348+
props: {
349+
projectId: this.projectId,
350+
eventId: this.$route.params.eventId,
351+
onClose: () => this.hidePopover(),
352+
},
353+
},
354+
align: {
355+
vertically: 'below',
356+
horizontally: 'right',
357+
},
358+
});
359+
},
305360
},
306361
});
307362
</script>
@@ -352,8 +407,14 @@ export default defineComponent({
352407
text-overflow: ellipsis;
353408
}
354409
355-
&__date {
410+
&__top-right {
411+
display: flex;
356412
float: right;
413+
align-items: center;
414+
gap: 12px;
415+
}
416+
417+
&__date {
357418
font-size: 12px;
358419
line-height: 23px;
359420
}
@@ -395,6 +456,7 @@ export default defineComponent({
395456
&:hover {
396457
color: var(--color-text-main)
397458
}
459+
398460
}
399461
400462
&__nav-bar, &__viewed-by {

src/components/project/Overview.vue

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ export default {
8484
return this.workspace?.isBlocked;
8585
},
8686
},
87+
watch: {
88+
/**
89+
* Refresh list when route gets a new reload token
90+
*
91+
* @param newValue
92+
* @param oldValue
93+
*/
94+
'$route.query.reload'(newValue, oldValue) {
95+
if (newValue && newValue !== oldValue) {
96+
this.reloadDailyEvents();
97+
}
98+
},
99+
},
87100
88101
/**
89102
* Vue created hook

src/i18n/messages/en.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,11 @@
613613
"title": "Event Hidden",
614614
"description": "We received this event but have hidden it because your subscription has expired.",
615615
"upgradeButton": "Pay and view event"
616-
}
616+
},
617+
"remove": "Remove event",
618+
"removeConfirmation": "Are you sure you want to remove this event? All related data will also be removed.",
619+
"removeButton": "Remove",
620+
"removeSuccess": "Event successfully removed"
617621
},
618622
"common": {
619623
"workspace": "Workspace",

src/i18n/messages/ru.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,11 @@
613613
"title": "Событие скрыто",
614614
"description": "Мы получили это событие, но скрыли его, потому что ваша подписка закончилась.",
615615
"upgradeButton": "Оплатить и посмотреть событие"
616-
}
616+
},
617+
"remove": "Удалить событие",
618+
"removeConfirmation": "Вы уверены, что хотите удалить это событие? Все связанные данные также будут удалены.",
619+
"removeButton": "Удалить",
620+
"removeSuccess": "Событие успешно удалено"
617621
},
618622
"common": {
619623
"workspace": "Воркспейc",

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import '@codexteam/ui/styles';
12
import './styles/base.css';
23
import { createApp } from 'vue';
34
import 'virtual:svg-icons-register';
@@ -11,7 +12,6 @@ import * as api from './api/index';
1112
import { REFRESH_TOKENS } from './store/modules/user/actionTypes';
1213
import { RESET_STORE } from './store/methodsTypes';
1314

14-
import '@codexteam/ui/styles';
1515

1616
const DEBOUNCE_TIMEOUT = 1000;
1717

src/store/modules/events/actionTypes.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ export const GET_CHART_DATA = 'GET_CHART_DATA';
6262
* Get list project with dailyEvents portion
6363
*/
6464
export const FETCH_PROJECT_OVERVIEW = 'FETCH_PROJECT_OVERVIEW';
65+
66+
/**
67+
* Remove a single event and all related data
68+
*/
69+
export const REMOVE_EVENT = 'REMOVE_EVENT';

0 commit comments

Comments
 (0)