Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add confirmation modals to destructive actions #270

Merged
merged 3 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 169 additions & 123 deletions src/components/admin/event/AdminPickupEvent/AdminPickupEventForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import DetailsFormItem from '@/components/admin/DetailsFormItem';
import { Button } from '@/components/common';
import { config, showToast } from '@/lib';
import useConfirm from '@/lib/hooks/useConfirm';
import { AdminEventManager } from '@/lib/managers';
import { UUID } from '@/lib/types';
import { OrderPickupEvent } from '@/lib/types/apiRequests';
Expand Down Expand Up @@ -203,136 +204,181 @@ const AdminPickupEventForm = ({ mode, defaultData = {}, token, upcomingEvents }:

const defaultFormText = loading ? 'Loading events from API...' : 'Select an Event';

const confirmComplete = useConfirm({
title: 'Confirm completion',
question:
'Are you sure you want to complete this pickup event? Pending orders will be marked as missed. This cannot be undone.',
action: 'Complete',
});
const confirmCancel = useConfirm({
title: 'Confirm cancellation',
question: 'Are you sure you want to cancel this pickup event? This cannot be undone.',
action: 'Cancel event',
cancel: 'Back',
});
const confirmDelete = useConfirm({
title: 'Confirm deletion',
question: 'Are you sure you want to delete this pickup event? This cannot be undone.',
action: 'Delete',
});
const confirmReset = useConfirm({
title: 'Confirm reset',
question: 'Are you sure you want to reset this form? This cannot be undone.',
action: 'Reset',
});

return (
<form onSubmit={handleSubmit(mode === 'edit' ? editPickupEvent : createPickupEvent)}>
<div className={style.header}>
<h1>{mode === 'edit' ? 'Modify' : 'Create'} Pickup Event</h1>

{defaultData.uuid ? (
<Link
className={style.viewPage}
href={`${config.admin.store.pickup}/${defaultData.uuid}`}
>
View pickup event page
<BsArrowRight aria-hidden />
</Link>
) : null}
</div>

<div className={style.form}>
<label htmlFor="title">Title</label>
<DetailsFormItem error={errors.title?.message}>
<input
type="text"
id="title"
placeholder="The Raccoon Pickup Event"
{...register('title', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="start">Starts At</label>
<DetailsFormItem error={errors.start?.message}>
<input
type="datetime-local"
id="start"
{...register('start', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="end">Ends At</label>
<DetailsFormItem error={errors.end?.message}>
<input
type="datetime-local"
id="end"
{...register('end', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="description">Description</label>
<DetailsFormItem error={errors.description?.message}>
<textarea
id="description"
{...register('description', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="points">Order Limit</label>
<DetailsFormItem error={errors.orderLimit?.message}>
<input
type="number"
id="orderLimit"
{...register('orderLimit', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="LinkedEvent">Linked Event</label>
<DetailsFormItem error={errors.linkedEventUuid?.message}>
<select
id=""
placeholder={defaultFormText}
defaultValue={defaultFormText}
{...register('linkedEventUuid', {})}
>
<option disabled>{defaultFormText}</option>

{upcomingEvents?.map(event => (
<option key={event.uuid} value={event.uuid}>
{event.title} ({DateTime.fromISO(event.start).toFormat('f')})
</option>
))}
</select>
</DetailsFormItem>
</div>
<div className={style.submitButtons}>
{mode === 'edit' ? (
<>
<Button submit disabled={loading}>
Save changes
</Button>

<Button onClick={resetForm} disabled={loading} destructive>
Discard changes
</Button>
{status === OrderPickupEventStatus.ACTIVE ? (
<Button onClick={() => completePickupEvent(uuid ?? '', token)} disabled={loading}>
Complete pickup event
<>
{confirmComplete.modal}
{confirmCancel.modal}
{confirmDelete.modal}
{confirmReset.modal}
<form onSubmit={handleSubmit(mode === 'edit' ? editPickupEvent : createPickupEvent)}>
<div className={style.header}>
<h1>{mode === 'edit' ? 'Modify' : 'Create'} Pickup Event</h1>

{defaultData.uuid ? (
<Link
className={style.viewPage}
href={`${config.admin.store.pickup}/${defaultData.uuid}`}
>
View pickup event page
<BsArrowRight aria-hidden />
</Link>
) : null}
</div>

<div className={style.form}>
<label htmlFor="title">Title</label>
<DetailsFormItem error={errors.title?.message}>
<input
type="text"
id="title"
placeholder="The Raccoon Pickup Event"
{...register('title', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="start">Starts At</label>
<DetailsFormItem error={errors.start?.message}>
<input
type="datetime-local"
id="start"
{...register('start', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="end">Ends At</label>
<DetailsFormItem error={errors.end?.message}>
<input
type="datetime-local"
id="end"
{...register('end', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="description">Description</label>
<DetailsFormItem error={errors.description?.message}>
<textarea
id="description"
{...register('description', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="points">Order Limit</label>
<DetailsFormItem error={errors.orderLimit?.message}>
<input
type="number"
id="orderLimit"
{...register('orderLimit', {
required: 'Required',
})}
/>
</DetailsFormItem>

<label htmlFor="LinkedEvent">Linked Event</label>
<DetailsFormItem error={errors.linkedEventUuid?.message}>
<select
id=""
placeholder={defaultFormText}
defaultValue={defaultFormText}
{...register('linkedEventUuid', {})}
>
<option disabled>{defaultFormText}</option>

{upcomingEvents?.map(event => (
<option key={event.uuid} value={event.uuid}>
{event.title} ({DateTime.fromISO(event.start).toFormat('f')})
</option>
))}
</select>
</DetailsFormItem>
</div>
<div className={style.submitButtons}>
{mode === 'edit' ? (
<>
<Button submit disabled={loading}>
Save changes
</Button>
<Button
onClick={() => confirmReset.confirm(resetForm)}
disabled={loading}
destructive
>
Discard changes
</Button>
{status === OrderPickupEventStatus.ACTIVE ? (
<Button
onClick={() =>
confirmComplete.confirm(() => completePickupEvent(uuid ?? '', token))
}
disabled={loading}
>
Complete pickup event
</Button>
) : null}
{status === OrderPickupEventStatus.ACTIVE ? (
<Button
onClick={() => confirmCancel.confirm(() => cancelPickupEvent(uuid ?? '', token))}
disabled={loading}
destructive
>
Cancel pickup event
</Button>
) : null}
<Button
onClick={() => confirmDelete.confirm(deletePickupEvent)}
disabled={loading}
destructive
>
Delete pickup event
</Button>
</>
) : (
<>
<Button submit disabled={loading}>
Create pickup event
</Button>
) : null}
{status === OrderPickupEventStatus.ACTIVE ? (
<Button
onClick={() => cancelPickupEvent(uuid ?? '', token)}
onClick={() => confirmReset.confirm(resetForm)}
disabled={loading}
destructive
>
Cancel pickup event
Clear form
</Button>
) : null}
<Button onClick={deletePickupEvent} disabled={loading} destructive>
Delete pickup event
</Button>
</>
) : (
<>
<Button submit disabled={loading}>
Create pickup event
</Button>
<Button onClick={resetForm} disabled={loading} destructive>
Clear form
</Button>
</>
)}
</div>
</form>
</>
)}
</div>
</form>
</>
);
};

Expand Down
24 changes: 21 additions & 3 deletions src/components/admin/event/EventDetailsForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import NotionAutofill from '@/components/admin/event/NotionAutofill';
import { Button, Cropper } from '@/components/common';
import { config, showToast } from '@/lib';
import { KlefkiAPI } from '@/lib/api';
import useConfirm from '@/lib/hooks/useConfirm';
import { AdminEventManager } from '@/lib/managers';
import { CookieService } from '@/lib/services';
import { FillInLater } from '@/lib/types';
Expand Down Expand Up @@ -175,8 +176,21 @@ const EventDetailsForm = (props: IProps) => {
});
};

const confirmDelete = useConfirm({
title: 'Confirm deletion',
question: 'Are you sure you want to delete this event? This cannot be undone.',
action: 'Delete',
});
const confirmReset = useConfirm({
title: 'Confirm reset',
question: 'Are you sure you want to reset this form? This cannot be undone.',
action: 'Reset',
});

return (
<div className={style.container}>
{confirmDelete.modal}
{confirmReset.modal}
<Link href="/admin" className={style.back}>
Back
</Link>
Expand Down Expand Up @@ -340,10 +354,14 @@ const EventDetailsForm = (props: IProps) => {
<Button onClick={handleSubmit(editEvent)} disabled={loading}>
Save Changes
</Button>
<Button onClick={resetForm} disabled={loading} destructive>
<Button onClick={() => confirmReset.confirm(resetForm)} disabled={loading} destructive>
Discard Changes
</Button>
<Button onClick={deleteEvent} disabled={loading} destructive>
<Button
onClick={() => confirmDelete.confirm(deleteEvent)}
disabled={loading}
destructive
>
Delete Event
</Button>
</>
Expand All @@ -352,7 +370,7 @@ const EventDetailsForm = (props: IProps) => {
<Button onClick={handleSubmit(createEvent)} disabled={loading}>
Create Event
</Button>
<Button onClick={resetForm} disabled={loading} destructive>
<Button onClick={() => confirmReset.confirm(resetForm)} disabled={loading} destructive>
Clear Form
</Button>
</>
Expand Down
Loading
Loading