Skip to content

Commit 6045dfa

Browse files
feat: Leave organisation button
1 parent 0820044 commit 6045dfa

4 files changed

Lines changed: 141 additions & 31 deletions

File tree

components/admin/OrganisationModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ export default function OrganisationModal({
221221
)}
222222
/>
223223
<DialogFooter className='sm:justify-between'>
224-
{selectedOrganisation !== null && (
224+
{selectedOrganisation !== null && authToken?.isAdmin && (
225225
<AlertDialog>
226226
<AlertDialogTrigger asChild>
227227
<Button

components/admin/Organisations.tsx

Lines changed: 84 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ChevronRightIcon,
77
ChevronsLeftIcon,
88
ChevronsRightIcon,
9+
CircleArrowLeftIcon,
910
EditIcon,
1011
SearchIcon,
1112
Trash2Icon,
@@ -48,7 +49,9 @@ import {
4849
createOrganisation,
4950
deleteOrganisation,
5051
editOrganisation,
52+
leaveOrganisation,
5153
} from '@/lib/actions/organisation';
54+
import { useAuth } from '@/lib/hooks/useAuth';
5255
import { NewOrganisationClientSchema } from '@/lib/schema/organisation';
5356
import { IGCategory, type Organisation } from '@/prisma/generated/prisma';
5457

@@ -61,6 +64,7 @@ interface OrganisationsPageProps {
6164
export default function OrganisationsPage({
6265
organisations,
6366
}: OrganisationsPageProps) {
67+
const isAuthenticated = useAuth();
6468
const [searchTerm, setSearchTerm] = useState('');
6569
const [categoryFilter, setCategoryFilter] = useState('All categories');
6670

@@ -84,6 +88,11 @@ export default function OrganisationsPage({
8488
deleteOrganisationAction,
8589
deleteOrganisationPending,
8690
] = useActionState(deleteOrganisation, null);
91+
const [
92+
leaveOrganisationState,
93+
leaveOrganisationAction,
94+
leaveOrganisationPending,
95+
] = useActionState(leaveOrganisation, null);
8796

8897
const form = useForm<z.input<typeof NewOrganisationClientSchema>>({
8998
resolver: standardSchemaResolver(NewOrganisationClientSchema),
@@ -160,6 +169,14 @@ export default function OrganisationsPage({
160169
});
161170
};
162171

172+
const handleLeaveSubmit = (organisationId: number) => {
173+
const leaveOrganisation = new FormData();
174+
leaveOrganisation.set('id', organisationId.toString());
175+
leaveOrganisationAction(leaveOrganisation);
176+
setSelectedOrganisation(null);
177+
setIsModalOpen(false);
178+
};
179+
163180
// Filter organizations based on search and category
164181
const filteredOrganisations = organisations.filter((org) => {
165182
const matchesSearch = org.name
@@ -322,37 +339,74 @@ export default function OrganisationsPage({
322339
<EditIcon className='mr-1 h-4 w-4' />
323340
EDIT
324341
</Button>
325-
<AlertDialog>
326-
<AlertDialogTrigger asChild>
327-
<Button
328-
variant='outline'
329-
size='sm'
330-
className='border-orange-200 text-orange-600 hover:border-orange-300 hover:text-orange-800'
331-
>
332-
<Trash2Icon className='mr-1 h-4 w-4' />
333-
DELETE
334-
</Button>
335-
</AlertDialogTrigger>
336-
<AlertDialogContent>
337-
<AlertDialogHeader>
338-
<AlertDialogTitle>
339-
Are you absolutely sure?
340-
</AlertDialogTitle>
341-
<AlertDialogDescription>
342-
This action cannot be undone. This will permanently
343-
delete the organisation &quot;{org.name}&quot;.
344-
</AlertDialogDescription>
345-
</AlertDialogHeader>
346-
<AlertDialogFooter>
347-
<AlertDialogCancel>Cancel</AlertDialogCancel>
348-
<AlertDialogAction
349-
onClick={() => handleDeleteSubmit(org.id)}
342+
{isAuthenticated?.userOrgs?.find(
343+
(userOrg) => userOrg.id === org.id,
344+
) && (
345+
<AlertDialog>
346+
<AlertDialogTrigger asChild>
347+
<Button
348+
variant='outline'
349+
size='sm'
350+
className='border-orange-200 text-orange-600 hover:border-orange-300 hover:text-orange-800'
351+
>
352+
<CircleArrowLeftIcon className='mr-1 h-4 w-4' />
353+
LEAVE
354+
</Button>
355+
</AlertDialogTrigger>
356+
<AlertDialogContent>
357+
<AlertDialogHeader>
358+
<AlertDialogTitle>
359+
Are you absolutely sure?
360+
</AlertDialogTitle>
361+
<AlertDialogDescription>
362+
This action cannot be undone. You will no longer be
363+
a part of the organisation &quot;{org.name}&quot;.
364+
</AlertDialogDescription>
365+
</AlertDialogHeader>
366+
<AlertDialogFooter>
367+
<AlertDialogCancel>Cancel</AlertDialogCancel>
368+
<AlertDialogAction
369+
onClick={() => handleLeaveSubmit(org.id)}
370+
>
371+
Continue
372+
</AlertDialogAction>
373+
</AlertDialogFooter>
374+
</AlertDialogContent>
375+
</AlertDialog>
376+
)}
377+
{isAuthenticated?.isAdmin && (
378+
<AlertDialog>
379+
<AlertDialogTrigger asChild>
380+
<Button
381+
variant='outline'
382+
size='sm'
383+
className='border-orange-200 text-orange-600 hover:border-orange-300 hover:text-orange-800'
350384
>
351-
Continue
352-
</AlertDialogAction>
353-
</AlertDialogFooter>
354-
</AlertDialogContent>
355-
</AlertDialog>
385+
<Trash2Icon className='mr-1 h-4 w-4' />
386+
DELETE
387+
</Button>
388+
</AlertDialogTrigger>
389+
<AlertDialogContent>
390+
<AlertDialogHeader>
391+
<AlertDialogTitle>
392+
Are you absolutely sure?
393+
</AlertDialogTitle>
394+
<AlertDialogDescription>
395+
This action cannot be undone. This will permanently
396+
delete the organisation &quot;{org.name}&quot;.
397+
</AlertDialogDescription>
398+
</AlertDialogHeader>
399+
<AlertDialogFooter>
400+
<AlertDialogCancel>Cancel</AlertDialogCancel>
401+
<AlertDialogAction
402+
onClick={() => handleDeleteSubmit(org.id)}
403+
>
404+
Continue
405+
</AlertDialogAction>
406+
</AlertDialogFooter>
407+
</AlertDialogContent>
408+
</AlertDialog>
409+
)}
356410
</div>
357411
</div>
358412
</div>

lib/actions/organisation.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import prisma from '@/lib/prisma';
99
import {
1010
DeleteOrganisationSchema,
1111
EditOrganisationServerSchema,
12+
LeaveOrganisationSchema,
1213
NewOrganisationServerSchema,
1314
} from '@/lib/schema/organisation';
1415
import { formDataToObject } from '@/lib/utils';
@@ -185,3 +186,56 @@ export const deleteOrganisation = async (
185186
message: 'Successfully deleted organisation!',
186187
};
187188
};
189+
190+
export const leaveOrganisation = async (
191+
_prevState: ServerActionState,
192+
formData: FormData,
193+
): Promise<ServerActionState> => {
194+
const token = await getAuthCookie();
195+
if (!token) {
196+
return {
197+
success: false,
198+
message: 'Please login!',
199+
};
200+
}
201+
202+
let data;
203+
try {
204+
data = LeaveOrganisationSchema.parse(formDataToObject(formData));
205+
} catch (error) {
206+
if (error instanceof z.ZodError) {
207+
return {
208+
success: false,
209+
message: z.prettifyError(error),
210+
};
211+
}
212+
return {
213+
success: false,
214+
message: 'Unknown error occurred. Please contact admin.',
215+
};
216+
}
217+
218+
// TODO: Use soft delete
219+
const deleted = await prisma.userOnOrg.delete({
220+
where: {
221+
userId_orgId: {
222+
orgId: data.id,
223+
userId: token.userId,
224+
},
225+
},
226+
});
227+
228+
if (!deleted) {
229+
return {
230+
success: false,
231+
message: 'You are not part of the organisation!',
232+
};
233+
}
234+
235+
revalidatePath('/admin/organisations');
236+
237+
return {
238+
success: true,
239+
message: 'You have successfully left th organisation.',
240+
};
241+
};

lib/schema/organisation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export const DeleteOrganisationSchema = z.object({
1818
id: z.coerce.number<number>().int().positive(),
1919
});
2020

21+
export const LeaveOrganisationSchema = DeleteOrganisationSchema;
22+
2123
export const EditOrganisationClientSchema = z.object({
2224
...NewOrganisationClientSchema.shape,
2325
...DeleteOrganisationSchema.shape,

0 commit comments

Comments
 (0)