mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Update: Implement delete account feature + package updates
This commit is contained in:
868
package-lock.json
generated
868
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
50
package.json
50
package.json
@@ -13,41 +13,41 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hapi/iron": "^7.0.1",
|
"@hapi/iron": "^7.0.1",
|
||||||
"@headlessui/react": "^1.7.14",
|
"@headlessui/react": "^1.7.15",
|
||||||
"@headlessui/tailwindcss": "^0.1.3",
|
"@headlessui/tailwindcss": "^0.1.3",
|
||||||
"@hookform/resolvers": "^3.1.0",
|
"@hookform/resolvers": "^3.1.0",
|
||||||
"@mapbox/mapbox-sdk": "^0.15.1",
|
"@mapbox/mapbox-sdk": "^0.15.1",
|
||||||
"@next/bundle-analyzer": "^13.4.3",
|
"@next/bundle-analyzer": "^13.4.4",
|
||||||
"@prisma/client": "^4.13.0",
|
"@prisma/client": "^4.15.0",
|
||||||
"@react-email/components": "^0.0.6",
|
"@react-email/components": "^0.0.6",
|
||||||
"@react-email/render": "^0.0.7",
|
"@react-email/render": "^0.0.7",
|
||||||
"@react-email/tailwind": "^0.0.8",
|
"@react-email/tailwind": "^0.0.8",
|
||||||
"@vercel/analytics": "^1.0.0",
|
"@vercel/analytics": "^1.0.1",
|
||||||
"argon2": "^0.30.3",
|
"argon2": "^0.30.3",
|
||||||
"cloudinary": "^1.36.4",
|
"cloudinary": "^1.37.0",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.1.3",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mapbox-gl": "^2.14.1",
|
"mapbox-gl": "^2.15.0",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"multer-storage-cloudinary": "^4.0.0",
|
"multer-storage-cloudinary": "^4.0.0",
|
||||||
"next": "^13.3.4",
|
"next": "^13.4.4",
|
||||||
"next-connect": "^1.0.0-next.3",
|
"next-connect": "^1.0.0-next.3",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"pino": "^8.12.0",
|
"pino": "^8.14.1",
|
||||||
"pino-pretty": "^10.0.0",
|
"pino-pretty": "^10.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-daisyui": "^3.1.2",
|
"react-daisyui": "^3.1.2",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-email": "^1.9.3",
|
"react-email": "^1.9.3",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.44.3",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.9.0",
|
||||||
"react-intersection-observer": "^9.4.3",
|
"react-intersection-observer": "^9.4.4",
|
||||||
"react-map-gl": "^7.0.23",
|
"react-map-gl": "^7.0.25",
|
||||||
"react-responsive-carousel": "^3.2.23",
|
"react-responsive-carousel": "^3.2.23",
|
||||||
"sparkpost": "^2.1.4",
|
"sparkpost": "^2.1.4",
|
||||||
"swr": "^2.1.5",
|
"swr": "^2.1.5",
|
||||||
@@ -55,37 +55,37 @@
|
|||||||
"zod": "^3.21.4"
|
"zod": "^3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^7.6.0",
|
"@faker-js/faker": "^8.0.2",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
"@types/jsonwebtoken": "^9.0.2",
|
"@types/jsonwebtoken": "^9.0.2",
|
||||||
"@types/lodash": "^4.14.194",
|
"@types/lodash": "^4.14.195",
|
||||||
"@types/mapbox__mapbox-sdk": "^0.13.4",
|
"@types/mapbox__mapbox-sdk": "^0.13.4",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^18.16.3",
|
"@types/node": "^20.2.5",
|
||||||
"@types/passport-local": "^1.0.35",
|
"@types/passport-local": "^1.0.35",
|
||||||
"@types/react": "^18.2.0",
|
"@types/react": "^18.2.8",
|
||||||
"@types/react-dom": "^18.2.1",
|
"@types/react-dom": "^18.2.4",
|
||||||
"@types/sparkpost": "^2.1.5",
|
"@types/sparkpost": "^2.1.5",
|
||||||
"@vercel/fetch": "^6.2.0",
|
"@vercel/fetch": "^6.2.0",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"daisyui": "^2.51.6",
|
"daisyui": "^3.0.0",
|
||||||
"dotenv-cli": "^7.2.1",
|
"dotenv-cli": "^7.2.1",
|
||||||
"eslint": "^8.39.0",
|
"eslint": "^8.41.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "17.0.0",
|
"eslint-config-airbnb-typescript": "17.0.0",
|
||||||
"eslint-config-next": "^13.3.4",
|
"eslint-config-next": "^13.4.4",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.24",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"prettier-plugin-jsdoc": "^0.4.2",
|
"prettier-plugin-jsdoc": "^0.4.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.2.8",
|
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||||
"onchange": "^7.1.0",
|
"onchange": "^7.1.0",
|
||||||
"prisma": "^4.13.0",
|
"prisma": "^4.15.0",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
"tailwindcss-animate": "^1.0.5",
|
"tailwindcss-animate": "^1.0.5",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.1.3"
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"schema": "./src/prisma/schema.prisma",
|
"schema": "./src/prisma/schema.prisma",
|
||||||
|
|||||||
70
src/components/Account/DeleteAccount.tsx
Normal file
70
src/components/Account/DeleteAccount.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { Switch } from '@headlessui/react';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { FunctionComponent, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
const DeleteAccount: FunctionComponent = () => {
|
||||||
|
const [deleteToggled, setDeleteToggled] = useState(false);
|
||||||
|
|
||||||
|
const deleteRef = useRef<null | HTMLDialogElement>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card w-full space-y-4">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="flex w-full items-center justify-between space-x-5">
|
||||||
|
<div className="">
|
||||||
|
<h1 className="text-lg font-bold">Delete Your Account</h1>
|
||||||
|
<p>Want to leave? Delete your account here.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Switch
|
||||||
|
className="toggle"
|
||||||
|
id="edit-toggle"
|
||||||
|
checked={deleteToggled}
|
||||||
|
onClick={() => {
|
||||||
|
setDeleteToggled((val) => !val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{deleteToggled && (
|
||||||
|
<>
|
||||||
|
<div className="mt-3">
|
||||||
|
<button
|
||||||
|
className="btn-primary btn w-full"
|
||||||
|
onClick={() => deleteRef.current!.showModal()}
|
||||||
|
>
|
||||||
|
Delete my account
|
||||||
|
</button>
|
||||||
|
<dialog id="delete-modal" className="modal" ref={deleteRef}>
|
||||||
|
<div className="modal-box text-center">
|
||||||
|
<h3 className="text-lg font-bold">{`You're about to delete your account.`}</h3>
|
||||||
|
<p className="">This action is permanent and cannot be reversed.</p>
|
||||||
|
<div className="modal-action flex-col space-x-0 space-y-3">
|
||||||
|
<button
|
||||||
|
className="btn-error btn-sm btn w-full"
|
||||||
|
onClick={async () => {
|
||||||
|
deleteRef.current!.close();
|
||||||
|
await router.replace('/api/users/logout');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Okay, delete my account
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn-success btn-sm btn w-full"
|
||||||
|
onClick={() => deleteRef.current!.close()}
|
||||||
|
>
|
||||||
|
Go back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeleteAccount;
|
||||||
@@ -20,7 +20,7 @@ const Security: FunctionComponent = () => {
|
|||||||
|
|
||||||
const onSubmit: SubmitHandler<z.infer<typeof UpdatePasswordSchema>> = async (data) => {
|
const onSubmit: SubmitHandler<z.infer<typeof UpdatePasswordSchema>> = async (data) => {
|
||||||
await sendUpdatePasswordRequest(data);
|
await sendUpdatePasswordRequest(data);
|
||||||
setEditToggled(value => !value)
|
setEditToggled((value) => !value);
|
||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ const CustomToast: FC<{ children: ReactNode }> = ({ children }) => {
|
|||||||
const alertType = toastToClassName(t.type);
|
const alertType = toastToClassName(t.type);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`alert ${alertType} w-11/12 flex-row items-center shadow-lg animate-in fade-in duration-200 lg:w-2/12`}
|
className={`alert ${alertType} w-11/12 flex-row items-center shadow-lg animate-in fade-in duration-200 lg:w-4/12`}
|
||||||
>
|
>
|
||||||
<p className='text-sm'>{resolveValue(t.message, t)}</p>
|
<p>{resolveValue(t.message, t)}</p>
|
||||||
{t.type !== 'loading' && (
|
{t.type !== 'loading' && (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const DesktopLinks: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="block flex-none">
|
<div className="block flex-none">
|
||||||
<ul className="menu menu-horizontal p-0">
|
<ul className="menu menu-horizontal menu-sm px-1">
|
||||||
{pages.map((page) => {
|
{pages.map((page) => {
|
||||||
return (
|
return (
|
||||||
<li key={page.slug}>
|
<li key={page.slug}>
|
||||||
@@ -43,7 +43,7 @@ const MobileLinks: FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
<ul
|
<ul
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
className="dropdown-content menu rounded-box menu-compact mt-3 w-48 bg-base-100 p-2 shadow"
|
className="menu-compact dropdown-content menu rounded-box mt-3 w-48 bg-base-100 p-2 shadow"
|
||||||
>
|
>
|
||||||
{pages.map((page) => (
|
{pages.map((page) => (
|
||||||
<li key={page.slug}>
|
<li key={page.slug}>
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ const Button: FunctionComponent<FormButtonProps> = ({
|
|||||||
// eslint-disable-next-line react/button-has-type
|
// eslint-disable-next-line react/button-has-type
|
||||||
<button
|
<button
|
||||||
type={type}
|
type={type}
|
||||||
className={`btn-primary btn w-full rounded-xl ${isSubmitting ? 'loading' : ''}`}
|
className={`btn-primary btn w-full rounded-xl`}
|
||||||
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import AccountInfo from '@/components/Account/AccountInfo';
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import UserContext from '@/contexts/UserContext';
|
import UserContext from '@/contexts/UserContext';
|
||||||
import Security from '@/components/Account/Security';
|
import Security from '@/components/Account/Security';
|
||||||
|
import DeleteAccount from '@/components/Account/DeleteAccount';
|
||||||
|
|
||||||
const AccountPage: NextPage = () => {
|
const AccountPage: NextPage = () => {
|
||||||
const { user } = useContext(UserContext);
|
const { user } = useContext(UserContext);
|
||||||
@@ -48,6 +49,7 @@ const AccountPage: NextPage = () => {
|
|||||||
<Tab.Panel className="h-full space-y-5">
|
<Tab.Panel className="h-full space-y-5">
|
||||||
<AccountInfo />
|
<AccountInfo />
|
||||||
<Security />
|
<Security />
|
||||||
|
<DeleteAccount />
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
<Tab.Panel>Your posts!</Tab.Panel>
|
<Tab.Panel>Your posts!</Tab.Panel>
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Welcome from '@/emails/Welcome';
|
|||||||
import { render } from '@react-email/render';
|
import { render } from '@react-email/render';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { BASE_URL } from '@/config/env';
|
import { BASE_URL } from '@/config/env';
|
||||||
|
import { ReactElement } from 'react';
|
||||||
import GetUserSchema from './schema/GetUserSchema';
|
import GetUserSchema from './schema/GetUserSchema';
|
||||||
|
|
||||||
type UserSchema = z.infer<typeof GetUserSchema>;
|
type UserSchema = z.infer<typeof GetUserSchema>;
|
||||||
@@ -17,8 +18,10 @@ const sendConfirmationEmail = async ({ id, username, email }: UserSchema) => {
|
|||||||
const url = `${BASE_URL}/users/confirm?token=${confirmationToken}`;
|
const url = `${BASE_URL}/users/confirm?token=${confirmationToken}`;
|
||||||
const address = email;
|
const address = email;
|
||||||
|
|
||||||
const html = render(Welcome({ name, url, subject })!);
|
const component = Welcome({ name, url, subject })! as ReactElement<unknown, string>;
|
||||||
const text = render(Welcome({ name, url, subject })!, { plainText: true });
|
|
||||||
|
const html = render(component);
|
||||||
|
const text = render(component, { plainText: true });
|
||||||
|
|
||||||
await sendEmail({ address, subject, text, html });
|
await sendEmail({ address, subject, text, html });
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user