From a319ea0f5e6efd7f405baa7892a1913626274717 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 19 Feb 2025 16:07:04 +1100 Subject: [PATCH] fix: add public profiles tests --- .../settings+/public-profile+/index.tsx | 5 +- apps/remix/app/routes/_profile+/_layout.tsx | 8 +- apps/remix/app/routes/_profile+/p.$url.tsx | 1 - apps/remix/app/routes/_recipient+/_layout.tsx | 2 +- .../share.$slug.opengraph.tsx | 2 +- .../routes/_unauthenticated+/share.$slug.tsx | 2 +- apps/remix/app/routes/api+/share.ts | 2 +- apps/remix/app/routes/api+/stripe.webhook.ts | 2 +- apps/remix/app/routes/api+/webhook.trigger.ts | 2 +- apps/remix/app/routes/embed+/_layout.tsx | 2 +- apps/remix/server/api/files.ts | 4 +- apps/remix/server/router.ts | 4 +- apps/remix/server/trpc/hono-trpc-open-api.ts | 4 +- packages/api/hono.ts | 4 +- packages/api/v1/middleware/authenticated.ts | 2 +- .../public-profiles/public-profiles.spec.ts | 144 ++++++++++++++++++ .../server/lib/session/session-cookies.ts | 4 +- packages/auth/server/lib/utils/get-session.ts | 2 +- .../lib/utils/handle-oauth-callback-url.ts | 4 +- packages/auth/server/routes/email-password.ts | 2 +- .../ee/server-only/stripe/webhook/handler.ts | 2 +- .../lib/client-only/providers/session.tsx | 4 +- packages/lib/jobs/client/inngest.ts | 2 +- packages/lib/server-only/user/create-user.ts | 10 +- packages/prisma/schema.prisma | 6 +- packages/ui/primitives/lazy-pdf-viewer.tsx | 2 +- 26 files changed, 187 insertions(+), 41 deletions(-) create mode 100644 packages/app-tests/e2e/public-profiles/public-profiles.spec.ts diff --git a/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx b/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx index e022daf06..8123cc8d5 100644 --- a/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx +++ b/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx @@ -60,7 +60,7 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps) const { _ } = useLingui(); const { toast } = useToast(); - const { user } = useSession(); + const { user, refreshSession } = useSession(); const team = useOptionalCurrentTeam(); const [isPublicProfileVisible, setIsPublicProfileVisible] = useState(profile.enabled); @@ -96,6 +96,9 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps) }); } else { await updateUserProfile(data); + + // Need to refresh session because we're editing the user's profile. + await refreshSession(); } if (data.enabled === undefined && !isPublicProfileVisible) { diff --git a/apps/remix/app/routes/_profile+/_layout.tsx b/apps/remix/app/routes/_profile+/_layout.tsx index 404d260d5..bc0cc871a 100644 --- a/apps/remix/app/routes/_profile+/_layout.tsx +++ b/apps/remix/app/routes/_profile+/_layout.tsx @@ -7,7 +7,7 @@ import { ChevronLeft } from 'lucide-react'; import { Link, Outlet } from 'react-router'; import LogoIcon from '@documenso/assets/logo_icon.png'; -import { useSession } from '@documenso/lib/client-only/providers/session'; +import { useOptionalSession } from '@documenso/lib/client-only/providers/session'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -21,7 +21,7 @@ export function meta() { } export default function PublicProfileLayout() { - const session = useSession(); + const { sessionData } = useOptionalSession(); const [scrollY, setScrollY] = useState(0); @@ -37,8 +37,8 @@ export default function PublicProfileLayout() { return (
- {session ? ( - + {sessionData ? ( + ) : (
null); - // Todo: Test if (!publicProfile || !publicProfile.profile.enabled) { throw new Response('Not Found', { status: 404 }); } diff --git a/apps/remix/app/routes/_recipient+/_layout.tsx b/apps/remix/app/routes/_recipient+/_layout.tsx index cd6736e33..24228b029 100644 --- a/apps/remix/app/routes/_recipient+/_layout.tsx +++ b/apps/remix/app/routes/_recipient+/_layout.tsx @@ -29,7 +29,7 @@ export default function RecipientLayout() { ); } -// Todo: Use generic error boundary. +// Todo: (RR7) Use generic error boundary. export function ErrorBoundary() { return (
diff --git a/apps/remix/app/routes/_unauthenticated+/share.$slug.opengraph.tsx b/apps/remix/app/routes/_unauthenticated+/share.$slug.opengraph.tsx index 9afd8fa73..5c3f26af4 100644 --- a/apps/remix/app/routes/_unauthenticated+/share.$slug.opengraph.tsx +++ b/apps/remix/app/routes/_unauthenticated+/share.$slug.opengraph.tsx @@ -1,4 +1,4 @@ -// Todo: Test, used AI to migrate this component from NextJS to Remix. +// Todo: (RR7) Test, used AI to migrate this component from NextJS to Remix. import satori from 'satori'; import sharp from 'sharp'; import { P, match } from 'ts-pattern'; diff --git a/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx b/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx index 3126e9995..3aa8ab8e8 100644 --- a/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx +++ b/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx @@ -4,7 +4,7 @@ import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL } from '@documenso/li import type { Route } from './+types/share.$slug'; -// Todo: Test meta. +// Todo: (RR7) Test meta. export function meta({ params: { slug } }: Route.MetaArgs) { return [ { title: 'Documenso - Share' }, diff --git a/apps/remix/app/routes/api+/share.ts b/apps/remix/app/routes/api+/share.ts index fa768526e..4db264848 100644 --- a/apps/remix/app/routes/api+/share.ts +++ b/apps/remix/app/routes/api+/share.ts @@ -6,7 +6,7 @@ export type ShareHandlerAPIResponse = | Awaited> | { error: string }; -// Todo: Test +// Todo: (RR7) Test export async function loader({ request }: Route.LoaderArgs) { try { const url = new URL(request.url); diff --git a/apps/remix/app/routes/api+/stripe.webhook.ts b/apps/remix/app/routes/api+/stripe.webhook.ts index de42f7e6e..33343ae53 100644 --- a/apps/remix/app/routes/api+/stripe.webhook.ts +++ b/apps/remix/app/routes/api+/stripe.webhook.ts @@ -1,6 +1,6 @@ import { stripeWebhookHandler } from '@documenso/ee/server-only/stripe/webhook/handler'; -// Todo +// Todo: (RR7) // export const config = { // api: { bodyParser: false }, // }; diff --git a/apps/remix/app/routes/api+/webhook.trigger.ts b/apps/remix/app/routes/api+/webhook.trigger.ts index f5c3ceadd..c51f65331 100644 --- a/apps/remix/app/routes/api+/webhook.trigger.ts +++ b/apps/remix/app/routes/api+/webhook.trigger.ts @@ -2,7 +2,7 @@ import { handlerTriggerWebhooks } from '@documenso/lib/server-only/webhooks/trig import type { Route } from './+types/webhook.trigger'; -// Todo +// Todo: (RR7) // export const config = { // maxDuration: 300, // api: { diff --git a/apps/remix/app/routes/embed+/_layout.tsx b/apps/remix/app/routes/embed+/_layout.tsx index f415d3062..12a3d4868 100644 --- a/apps/remix/app/routes/embed+/_layout.tsx +++ b/apps/remix/app/routes/embed+/_layout.tsx @@ -6,7 +6,7 @@ import { EmbedPaywall } from '~/components/embed/embed-paywall'; import type { Route } from './+types/_layout'; -// Todo: Test +// Todo: (RR7) Test export function headers({ loaderHeaders }: Route.HeadersArgs) { const origin = loaderHeaders.get('Origin') ?? '*'; diff --git a/apps/remix/server/api/files.ts b/apps/remix/server/api/files.ts index 86a7c68d8..c8af41546 100644 --- a/apps/remix/server/api/files.ts +++ b/apps/remix/server/api/files.ts @@ -33,7 +33,7 @@ export const filesRoute = new Hono() return c.json({ error: 'No file provided' }, 400); } - // Todo: This is new. + // Todo: (RR7) This is new. // Add file size validation. // Convert MB to bytes (1 MB = 1024 * 1024 bytes) const MAX_FILE_SIZE = APP_DOCUMENT_UPLOAD_SIZE_LIMIT * 1024 * 1024; @@ -54,7 +54,7 @@ export const filesRoute = new Hono() throw new AppError('INVALID_DOCUMENT_FILE'); } - // Todo: Test this. + // Todo: (RR7) Test this. if (!file.name.endsWith('.pdf')) { Object.defineProperty(file, 'name', { writable: true, diff --git a/apps/remix/server/router.ts b/apps/remix/server/router.ts index 45b1f9c44..abb1e9fc8 100644 --- a/apps/remix/server/router.ts +++ b/apps/remix/server/router.ts @@ -37,13 +37,13 @@ app.route('/api/auth', auth); // Files route. app.route('/api/files', filesRoute); -// API servers. Todo: Configure max durations, etc? +// API servers. Todo: (RR7) Configure max durations, etc? app.route('/api/v1', tsRestHonoApp); app.use('/api/jobs/*', jobsClient.getApiHandler()); app.use('/api/trpc/*', reactRouterTrpcServer); // Unstable API server routes. Order matters for these two. app.get(`${API_V2_BETA_URL}/openapi.json`, (c) => c.json(openApiDocument)); -app.use(`${API_V2_BETA_URL}/*`, async (c) => openApiTrpcServerHandler(c)); // Todo: Add next()? +app.use(`${API_V2_BETA_URL}/*`, async (c) => openApiTrpcServerHandler(c)); // Todo: (RR7) Add next()? export default app; diff --git a/apps/remix/server/trpc/hono-trpc-open-api.ts b/apps/remix/server/trpc/hono-trpc-open-api.ts index 95cd4c1c8..bc5348ad6 100644 --- a/apps/remix/server/trpc/hono-trpc-open-api.ts +++ b/apps/remix/server/trpc/hono-trpc-open-api.ts @@ -11,8 +11,8 @@ export const openApiTrpcServerHandler = async (c: Context) => { return createOpenApiFetchHandler({ endpoint: API_V2_BETA_URL, router: appRouter, - // Todo: Test this, since it's not using the createContext params. - // Todo: Reduce calls since we fetch on most request? maybe + // Todo: (RR7) Test this, since it's not using the createContext params. + // Todo: (RR7) Reduce calls since we fetch on most request? maybe createContext: async () => createTrpcContext({ c, requestSource: 'apiV2' }), req: c.req.raw, onError: (opts) => handleTrpcRouterError(opts, 'apiV2'), diff --git a/packages/api/hono.ts b/packages/api/hono.ts index bc4de971a..bd7aedee8 100644 --- a/packages/api/hono.ts +++ b/packages/api/hono.ts @@ -18,8 +18,8 @@ tsRestHonoApp .get('/openapi.json', (c) => c.json(OpenAPIV1)) .get('/me', async (c) => testCredentialsHandler(c.req.raw)); -// Zapier. Todo: Check methods. Are these get/post/update requests? -// Todo: Is there really no validations? +// Zapier. Todo: (RR7) Check methods. Are these get/post/update requests? +// Todo: (RR7) Is there really no validations? tsRestHonoApp .all('/zapier/list-documents', async (c) => listDocumentsHandler(c.req.raw)) .all('/zapier/subscribe', async (c) => subscribeHandler(c.req.raw)) diff --git a/packages/api/v1/middleware/authenticated.ts b/packages/api/v1/middleware/authenticated.ts index a09e946df..ba7fb85a5 100644 --- a/packages/api/v1/middleware/authenticated.ts +++ b/packages/api/v1/middleware/authenticated.ts @@ -52,7 +52,7 @@ export const authenticatedMiddleware = < } const metadata: ApiRequestMetadata = { - requestMetadata: extractRequestMetadata(request), // Todo: Test + requestMetadata: extractRequestMetadata(request), // Todo: (RR7) Test source: 'apiV1', auth: 'api', auditUser: { diff --git a/packages/app-tests/e2e/public-profiles/public-profiles.spec.ts b/packages/app-tests/e2e/public-profiles/public-profiles.spec.ts new file mode 100644 index 000000000..016a0a199 --- /dev/null +++ b/packages/app-tests/e2e/public-profiles/public-profiles.spec.ts @@ -0,0 +1,144 @@ +import { expect, test } from '@playwright/test'; + +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; +import { seedTeam } from '@documenso/prisma/seed/teams'; +import { seedDirectTemplate } from '@documenso/prisma/seed/templates'; +import { seedUser } from '@documenso/prisma/seed/users'; + +import { apiSignin } from '../fixtures/authentication'; + +test.describe.configure({ mode: 'parallel' }); + +test('[PUBLIC_PROFILE]: create profile', async ({ page }) => { + const user = await seedUser(); + + // Create direct template. + const directTemplate = await seedDirectTemplate({ + userId: user.id, + }); + + await apiSignin({ + page, + email: user.email, + redirectPath: '/settings/public-profile', + }); + + const publicProfileUrl = Date.now().toString(); + const publicProfileBio = `public-profile-bio`; + + await page.getByRole('textbox', { name: 'Public profile URL' }).click(); + await page.getByRole('textbox', { name: 'Public profile URL' }).fill(publicProfileUrl); + + await page.getByRole('textbox', { name: 'Bio' }).click(); + await page.getByRole('textbox', { name: 'Bio' }).fill(publicProfileBio); + + await page.getByRole('button', { name: 'Update' }).click(); + + await expect(page.getByRole('status').first()).toContainText( + 'Your public profile has been updated.', + ); + + // Link direct template to public profile. + await page.getByRole('button', { name: 'Link template' }).click(); + await page.getByRole('cell', { name: directTemplate.title }).click(); + await page.getByRole('button', { name: 'Continue' }).click(); + + await page.getByRole('textbox', { name: 'Title *' }).fill('public-direct-template-title'); + await page + .getByRole('textbox', { name: 'Description *' }) + .fill('public-direct-template-description'); + await page.getByRole('button', { name: 'Update' }).click(); + + // Check that public profile is disabled. + await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`); + await expect(page.locator('body')).toContainText('404 Profile not found'); + + // Go back to public profile page. + await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/settings/public-profile`); + await page.getByRole('switch').click(); + + // Assert values. + await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`); + await expect(page.getByRole('main')).toContainText(publicProfileBio); + await expect(page.locator('body')).toContainText('public-direct-template-title'); + await expect(page.locator('body')).toContainText('public-direct-template-description'); + + await page.getByRole('link', { name: 'Sign' }).click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.getByRole('button', { name: 'Complete' }).click(); + await page.getByRole('button', { name: 'Sign' }).click(); + + await expect(page.getByRole('heading', { name: 'Document Signed' })).toBeVisible(); + await expect(page.getByRole('heading')).toContainText('Document Signed'); +}); + +test('[PUBLIC_PROFILE]: create team profile', async ({ page }) => { + const team = await seedTeam({ + createTeamMembers: 1, + }); + + const user = team.owner; + + // Create direct template. + const directTemplate = await seedDirectTemplate({ + userId: user.id, + teamId: team.id, + }); + + // Create non team template to make sure you can only see the team one. + // Will be indirectly asserted because test should fail when 2 elements appear. + await seedDirectTemplate({ + userId: user.id, + }); + + await apiSignin({ + page, + email: user.email, + redirectPath: `/t/${team.url}/settings/public-profile`, + }); + + const publicProfileUrl = team.url; + const publicProfileBio = `public-profile-bio`; + + await page.getByRole('textbox', { name: 'Bio' }).click(); + await page.getByRole('textbox', { name: 'Bio' }).fill(publicProfileBio); + + await page.getByRole('button', { name: 'Update' }).click(); + + await expect(page.getByRole('status').first()).toContainText( + 'Your public profile has been updated.', + ); + + // Link direct template to public profile. + await page.getByRole('button', { name: 'Link template' }).click(); + await page.getByRole('cell', { name: directTemplate.title }).click(); + await page.getByRole('button', { name: 'Continue' }).click(); + + await page.getByRole('textbox', { name: 'Title *' }).fill('public-direct-template-title'); + await page + .getByRole('textbox', { name: 'Description *' }) + .fill('public-direct-template-description'); + await page.getByRole('button', { name: 'Update' }).click(); + + // Check that public profile is disabled. + await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`); + await expect(page.locator('body')).toContainText('404 Profile not found'); + + // Go back to public profile page. + await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/settings/public-profile`); + await page.getByRole('switch').click(); + + // Assert values. + await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`); + await expect(page.getByRole('main')).toContainText(publicProfileBio); + await expect(page.locator('body')).toContainText('public-direct-template-title'); + await expect(page.locator('body')).toContainText('public-direct-template-description'); + + await page.getByRole('link', { name: 'Sign' }).click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.getByRole('button', { name: 'Complete' }).click(); + await page.getByRole('button', { name: 'Sign' }).click(); + + await expect(page.getByRole('heading', { name: 'Document Signed' })).toBeVisible(); + await expect(page.getByRole('heading')).toContainText('Document Signed'); +}); diff --git a/packages/auth/server/lib/session/session-cookies.ts b/packages/auth/server/lib/session/session-cookies.ts index 0905a0bb4..a68f67c99 100644 --- a/packages/auth/server/lib/session/session-cookies.ts +++ b/packages/auth/server/lib/session/session-cookies.ts @@ -30,10 +30,10 @@ const getAuthSecret = () => { export const sessionCookieOptions = { httpOnly: true, path: '/', - sameSite: useSecureCookies ? 'none' : 'lax', // Todo: This feels wrong? + sameSite: useSecureCookies ? 'none' : 'lax', // Todo: (RR7) This feels wrong? secure: useSecureCookies, domain: getCookieDomain(), - // Todo: Max age for specific auth cookies. + // Todo: (RR7) Max age for specific auth cookies. } as const; export const extractSessionCookieFromHeaders = (headers: Headers): string | null => { diff --git a/packages/auth/server/lib/utils/get-session.ts b/packages/auth/server/lib/utils/get-session.ts index 1d46f1819..66ff5ab99 100644 --- a/packages/auth/server/lib/utils/get-session.ts +++ b/packages/auth/server/lib/utils/get-session.ts @@ -38,7 +38,7 @@ export const getOptionalSession = async ( }; /** - * Todo: Rethink, this is pretty sketchy. + * Todo: (RR7) Rethink, this is pretty sketchy. */ const mapRequestToContextForCookie = (c: Context | Request) => { if (c instanceof Request) { diff --git a/packages/auth/server/lib/utils/handle-oauth-callback-url.ts b/packages/auth/server/lib/utils/handle-oauth-callback-url.ts index 30f1dd929..534b7e3a0 100644 --- a/packages/auth/server/lib/utils/handle-oauth-callback-url.ts +++ b/packages/auth/server/lib/utils/handle-oauth-callback-url.ts @@ -144,7 +144,7 @@ export const handleOAuthCallbackUrl = async (options: HandleOAuthCallbackUrlOpti }, data: { emailVerified: new Date(), - password: null, // Todo: Check this + password: null, // Todo: (RR7) Check this }, }); } @@ -182,7 +182,7 @@ export const handleOAuthCallbackUrl = async (options: HandleOAuthCallbackUrlOpti }); await onCreateUserHook(createdUser).catch((err) => { - // Todo: Add logging. + // Todo: (RR7) Add logging. console.error(err); }); diff --git a/packages/auth/server/routes/email-password.ts b/packages/auth/server/routes/email-password.ts index 058f78ed6..674cc8bbc 100644 --- a/packages/auth/server/routes/email-password.ts +++ b/packages/auth/server/routes/email-password.ts @@ -50,7 +50,7 @@ export const emailPasswordRoute = new Hono() const csrfCookieToken = await getCsrfCookie(c); - // Todo: Add logging here. + // Todo: (RR7) Add logging here. if (csrfToken !== csrfCookieToken || !csrfCookieToken) { throw new AppError(AuthenticationErrorCode.InvalidRequest, { message: 'Invalid CSRF token', diff --git a/packages/ee/server-only/stripe/webhook/handler.ts b/packages/ee/server-only/stripe/webhook/handler.ts index 4a1a84379..32f0c8516 100644 --- a/packages/ee/server-only/stripe/webhook/handler.ts +++ b/packages/ee/server-only/stripe/webhook/handler.ts @@ -51,7 +51,7 @@ export const stripeWebhookHandler = async (req: Request) => { ); } - // Todo: I'm not sure about this. + // Todo: (RR7) I'm not sure about this. const clonedReq = req.clone(); const rawBody = await clonedReq.arrayBuffer(); const body = Buffer.from(rawBody); diff --git a/packages/lib/client-only/providers/session.tsx b/packages/lib/client-only/providers/session.tsx index 0ad112321..b9ad7862c 100644 --- a/packages/lib/client-only/providers/session.tsx +++ b/packages/lib/client-only/providers/session.tsx @@ -40,7 +40,7 @@ export const useSession = () => { return { ...context.sessionData, - refresh: context.refresh, + refreshSession: context.refresh, }; }; @@ -68,7 +68,7 @@ export const SessionProvider = ({ children, initialSession }: SessionProviderPro } const teams = await trpc.team.getTeams.query().catch(() => { - // Todo: Log + // Todo: (RR7) Log return []; }); diff --git a/packages/lib/jobs/client/inngest.ts b/packages/lib/jobs/client/inngest.ts index ce91cb56a..0da2ed374 100644 --- a/packages/lib/jobs/client/inngest.ts +++ b/packages/lib/jobs/client/inngest.ts @@ -92,7 +92,7 @@ export class InngestJobProvider extends BaseJobProvider { // }; // } - // Todo: Do we need to handle the above? + // Todo: (RR7) Do we need to handle the above? public getApiHandler() { return async (context: HonoContext) => { const handler = createHonoPagesRoute({ diff --git a/packages/lib/server-only/user/create-user.ts b/packages/lib/server-only/user/create-user.ts index fd20be7a8..1b8a15a60 100644 --- a/packages/lib/server-only/user/create-user.ts +++ b/packages/lib/server-only/user/create-user.ts @@ -52,18 +52,18 @@ export const createUser = async ({ name, email, password, signature, url }: Crea data: { name, email: email.toLowerCase(), - password: hashedPassword, // Todo: Drop password. + password: hashedPassword, // Todo: (RR7) Drop password. signature, url, }, }); - // Todo: Migrate to use this after RR7. + // Todo: (RR7) Migrate to use this after RR7. // await tx.account.create({ // data: { // userId: user.id, - // type: 'emailPassword', // Todo - // provider: 'DOCUMENSO', // Todo: Enums + // type: 'emailPassword', // Todo: (RR7) + // provider: 'DOCUMENSO', // Todo: (RR7) Enums // providerAccountId: user.id.toString(), // password: hashedPassword, // }, @@ -73,7 +73,7 @@ export const createUser = async ({ name, email, password, signature, url }: Crea }); await onCreateUserHook(user).catch((err) => { - // Todo: Add logging. + // Todo: (RR7) Add logging. console.error(err); }); diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 4c012346a..ce7e4ed8f 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -23,7 +23,7 @@ datasource db { directUrl = env("NEXT_PRIVATE_DIRECT_DATABASE_URL") } -// Todo: Remove after RR7 migration. +// Todo: (RR7) Remove after RR7 migration. enum IdentityProvider { DOCUMENSO GOOGLE @@ -41,14 +41,14 @@ model User { customerId String? @unique email String @unique emailVerified DateTime? - password String? // Todo: Remove after RR7 migration. + password String? // Todo: (RR7) Remove after RR7 migration. source String? signature String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt lastSignedIn DateTime @default(now()) roles Role[] @default([USER]) - identityProvider IdentityProvider @default(DOCUMENSO) // Todo: Remove after RR7 migration. + identityProvider IdentityProvider @default(DOCUMENSO) // Todo: (RR7) Remove after RR7 migration. avatarImageId String? disabled Boolean @default(false) diff --git a/packages/ui/primitives/lazy-pdf-viewer.tsx b/packages/ui/primitives/lazy-pdf-viewer.tsx index 6aff4191a..74ee72110 100644 --- a/packages/ui/primitives/lazy-pdf-viewer.tsx +++ b/packages/ui/primitives/lazy-pdf-viewer.tsx @@ -1,4 +1,4 @@ -// Todo: Not sure if this actually makes it client-only. +// Todo: (RR7) Not sure if this actually makes it client-only. import { Suspense, lazy } from 'react'; import { Trans } from '@lingui/react/macro';