Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
feat(activities): file deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
jhdcruz committed Nov 20, 2024
1 parent f786b93 commit 30be119
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/app/eval/_components/Forms/BeneficiariesForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ const BeneficiariesForm = ({
label="Age"
max={100}
min={0}
placeholder="How old are you?"
my="sm"
placeholder="How old are you?"
readOnly={!!feedback}
{...form.getInputProps('respondent.age')}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Tooltip,
Box,
Timeline,
ActionIcon,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { useClipboard } from '@mantine/hooks';
Expand All @@ -26,14 +27,19 @@ import {
IconShieldExclamation,
IconShieldX,
IconUsersGroup,
IconX,
} from '@tabler/icons-react';
import { downloadActivityFile } from '@portal/activities/actions';
import {
deleteActivityFile,
downloadActivityFile,
} from '@portal/activities/actions';
import dayjs from '@/libs/dayjs';
import { isPrivate, isInternal } from '@/utils/access-control';
import { identifyFileType } from '@/utils/file-types';
import { PageLoader } from '@/components/Loader/PageLoader';
import { UserDisplay } from '@/components/Display/UserDisplay';
import { createBrowserClient } from '@/libs/supabase/client';
import { modals } from '@mantine/modals';

const RTEditor = dynamic(
() =>
Expand Down Expand Up @@ -219,6 +225,65 @@ function ActivityDetailsBody({
}
};

const handleDeleteFile = async (name: string, checksum: string) => {
modals.openConfirmModal({
centered: true,
title: `Delete ${name}?`,
children: (
<>
<Text>Are you sure you want to delete this file?</Text>
<Text fw="bold" mt="sm">
This action is irreversible!.
</Text>
</>
),
labels: { confirm: 'Delete', cancel: 'Cancel' },
confirmProps: { color: 'red' },
onConfirm: async () => {
notifications.show({
id: `delete-${checksum}`,
loading: true,
title: 'Deleting file...',
message: name,
withBorder: true,
});

const response = await deleteActivityFile(activity.id!, checksum);

if (response.status === 0) {
notifications.update({
id: `delete-${checksum}`,
loading: false,
title: 'File deleted',
message: 'The file has been successfully deleted.',
color: 'green',
icon: <IconCheck />,
withBorder: true,
withCloseButton: true,
autoClose: 4000,
});

// remove file from list
setFiles((files) => {
if (!files) return null;
return files.filter((file) => file.checksum !== checksum);
});
} else {
notifications.update({
id: `delete-${checksum}`,
loading: false,
title: 'Unable to delete file',
message: 'The file may have been deleted or is inaccessible.',
color: 'red',
withBorder: true,
withCloseButton: true,
autoClose: 4000,
});
}
},
});
};

// show notification when email is copied to clipboard
useEffect(() => {
// fixes notification appearing twice
Expand Down Expand Up @@ -450,9 +515,11 @@ function ActivityDetailsBody({
onClick={() => saveFile(file.name, file.checksum)}
size="sm"
ta="left"
w={260}
>
{file.name}
</Anchor>

<Group gap={2} mt={4}>
<Text c="dimmed" size="xs">
{dayjs(file.uploaded_at).fromNow()}
Expand All @@ -475,6 +542,15 @@ function ActivityDetailsBody({
</Tooltip>
</Group>
</div>

<ActionIcon
aria-label="Delete file"
color="dimmed"
onClick={() => handleDeleteFile(file.name, file.checksum)}
variant="transparent"
>
<IconX size={16} />
</ActionIcon>
</Group>
))}
</>
Expand Down
44 changes: 44 additions & 0 deletions src/app/portal/activities/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,50 @@ export async function downloadActivityFile(
};
}

/**
* Delete activity file based on checksum
*
* @param activityId - The activity id to delete.
* @param checksum - The checksum of the file.
*/
export async function deleteActivityFile(
activityId: string,
checksum: string,
): Promise<ApiResponse> {
const cookieStore = cookies();
const supabase = await createServerClient(cookieStore);

// remove from table
const db = supabase.from('activity_files').delete().eq('checksum', checksum);

const storage = supabase.storage
.from('activities')
.remove([`${activityId}/${checksum}`]);

const [dbRes, storageRes] = await Promise.all([db, storage]);

if (dbRes.error) {
return {
status: 1,
title: 'Unable to delete file metadata',
message: dbRes.error.message,
};
}

if (storageRes.error) {
return {
status: 1,
title: 'Unable to delete file',
message: storageRes.error.message,
};
}

return {
status: 0,
title: 'File deleted',
};
}

/**
* Check if user is subscribed to the activity
*
Expand Down
2 changes: 1 addition & 1 deletion src/app/portal/users/_components/Modals/UserEditModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function UserEdit({
</Text>
</>
),
labels: { confirm: 'Confirm', cancel: 'Cancel' },
labels: { confirm: 'Delete', cancel: 'Cancel' },
confirmProps: { color: 'red' },
onConfirm: async () => {
const response = await deleteUser(selected.id);
Expand Down

0 comments on commit 30be119

Please sign in to comment.