diff --git a/lib/src/compositions/issue/issue-header.tsx b/lib/src/compositions/issue/issue-header.tsx
index 4f2f8f3a..9935466d 100644
--- a/lib/src/compositions/issue/issue-header.tsx
+++ b/lib/src/compositions/issue/issue-header.tsx
@@ -20,6 +20,9 @@ import { WrapComingSoonPopover } from '@elements/components/coming-soon-popover'
import { useIdent, useWrapRequireAuth } from '@elements/store/hooks';
import { Status } from '@elements/logic/meta/initiative';
import { IssueTab } from '@elements/logic/issue';
+import { ContextMenu } from '@elements/components/context-menu';
+import { type ItemType } from '@elements/components/dropdown';
+import { TrashOutline } from '@elements/icons';
const Title = suspensify(() => {
const issueId = useValue('current.issue/id');
@@ -163,6 +166,46 @@ const ActionBar = () => {
);
};
+const IssueContextMenu = suspensify(() => {
+ const t = useTranslation();
+
+ const issueId = useValue('current.issue/id');
+ const canDelete = useValue('issue/can-delete', { 'issue/id': issueId });
+ const openModal = useDispatch('confirmation-modal/open');
+ const deleteAction = useDispatch('issue/delete');
+
+ const onDeleteClick = useCallback(() => {
+ const onConfirm = async () => deleteAction({ 'issue/id': issueId });
+ openModal({
+ kind: 'danger',
+ confirmText: t('common/delete'),
+ titleText: t('issue.delete.modal/title'),
+ bodyText: t('issue.delete.modal/body'),
+ cancelText: t('common/cancel'),
+ onConfirm,
+ });
+ }, [openModal, t, deleteAction, issueId]);
+
+ const items = useMemo(() => {
+ let items = [];
+ if (canDelete) {
+ items.push({
+ text: t('common/delete'),
+ onClick: onDeleteClick,
+ Icon: TrashOutline,
+ kind: 'danger',
+ key: 'delete',
+ type: 'button',
+ });
+ }
+
+ return items;
+ }, [t, canDelete, onDeleteClick]) as ItemType[];
+ return items.length === 0 ? null : (
+
+ );
+});
+
export const IssueHeader = suspensify(() => {
const issueId = useValue('current.issue/id');
const updatedAt = useValue('issue/updated-at', { 'issue/id': issueId });
@@ -179,6 +222,10 @@ export const IssueHeader = suspensify(() => {
+
+ {/*{isDraft ? null : }*/}
+
+
{/**/}
diff --git a/lib/src/logic/issue.ts b/lib/src/logic/issue.ts
index 9d2b169e..b8440a37 100644
--- a/lib/src/logic/issue.ts
+++ b/lib/src/logic/issue.ts
@@ -58,6 +58,7 @@ export type Subs = {
'issue.locality/zoom': Sub<{ 'issue/id': string }, number>;
'issue/images': Sub<{ 'issue/id': string }, Image[]>;
'issue.image/can-delete': Sub<{ 'issue/id': string }, boolean>;
+ 'issue/can-delete': Sub<{ 'issue/id': string }, boolean>;
};
export type Events = {
@@ -87,6 +88,7 @@ export type Events = {
'navigated.issue/new': Evt<{ route: Match }>;
'issue.image/add': Evt<{ file: File; 'issue/id': string; caption: string }>;
'issue.image/delete': Evt<{ 'image/id': string; 'issue/id': string }>;
+ 'issue/delete': Evt<{ 'issue/id': string }>;
};
export const issueSlice = () => ({
diff --git a/lib/src/translations/en.ts b/lib/src/translations/en.ts
index feb4e7f0..b86d9a98 100644
--- a/lib/src/translations/en.ts
+++ b/lib/src/translations/en.ts
@@ -84,6 +84,8 @@ const issue = {
'issue.locality/update': 'Update issue locality',
'issue.locality/description': 'The locality around which the issue is centered.',
'issue/locality': 'Issue locality',
+ 'issue.delete.modal/title': 'Delete Issue',
+ 'issue.delete.modal/body': 'Are you sure you want to delete the issue? This cannot be undone.',
'issue.severity/label': ({ avgScore, userScore, votes }: any) => {
const oneVote = votes === 1 || votes === '1';
const userScoreText = userScore
diff --git a/storybook/src/stores/action/status.ts b/storybook/src/stores/action/status.ts
index 4efef837..8b70f021 100644
--- a/storybook/src/stores/action/status.ts
+++ b/storybook/src/stores/action/status.ts
@@ -1,11 +1,13 @@
import { ActionStatus } from '@elements/logic/action';
+const actionId = 'action-1';
+
export const store = {
sub: {
- 'action.status/modal': { visible: true, 'action/id': '' },
+ 'action.status/modal': { visible: true, 'action/id': actionId },
'action/status': ActionStatus.Active,
'action.status/can-update': false,
- 'action/id': '',
+ 'action/id': actionId,
},
evt: ['action.status.modal/close', 'action.status.modal/open', 'action.status/update'],
};
diff --git a/storybook/src/stores/issue/header.ts b/storybook/src/stores/issue/header.ts
index 4d06cd81..2bb8dbcb 100644
--- a/storybook/src/stores/issue/header.ts
+++ b/storybook/src/stores/issue/header.ts
@@ -1,4 +1,5 @@
import { store as votingStore } from '@story/stores/voting';
+import { store as confirmationModalStore } from '@story/stores/confirmation-modal';
import { lorem } from '@story/utils/string';
import { randomTimestamp } from '@story/utils/time';
import { store as textEditorStore } from '@story/stores/text-editor';
@@ -16,11 +17,13 @@ export const store = {
'issue.title/text': lorem.generateSentences(1),
'issue.tabs/active-tab': 'locations',
'issue/updated-at': randomTimestamp(),
+ 'issue/can-delete': false,
},
evt: [
...votingStore.evt,
...textEditorStore.evt,
+ ...confirmationModalStore.evt,
'issue.current.user/face',
'issue/follow',
'issue/unfollow',
@@ -28,5 +31,6 @@ export const store = {
'issue/unsave',
'issue.severity/reset',
'issue.tabs/update',
+ 'issue/delete',
],
};