From 3ac4d3b00d663f2fcc3a2a7baae1cc7a32777022 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Thu, 28 Mar 2024 11:20:12 +0800 Subject: [PATCH] Updated account deletion process and refactor packages The primary update was on the process of account deletion where email notifications are now sent to users. The @kit/emails was also renamed to @kit/email-templates and adjustments were accordingly made on the relevant code and configuration files. In addition, package interaction was refactored to enhance readability and ease of maintenance. Some minor alterations were made on the User Interface, and code comments were updated. --- README.md | 2 +- apps/web/config/app.config.ts | 2 +- apps/web/middleware.ts | 9 +- apps/web/next.config.mjs | 1 + apps/web/package.json | 6 +- apps/web/public/locales/en/account.json | 1 + packages/billing-gateway/package.json | 4 +- .../{emails => email-templates}/README.md | 2 +- .../{emails => email-templates}/package.json | 2 +- .../src/account-delete.email.tsx | 0 .../{emails => email-templates}/src/index.ts | 0 .../src/invite.email.tsx | 0 .../{emails => email-templates}/tsconfig.json | 0 packages/features/accounts/package.json | 3 + .../account-danger-zone.tsx | 67 ++++++--- .../mfa/multi-factor-auth-list.tsx | 9 +- .../password/update-password-form.tsx | 79 ++++++---- .../update-account-details-form.tsx | 1 - .../personal-accounts-server-actions.ts | 57 +++++--- .../services/personal-accounts.service.ts | 72 ++++++++- .../src/components/email-otp-container.tsx | 137 ------------------ packages/features/team-accounts/package.json | 6 +- .../services/account-invitations.service.ts | 4 +- packages/supabase/package.json | 4 +- packages/ui/package.json | 5 +- packages/ui/src/makerkit/mdx/mdx-renderer.tsx | 2 +- packages/ui/src/shadcn/form.tsx | 2 +- packages/ui/src/shadcn/input-otp.tsx | 8 +- pnpm-lock.yaml | 56 ++++--- turbo.json | 13 +- 30 files changed, 290 insertions(+), 264 deletions(-) rename packages/{emails => email-templates}/README.md (86%) rename packages/{emails => email-templates}/package.json (95%) rename packages/{emails => email-templates}/src/account-delete.email.tsx (100%) rename packages/{emails => email-templates}/src/index.ts (100%) rename packages/{emails => email-templates}/src/invite.email.tsx (100%) rename packages/{emails => email-templates}/tsconfig.json (100%) delete mode 100644 packages/features/auth/src/components/email-otp-container.tsx diff --git a/README.md b/README.md index edc8a010a..0a57fcb4a 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Below are the reusable packages that can be shared across multiple applications - **`@kit/billing-gateway`**: Billing gateway package that defines the schema and logic for managing payment gateways - **`@kit/stripe`**: Stripe package that defines the schema and logic for managing Stripe. This is used by the `@kit/billing-gateway` package and abstracts the Stripe API. - **`@kit/lemon-squeezy`**: Lemon Squeezy package that defines the schema and logic for managing Lemon Squeezy. This is used by the `@kit/billing-gateway` package and abstracts the Lemon Squeezy API. -- **`@kit/emails`**: Here we define the email templates using the `react.email` package. +- **`@kit/email-templates`**: Here we define the email templates using the `react.email` package. - **`@kit/mailers`**: Mailer package that abstracts the email service provider (e.g., Resend, Cloudflare, SendGrid, Mailgun, etc.) And features that can be added to the application: diff --git a/apps/web/config/app.config.ts b/apps/web/config/app.config.ts index 8430b0b56..7c3b715fb 100644 --- a/apps/web/config/app.config.ts +++ b/apps/web/config/app.config.ts @@ -36,7 +36,7 @@ const AppConfigSchema = z.object({ }); const appConfig = AppConfigSchema.parse({ - name: 'Awesomely', + name: process.env.NEXT_PUBLIC_PRODUCT_NAME, title: 'Awesomely - Your SaaS Title', description: 'Your SaaS Description', url: process.env.NEXT_PUBLIC_SITE_URL, diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index d75fa085f..d18f3e3b0 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -128,9 +128,12 @@ function getPatterns() { handler: async (req: NextRequest, res: NextResponse) => { const supabase = createMiddlewareClient(req, res); const { data } = await supabase.auth.getSession(); + + // check if we need to verify MFA (user is authenticated but needs to verify MFA) const isVerifyMfa = req.nextUrl.pathname === pathsConfig.auth.verifyMfa; - // If user is logged in, redirect to home page. + // If user is logged in and does not need to verify MFA, + // redirect to home page. if (data.session && !isVerifyMfa) { return NextResponse.redirect( new URL(pathsConfig.app.home, req.nextUrl.origin).href, @@ -157,10 +160,6 @@ function getPatterns() { const requiresMultiFactorAuthentication = await checkRequiresMultiFactorAuthentication(supabase); - console.log({ - requiresMultiFactorAuthentication, - }); - // If user requires multi-factor authentication, redirect to MFA page. if (requiresMultiFactorAuthentication) { return NextResponse.redirect( diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index be6ba1124..d7667fad2 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -15,6 +15,7 @@ const INTERNAL_PACKAGES = [ '@kit/billing', '@kit/billing-gateway', '@kit/stripe', + '@kit/email-templates', ]; /** @type {import('next').NextConfig} */ diff --git a/apps/web/package.json b/apps/web/package.json index 6a51a6714..af471b38c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -22,7 +22,7 @@ "@kit/auth": "^0.1.0", "@kit/billing": "^0.1.0", "@kit/billing-gateway": "^0.1.0", - "@kit/emails": "^0.1.0", + "@kit/email-templates": "^0.1.0", "@kit/i18n": "^0.1.0", "@kit/mailers": "^0.1.0", "@kit/shared": "^0.1.0", @@ -32,7 +32,7 @@ "@next/mdx": "^14.1.0", "@radix-ui/react-icons": "^1.3.0", "@supabase/ssr": "^0.1.0", - "@supabase/supabase-js": "^2.39.8", + "@supabase/supabase-js": "^2.40.0", "@tanstack/react-query": "5.28.6", "@tanstack/react-query-next-experimental": "^5.28.6", "@tanstack/react-table": "^8.11.3", @@ -47,7 +47,7 @@ "next-themes": "^0.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "react-hook-form": "^7.51.1", + "react-hook-form": "^7.51.2", "react-i18next": "^14.1.0", "recharts": "^2.10.3", "rehype-autolink-headings": "^7.1.0", diff --git a/apps/web/public/locales/en/account.json b/apps/web/public/locales/en/account.json index feca5d60e..8cf3d3ca4 100644 --- a/apps/web/public/locales/en/account.json +++ b/apps/web/public/locales/en/account.json @@ -34,6 +34,7 @@ "currentPassword": "Current Password", "newPassword": "New Password", "repeatPassword": "Repeat New Password", + "repeatPasswordDescription": "Please repeat your new password to confirm it", "yourPassword": "Your Password", "updatePasswordSubmitLabel": "Update Password", "updateEmailCardTitle": "Update your Email", diff --git a/packages/billing-gateway/package.json b/packages/billing-gateway/package.json index a524476cb..0fc405faa 100644 --- a/packages/billing-gateway/package.json +++ b/packages/billing-gateway/package.json @@ -19,7 +19,7 @@ "@kit/stripe": "0.1.0", "@kit/supabase": "^0.1.0", "@kit/ui": "0.1.0", - "@supabase/supabase-js": "^2.39.8", + "@supabase/supabase-js": "^2.40.0", "lucide-react": "^0.363.0", "zod": "^3.22.4" }, @@ -33,7 +33,7 @@ "@kit/tailwind-config": "0.1.0", "@kit/tsconfig": "0.1.0", "@kit/ui": "*", - "@supabase/supabase-js": "^2.39.8", + "@supabase/supabase-js": "^2.40.0", "lucide-react": "^0.363.0", "zod": "^3.22.4" }, diff --git a/packages/emails/README.md b/packages/email-templates/README.md similarity index 86% rename from packages/emails/README.md rename to packages/email-templates/README.md index 9a713ef2a..dbd6ada88 100644 --- a/packages/emails/README.md +++ b/packages/email-templates/README.md @@ -1,4 +1,4 @@ -# Emails - @kit/emails +# Emails - @kit/email-templates This package is responsible for managing email templates using the react.email library. diff --git a/packages/emails/package.json b/packages/email-templates/package.json similarity index 95% rename from packages/emails/package.json rename to packages/email-templates/package.json index f7084ab12..a6a9260c5 100644 --- a/packages/emails/package.json +++ b/packages/email-templates/package.json @@ -1,5 +1,5 @@ { - "name": "@kit/emails", + "name": "@kit/email-templates", "private": true, "version": "0.1.0", "scripts": { diff --git a/packages/emails/src/account-delete.email.tsx b/packages/email-templates/src/account-delete.email.tsx similarity index 100% rename from packages/emails/src/account-delete.email.tsx rename to packages/email-templates/src/account-delete.email.tsx diff --git a/packages/emails/src/index.ts b/packages/email-templates/src/index.ts similarity index 100% rename from packages/emails/src/index.ts rename to packages/email-templates/src/index.ts diff --git a/packages/emails/src/invite.email.tsx b/packages/email-templates/src/invite.email.tsx similarity index 100% rename from packages/emails/src/invite.email.tsx rename to packages/email-templates/src/invite.email.tsx diff --git a/packages/emails/tsconfig.json b/packages/email-templates/tsconfig.json similarity index 100% rename from packages/emails/tsconfig.json rename to packages/email-templates/tsconfig.json diff --git a/packages/features/accounts/package.json b/packages/features/accounts/package.json index 4e6d1eedd..442dfa2df 100644 --- a/packages/features/accounts/package.json +++ b/packages/features/accounts/package.json @@ -16,6 +16,8 @@ }, "devDependencies": { "@kit/billing-gateway": "*", + "@kit/email-templates": "*", + "@kit/mailers": "*", "@kit/eslint-config": "0.2.0", "@kit/prettier-config": "0.1.0", "@kit/shared": "*", @@ -23,6 +25,7 @@ "@kit/tailwind-config": "0.1.0", "@kit/tsconfig": "0.1.0", "@kit/ui": "*", + "@hookform/resolvers": "^3.3.4", "@radix-ui/react-icons": "^1.3.0", "lucide-react": "^0.363.0", "react-hook-form": "^7.51.2", diff --git a/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx b/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx index baa3309aa..932bd01dd 100644 --- a/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx +++ b/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx @@ -1,17 +1,23 @@ 'use client'; +import { useFormStatus } from 'react-dom'; + +import { zodResolver } from '@hookform/resolvers/zod'; import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; import { useForm } from 'react-hook-form'; +import { z } from 'zod'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; -import { Button } from '@kit/ui/button'; import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '@kit/ui/dialog'; + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@kit/ui/alert-dialog'; +import { Button } from '@kit/ui/button'; import { ErrorBoundary } from '@kit/ui/error-boundary'; import { Form, FormControl, FormItem, FormLabel } from '@kit/ui/form'; import { Input } from '@kit/ui/input'; @@ -20,10 +26,6 @@ import { Trans } from '@kit/ui/trans'; import { deletePersonalAccountAction } from '../../server/personal-accounts-server-actions'; export function AccountDangerZone() { - return ; -} - -function DeleteAccountContainer() { return (
@@ -45,30 +47,39 @@ function DeleteAccountContainer() { function DeleteAccountModal() { return ( - - + + - + - - - + e.preventDefault()}> + + - - + + }> - - + + ); } function DeleteAccountForm() { - const form = useForm(); + const form = useForm({ + resolver: zodResolver( + z.object({ + confirmation: z.string().refine((value) => value === 'DELETE'), + }), + ), + defaultValues: { + confirmation: '', + }, + }); return (
@@ -110,17 +121,25 @@ function DeleteAccountForm() {
-
+ + + + + -
+ ); } function DeleteAccountSubmitButton() { + const { pending } = useFormStatus(); + return ( -
- - ); -} - -function EmailOtpForm({ - shouldCreateUser, - onSuccess, -}: React.PropsWithChildren<{ - shouldCreateUser: boolean; - onSuccess: (email: string) => void; -}>) { - const signInWithOtpMutation = useSignInWithOtp(); - - return ( -
{ - event.preventDefault(); - - const email = event.currentTarget.email.value; - - await signInWithOtpMutation.mutateAsync({ - email, - options: { - shouldCreateUser, - }, - }); - - onSuccess(email); - }} - > -
- - - -
-
- ); -} diff --git a/packages/features/team-accounts/package.json b/packages/features/team-accounts/package.json index 962a7a036..6f1fec1bd 100644 --- a/packages/features/team-accounts/package.json +++ b/packages/features/team-accounts/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@kit/accounts": "*", - "@kit/emails": "*", + "@kit/email-templates": "*", "@kit/eslint-config": "0.2.0", "@kit/mailers": "*", "@kit/prettier-config": "0.1.0", @@ -22,12 +22,12 @@ "@kit/tailwind-config": "0.1.0", "@kit/tsconfig": "0.1.0", "@kit/ui": "*", - "@hookform/resolvers/zod": "1.0.0", + "@hookform/resolvers": "^3.3.4", "lucide-react": "^0.363.0" }, "peerDependencies": { "@kit/accounts": "0.1.0", - "@kit/emails": "0.1.0", + "@kit/email-templates": "0.1.0", "@kit/mailers": "0.1.0", "@kit/shared": "0.1.0", "@kit/supabase": "0.1.0", diff --git a/packages/features/team-accounts/src/services/account-invitations.service.ts b/packages/features/team-accounts/src/services/account-invitations.service.ts index cded7f533..22281c0bf 100644 --- a/packages/features/team-accounts/src/services/account-invitations.service.ts +++ b/packages/features/team-accounts/src/services/account-invitations.service.ts @@ -149,7 +149,9 @@ export class AccountInvitationsService { for (const invitation of responseInvitations) { const promise = async () => { try { - const { renderInviteEmail } = await import('@kit/emails'); + const { renderInviteEmail } = await import( + '../../../../email-templates' + ); const html = renderInviteEmail({ link: this.getInvitationLink(invitation.invite_token), diff --git a/packages/supabase/package.json b/packages/supabase/package.json index b15a8608e..c0b6eaa54 100644 --- a/packages/supabase/package.json +++ b/packages/supabase/package.json @@ -29,13 +29,13 @@ "@kit/tailwind-config": "0.1.0", "@kit/tsconfig": "0.1.0", "@supabase/ssr": "^0.1.0", - "@supabase/supabase-js": "^2.39.8", + "@supabase/supabase-js": "^2.40.0", "@tanstack/react-query": "5.28.6" }, "peerDependencies": { "@epic-web/invariant": "^1.0.0", "@supabase/ssr": "^0.1.0", - "@supabase/supabase-js": "^2.39.8", + "@supabase/supabase-js": "^2.40.0", "@tanstack/react-query": "^5.28.6" }, "eslintConfig": { diff --git a/packages/ui/package.json b/packages/ui/package.json index b5510c719..3186a8e45 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -9,6 +9,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@hookform/resolvers": "^3.3.4", "@radix-ui/react-accordion": "1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", @@ -38,7 +39,7 @@ "class-variance-authority": "^0.7.0", "date-fns": "^3.2.0", "lucide-react": "^0.363.0", - "react-hook-form": "^7.49.2", + "react-hook-form": "^7.51.2", "react-i18next": "^14.1.0", "sonner": "^1.4.41", "zod": "^3.22.4" @@ -58,7 +59,7 @@ "lucide-react": "^0.363.0", "prettier": "^3.2.4", "react-day-picker": "^8.10.0", - "react-hook-form": "^7.51.1", + "react-hook-form": "^7.51.2", "react-i18next": "^14.1.0", "sonner": "^1.4.41", "tailwindcss": "3.4.1", diff --git a/packages/ui/src/makerkit/mdx/mdx-renderer.tsx b/packages/ui/src/makerkit/mdx/mdx-renderer.tsx index fce708615..73931d83e 100644 --- a/packages/ui/src/makerkit/mdx/mdx-renderer.tsx +++ b/packages/ui/src/makerkit/mdx/mdx-renderer.tsx @@ -2,7 +2,7 @@ import type { MDXComponents } from 'mdx/types'; import { getMDXComponent } from 'next-contentlayer/hooks'; import Components from './mdx-components'; -// @ts-expect-error +// @ts-ignore import styles from './mdx-renderer.module.css'; export function Mdx({ diff --git a/packages/ui/src/shadcn/form.tsx b/packages/ui/src/shadcn/form.tsx index ebe0f40c7..335659df3 100644 --- a/packages/ui/src/shadcn/form.tsx +++ b/packages/ui/src/shadcn/form.tsx @@ -5,7 +5,7 @@ import { Slot } from '@radix-ui/react-slot'; import type { ControllerProps, FieldPath, FieldValues } from 'react-hook-form'; import { Controller, FormProvider, useFormContext } from 'react-hook-form'; -import { cn } from '../utils/cn'; +import { cn } from '../utils'; import { Label } from './label'; const Form = FormProvider; diff --git a/packages/ui/src/shadcn/input-otp.tsx b/packages/ui/src/shadcn/input-otp.tsx index 5648462b6..a626efdc9 100644 --- a/packages/ui/src/shadcn/input-otp.tsx +++ b/packages/ui/src/shadcn/input-otp.tsx @@ -36,7 +36,13 @@ const InputOTPSlot = React.forwardRef< React.ComponentPropsWithoutRef<'div'> & { index: number } >(({ index, className, ...props }, ref) => { const inputOTPContext = React.useContext(OTPInputContext); - const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]; + const slot = inputOTPContext.slots[index]; + + if (!slot) { + return null; + } + + const { char, isActive, hasFakeCaret } = slot; return (
=12.22.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 diff --git a/turbo.json b/turbo.json index 9ac375933..1fa92bd2a 100644 --- a/turbo.json +++ b/turbo.json @@ -55,6 +55,17 @@ "globalEnv": [ "SKIP_ENV_VALIDATION", "STRIPE_SECRET_KEY", - "STRIPE_WEBHOOK_SECRET" + "STRIPE_WEBHOOK_SECRET", + "NEXT_PUBLIC_PRODUCT_NAME", + "EMAIL_SENDER", + "EMAIL_PORT", + "EMAIL_HOST", + "EMAIL_TLS", + "EMAIL_USER", + "EMAIL_PASSWORD", + "SIGN_IN_PATH", + "SIGN_UP_PATH", + "TEAM_ACCOUNTS_HOME_PATH", + "INVITATION_PAGE_PATH" ] } \ No newline at end of file