Skip to content

Commit af328d4

Browse files
Subscription dialog flow (#10)
* create initial subscription flow * remove subscription pages * full screen dialog on mobile * add exit symbols to subscription dialogs * items hyperlink is base url * complete subscription dialog flow --------- Co-authored-by: Gabriel Hall <[email protected]>
1 parent f851f39 commit af328d4

15 files changed

+224
-166
lines changed

src/components/AuthWidget.tsx

+27-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ import Link from 'next/link';
66
import {
77
FaCircleNotch,
88
FaList,
9+
FaMailBulk,
10+
FaNotesMedical,
911
FaSignInAlt,
1012
FaSignOutAlt,
1113
FaUser,
1214
FaUserGraduate
1315
} from 'react-icons/fa';
16+
import useDialogStore from '../stores/DialogStore';
1417

1518
function AuthWidget() {
1619
const { data: session, status } = useSession();
20+
const { subscribeDialog, manageSubscriptionsDialog } = useDialogStore();
1721

1822
if (status === 'loading') {
1923
return (
@@ -54,14 +58,36 @@ function AuthWidget() {
5458
</Link>
5559
</li>
5660
<li>
57-
<Link className="font-bold text-base-content" href="/items">
61+
<Link className="font-bold text-base-content" href="/">
5862
<FaList />
5963
<span>Items</span>
6064
</Link>
6165
</li>
6266
<li />
6367
</>
6468
)}
69+
70+
<li>
71+
<button
72+
type="button"
73+
onClick={subscribeDialog}
74+
className="font-bold text-base-content"
75+
>
76+
<FaMailBulk />
77+
<span>Subscriptions</span>
78+
</button>
79+
</li>
80+
<li>
81+
<button
82+
type="button"
83+
onClick={manageSubscriptionsDialog}
84+
className="font-bold text-base-content"
85+
>
86+
<FaNotesMedical />
87+
<span>Manage</span>
88+
</button>
89+
</li>
90+
<li />
6591
<li>
6692
<button
6793
type="button"

src/components/Dialog.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function Dialog({ children, isOpen, onClose }: DialogProps) {
2626
</Transition.Child>
2727

2828
<div className="fixed inset-0 overflow-y-auto">
29-
<div className="flex min-h-full items-center justify-center p-4 text-center">
29+
<div className="flex h-screen items-center justify-center text-center md:min-h-full md:p-4">
3030
<Transition.Child
3131
as={Fragment}
3232
enter="ease-out duration-300"
@@ -36,7 +36,7 @@ export function Dialog({ children, isOpen, onClose }: DialogProps) {
3636
leaveFrom="opacity-100 scale-100"
3737
leaveTo="opacity-0 scale-95"
3838
>
39-
<HeadlessDialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-base-100 p-6 text-left align-middle shadow-xl transition-all">
39+
<HeadlessDialog.Panel className="h-full w-full transform overflow-hidden bg-base-100 p-6 text-left align-middle shadow-xl transition-all md:h-auto md:max-w-md md:rounded-2xl">
4040
{children}
4141
</HeadlessDialog.Panel>
4242
</Transition.Child>

src/components/ItemCreateModal.tsx renamed to src/components/ItemCreateDialog.tsx

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import useZodForm from 'lib/form';
77
import { ItemCreateSchema } from 'lib/schemas';
88
import { toast } from 'react-toastify';
99
import { trpc } from 'utils/trpc';
10-
import useModalStore from '../stores/ModalStore';
10+
import useDialogStore from '../stores/DialogStore';
1111
import { Dialog } from './Dialog';
1212

13-
function ItemCreateModal() {
14-
const { modal, clearModal } = useModalStore();
13+
function ItemCreateDialog() {
14+
const { dialog, clearDialog } = useDialogStore();
1515

1616
const context = trpc.useContext();
1717
const auditCreateMutation = trpc.audit.create.useMutation();
@@ -24,15 +24,15 @@ function ItemCreateModal() {
2424
change: { create: item }
2525
});
2626
await context.item.invalidate();
27-
clearModal();
27+
clearDialog();
2828
toast('Item Created');
2929
}
3030
});
3131

3232
const methods = useZodForm({ schema: ItemCreateSchema });
3333

3434
return (
35-
<Dialog isOpen={modal === 'createItem'} onClose={clearModal}>
35+
<Dialog isOpen={dialog === 'createItem'} onClose={clearDialog}>
3636
<h3 className="text-center text-lg font-bold">Add Item</h3>
3737
<hr />
3838
<form
@@ -216,7 +216,7 @@ function ItemCreateModal() {
216216
className="btn-error btn-sm btn"
217217
onClick={() => {
218218
methods.reset();
219-
clearModal();
219+
clearDialog();
220220
}}
221221
>
222222
Cancel
@@ -230,4 +230,4 @@ function ItemCreateModal() {
230230
);
231231
}
232232

233-
export default ItemCreateModal;
233+
export default ItemCreateDialog;

src/components/ItemEditModal.tsx renamed to src/components/ItemEditDialog.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import useZodForm from 'lib/form';
77
import { ItemEditSchema } from 'lib/schemas';
88
import { toast } from 'react-toastify';
99
import { trpc } from 'utils/trpc';
10-
import useModalStore from '../stores/ModalStore';
10+
import useDialogStore from '../stores/DialogStore';
1111
import { Dialog } from './Dialog';
1212

1313
type Props = {
1414
itemId: string;
1515
};
1616

17-
function ItemEditModal({ itemId }: Props) {
18-
const { modal, clearModal } = useModalStore();
17+
function ItemEditDialog({ itemId }: Props) {
18+
const { dialog, clearDialog } = useDialogStore();
1919

2020
const { data: item, status } = trpc.item.byId.useQuery({ id: itemId });
2121

@@ -46,7 +46,7 @@ function ItemEditModal({ itemId }: Props) {
4646
methods.reset(item);
4747

4848
return (
49-
<Dialog isOpen={modal === 'editItem'} onClose={clearModal}>
49+
<Dialog isOpen={dialog === 'editItem'} onClose={clearDialog}>
5050
<h3 className="text-center text-lg font-bold">Add Item</h3>
5151
<hr />
5252
<form
@@ -224,7 +224,7 @@ function ItemEditModal({ itemId }: Props) {
224224
className="btn-error btn-sm btn"
225225
onClick={() => {
226226
methods.reset();
227-
clearModal();
227+
clearDialog();
228228
}}
229229
>
230230
Cancel
@@ -238,4 +238,4 @@ function ItemEditModal({ itemId }: Props) {
238238
);
239239
}
240240

241-
export default ItemEditModal;
241+
export default ItemEditDialog;

src/components/ItemRow.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
FaNewspaper
1616
} from 'react-icons/fa';
1717
import { trpc } from 'utils/trpc';
18-
import useModalStore from '../stores/ModalStore';
18+
import useDialogStore from '../stores/DialogStore';
1919

2020
type ItemHistoryProps = {
2121
item: Item;
@@ -148,7 +148,7 @@ type ItemRowProps = {
148148
};
149149

150150
export default function ItemRow({ item, selected, setSelected }: ItemRowProps) {
151-
const { editItemModal } = useModalStore();
151+
const { editItemDialog } = useDialogStore();
152152

153153
return (
154154
<tr>
@@ -215,7 +215,11 @@ export default function ItemRow({ item, selected, setSelected }: ItemRowProps) {
215215
</td>
216216
<td>{StatusIcons[item.status]}</td>
217217
<td>
218-
<button type="button" onClick={editItemModal} className="btn-ghost btn">
218+
<button
219+
type="button"
220+
onClick={editItemDialog}
221+
className="btn-ghost btn"
222+
>
219223
<FaEdit size={20} />
220224
</button>
221225
</td>

src/pages/subscribe.tsx renamed to src/components/SubscriptionDialog.tsx

+42-29
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
/* eslint-disable react/jsx-props-no-spreading */
2+
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
23
/* eslint-disable jsx-a11y/label-has-associated-control */
34

45
import { Category } from '@prisma/client';
5-
import MainLayout from 'components/layout/MainLayout';
66
import useZodForm from 'lib/form';
77
import { useSession } from 'next-auth/react';
88
import Image from 'next/image';
9-
import Link from 'next/link';
9+
import { FaTimes } from 'react-icons/fa';
1010
import { toast } from 'react-toastify';
1111
import { Categories } from 'types';
1212
import { trpc } from 'utils/trpc';
1313
import { z } from 'zod';
14+
import useDialogStore from '../stores/DialogStore';
15+
import { Dialog } from './Dialog';
16+
17+
export default function SubscriptionDialog() {
18+
const { dialog, clearDialog, manageSubscriptionsDialog } = useDialogStore();
19+
20+
const context = trpc.useContext();
1421

15-
export default function SubscribePage() {
1622
const { data: subscriptions, status } = trpc.subscription.list.useQuery();
1723
const { data: session, status: sessionStatus } = useSession({
1824
required: true
1925
});
2026

21-
const context = trpc.useContext();
22-
2327
const subscriptionCreate = trpc.subscription.create.useMutation({
2428
onSuccess: () => {
2529
toast('Subscription created!');
@@ -54,9 +58,21 @@ export default function SubscribePage() {
5458
if (status === 'loading') return <div>Loading...</div>;
5559

5660
return (
57-
<MainLayout>
58-
<div className="flex flex-col gap-2">
59-
<div className="text-2xl font-bold md:text-4xl">Item Subscription</div>
61+
<Dialog isOpen={dialog === 'subscribe'} onClose={clearDialog}>
62+
<div className="flex h-full flex-col gap-2">
63+
<div className="flex items-center justify-between">
64+
<span className="text-2xl font-bold md:text-3xl">
65+
Email Notification Sign-Up
66+
</span>
67+
<button
68+
type="button"
69+
onClick={clearDialog}
70+
className="btn-ghost btn-circle btn"
71+
>
72+
<FaTimes className="h-6 w-6" />
73+
</button>
74+
</div>
75+
<div className="divider my-0" />
6076
<div>
6177
<div className="flex items-center gap-2">
6278
<div className="relative h-20 w-20">
@@ -67,26 +83,25 @@ export default function SubscribePage() {
6783
</span>
6884
</div>
6985
</div>
70-
<div className="prose-sm prose">
86+
<div className="prose-neutral prose">
7187
<strong>
7288
<ol>
73-
<li>Add your information below.</li>
89+
<li>Select up to two product categories below</li>
7490
<li>
75-
You&apos;ll receive an email at the end of each day filtered to
76-
your selected category.
91+
You&apos;ll receive a daily email listing newly lost items for
92+
each category. Email notifications will run for seven
93+
consecutive days.
7794
</li>
7895
<li>
79-
You&apos;ll receive emails for seven days, and you can
80-
unsubscribe at any time.
96+
If you wish to renew notifications after this period, please
97+
revisit this page.
8198
</li>
8299
</ol>
83100
</strong>
84101
</div>
85-
<div>
86-
<span className="text-lg font-bold">Add Item Information</span>
87-
</div>
102+
<div className="divider my-0" />
88103
<form
89-
className="flex w-full flex-col gap-2 font-bold"
104+
className="flex w-full flex-1 flex-col gap-2 font-bold"
90105
onSubmit={methods.handleSubmit(async (data) => {
91106
data.categories.forEach(async (category) => {
92107
await subscriptionCreate.mutateAsync({ category });
@@ -95,7 +110,7 @@ export default function SubscribePage() {
95110
>
96111
<div className="form-control gap-1">
97112
<label className="label">
98-
<span className="label-text">Email Address</span>
113+
<span className="label-text text-lg">Contact Information</span>
99114
</label>
100115
<input
101116
type="email"
@@ -106,7 +121,7 @@ export default function SubscribePage() {
106121
</div>
107122
<div className="form-control gap-1">
108123
<label className="label">
109-
<span className="label-text">Item Category</span>
124+
<span className="label-text text-lg">Select Item Category</span>
110125
</label>
111126
<div className="flex flex-wrap gap-2">
112127
{Object.values(Category).map((category) => (
@@ -130,26 +145,24 @@ export default function SubscribePage() {
130145
{methods.formState.errors.categories?.message}
131146
</span>
132147
</div>
133-
<div className="form-control">
148+
<div className="form-control mt-4 flex-1 justify-end gap-2">
134149
<button
135150
type="submit"
136151
className="btn-accent btn-sm btn"
137152
disabled={!methods.formState.isDirty}
138153
>
139154
Subscribe
140155
</button>
141-
</div>
142-
<div className="divider" />
143-
<div className="form-control">
144-
<Link
145-
href="/subscriptions"
146-
className="btn-outline btn-accent btn-ghost btn-sm btn"
156+
<button
157+
type="button"
158+
onClick={manageSubscriptionsDialog}
159+
className="btn-accent btn-ghost btn-outline btn-sm btn"
147160
>
148161
Manage Subscriptions
149-
</Link>
162+
</button>
150163
</div>
151164
</form>
152165
</div>
153-
</MainLayout>
166+
</Dialog>
154167
);
155168
}

0 commit comments

Comments
 (0)