Skip to content

Lewis/user portal - #65

Merged
carhartlewis merged 4 commits into
mainfrom
lewis/user-portal
Feb 18, 2025
Merged

carhartlewis merged 4 commits into
mainfrom
lewis/user-portal

Conversation

@carhartlewis

@carhartlewis carhartlewis commented Feb 18, 2025 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Launched a refreshed portal experience with an updated, responsive layout including a dedicated OTP-based login page, personalized home view, and an intuitive 404 error screen.
    • Enhanced authentication with a streamlined email verification process using a new OTP email template.
    • Added multi-language support for localized user experiences.
    • Introduced a user session management system with improved session handling.
  • Integrations

    • Streamlined connection management by simplifying external integration functionality.

@vercel

vercel Bot commented Feb 18, 2025 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 18, 2025 10:30pm
web ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 18, 2025 10:30pm

@coderabbitai

coderabbitai Bot commented Feb 18, 2025 •

Copy link
Copy Markdown

Walkthrough

The changes remove Nango integration code from existing actions and components while introducing a new portal setup. New configuration and documentation files for a Next.js application have been added, including environment settings, Tailwind, and TypeScript configurations. A set of new pages, components, and API routes establish an OTP-based authentication flow with internationalization support and error handling. Additionally, the database schema has been updated with new models and relationships for a user portal, and an OTP email template component has been added.

Changes

File(s) Change Summary
apps/app/src/actions/integrations/delete-integration-connection.ts
apps/app/src/actions/integrations/retrieve-integration-session-token.ts
apps/app/src/components/integrations/integrations-card.tsx
Removed Nango library usage, eliminating its instantiation and dependency from integration connection and session token actions.
apps/portal/.gitignore
apps/portal/README.md
apps/portal/next.config.ts
apps/portal/package.json
apps/portal/postcss.config.mjs
apps/portal/public/site.webmanifest
Introduced new configuration and documentation files for the portal application, establishing build, dependency, and deployment settings.
apps/portal/src/app/[locale]/{(app)/page.tsx, (public)/auth/page.tsx, (public)/layout.tsx, layout.tsx, not-found.tsx}
apps/portal/src/app/{actions/login.ts, api/auth/[...all]/route.ts}
apps/portal/src/app/{components/otp-form.tsx, components/otp-input.tsx, components/otp.tsx}
apps/portal/src/app/{global-error.tsx, lib/auth-client.ts, lib/auth.ts, lib/resend.ts}
apps/portal/src/app/{locales/client.ts, locales/en.ts, locales/es.ts, locales/fr.ts, locales/no.ts, locales/pt.ts, locales/server.ts}
apps/portal/src/env.mjs
apps/portal/src/middleware.ts
apps/portal/tailwind.config.ts
apps/portal/tsconfig.json
Added multiple pages, layouts, and components to support OTP-based authentication, internationalization, theming, error handling, and session management. This includes server-side actions, API routes, i18n configurations, and providers for integrating analytics and localization into the portal.
packages/db/prisma/migrations/.../migration.sql
packages/db/prisma/schema.prisma
Updated the database schema by adding a linkId field to the Employee table and introducing new models: PortalUser, PortalSession, PortalAccount, and PortalVerification with corresponding foreign key constraints and indexes.
packages/email/emails/otp.tsx Introduced the OTPVerificationEmail component to generate an email template for OTP verification using React Email components and Tailwind CSS styling.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant B as Browser
    participant P as Portal App
    participant A as Auth API
    participant E as Email Service
    participant DB as Database

    U->>B: Navigate to login page
    B->>P: Request login page
    P-->>B: Render OTP sign-in form
    U->>B: Enter email and request OTP
    B->>P: Submit login request with email
    P->>A: Trigger OTP generation
    A->>DB: Create/validate OTP record
    A->>E: Send OTP email (OTPVerificationEmail)
    E-->>U: Deliver OTP email
    U->>B: Submit OTP for verification
    B->>P: OTP verification request
    P->>A: Validate OTP and sign in
    A->>DB: Verify OTP & create session
    DB-->>A: Return session info
    A-->>P: Return session result
    P-->>B: Render authenticated state
Loading

Poem

Hop along, I’ve got news today,
Codes have danced in a clever way.
Nango’s gone, the path is clear,
OTP and portals now appear.
I’m a rabbit, joyful and free—
With new changes leaping with glee!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce3492 and 453a69d.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • apps/app/package.json (1 hunks)
🔇 Additional comments (1)
apps/app/package.json (1)

14-14: New Dependency Addition: "@ai-sdk/provider"

The new dependency "@ai-sdk/provider": "^1.0.7" has been correctly added to the dependencies section. This aligns with the broader removal of the Nango integration as described in the PR objectives and AI summary. Ensure that any functionality previously relying on Nango is now correctly switched over to use the new provider.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🧹 Nitpick comments (16)
apps/portal/src/app/lib/resend.ts (1)

1-4: Validate RESEND_API_KEY on initialization.
To avoid runtime failures, consider validating that env.RESEND_API_KEY is defined and non-empty. This provides clearer error handling if the environment variable is missing or misconfigured.

-export const resend = new Resend(env.RESEND_API_KEY);
+if (!env.RESEND_API_KEY) {
+  throw new Error("RESEND_API_KEY is not defined in environment variables.");
+}
+
+export const resend = new Resend(env.RESEND_API_KEY);
apps/portal/src/app/[locale]/(public)/layout.tsx (1)

1-7: Remove the async keyword if not needed.
Currently, there is no await usage or asynchronous logic. If future code changes require async data fetching, you can re-introduce it. Otherwise, removing it now may reduce confusion.

-export default async function Layout({
+export default function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  return <>{children}</>;
}
apps/portal/src/app/locales/en.ts (1)

1-19: Consider adding translations for common error states.

The translations are well-organized, but consider adding messages for common error scenarios:

  • Invalid email format
  • Rate limiting messages
  • Network error states
  • Generic error state

Example additions:

 export default {
   auth: {
     title: "Employee Portal",
     description: "Enter your email and one time password to continue",
     options: "More options",
     email: {
       otp_sent: "One time password sent",
       otp_description: "Check your email for the one time password",
       otp_try_again: "Try again",
       placeholder: "Your work email",
       button: "Get one time password",
+      errors: {
+        invalid_email: "Please enter a valid email address",
+        rate_limit: "Too many attempts. Please try again later",
+        network_error: "Network error. Please check your connection",
+        generic: "Something went wrong. Please try again"
+      }
     },
   },
   // ...
 } as const;
apps/portal/src/env.mjs (1)

6-8: Enhance validation for sensitive environment variables.

Consider adding stricter validation for sensitive variables:

 server: {
-  BETTER_AUTH_SECRET: z.string(),
+  BETTER_AUTH_SECRET: z.string().min(32),
-  BETTER_AUTH_URL: z.string(),
+  BETTER_AUTH_URL: z.string().url(),
-  RESEND_API_KEY: z.string(),
+  RESEND_API_KEY: z.string().regex(/^re_/),
 },
apps/portal/src/app/components/otp-input.tsx (1)

26-26: Consider a more maintainable styling approach.

The use of ! in the className to force styles might lead to specificity issues. Consider using Tailwind's built-in modifiers or a more maintainable styling approach.

-          className={cn("!w-12 !appearance-none selection:bg-none ", className)}
+          className={cn("[&]:w-12 [&]:appearance-none selection:bg-none", className)}
apps/portal/src/app/[locale]/(app)/page.tsx (1)

37-55: Optimize database query with selective fields.

Consider optimizing the database query by selecting only the required fields from the employee table.

 export const getUser = unstable_cache(async (email: string) => {
   "use server";
 
   const employee_data = await db.employee.findFirst({
     where: {
       email,
     },
+    select: {
+      id: true,
+      email: true,
+      organization: {
+        select: {
+          id: true,
+          name: true,
+        },
+      },
+    },
-    include: {
-      organization: {
-        select: {
-          id: true,
-          name: true,
-        },
-      },
-    },
   });
 
   return employee_data;
 });
apps/portal/src/app/[locale]/(public)/auth/page.tsx (1)

6-8: Enhance SEO with additional metadata.

Consider adding more metadata fields for better SEO:

 export const metadata: Metadata = {
   title: "Login | Comp AI",
+  description: "Secure login page for Comp AI using one-time password authentication",
+  robots: "noindex, nofollow",
 };
packages/email/emails/otp.tsx (2)

62-64: Remove duplicate text in email body.

The text "Your one-time password for Comp AI" appears twice in the email.

-            <Text className="text-[14px] leading-[24px] text-[#121212]">
-              Your one-time password for Comp AI
-            </Text>

27-47: Consider self-hosting fonts for reliability.

Loading fonts from CDN could cause rendering issues if the CDN is down. Consider self-hosting the fonts or providing multiple fallback options.

apps/portal/src/app/components/otp-form.tsx (2)

36-37: Simplify loading state management.

The component manages loading state twice with both isLoading and isExecuting. Consider using only isExecuting from useAction.

-  const [isLoading, setIsLoading] = useState(false);
   const router = useRouter();
   const { execute, isExecuting } = useAction(login, {
     onSuccess: () => {
       toast.success("OTP verified");
       router.push("/");
     },
     onError: (error) => {
       toast.error(error.error.serverError as string);
     },
   });

Update the button to use isExecuting:

-          disabled={isLoading}
+          disabled={isExecuting}

Also applies to: 46-54


56-69: Simplify error handling in onSubmit.

The try-catch block with loading state management can be removed since error handling is already managed by useAction.

   const onSubmit = async (formData: OtpFormValues) => {
-    try {
-      setIsLoading(true);
-
-      await execute({
-        otp: formData.otp,
-        email: formData.email,
-      });
-    } catch (error) {
-      toast.error("An unexpected error occurred");
-    } finally {
-      setIsLoading(false);
-    }
+    await execute({
+      otp: formData.otp,
+      email: formData.email,
+    });
   };
apps/portal/src/app/components/otp.tsx (2)

29-29: Rename state variable for clarity.

The _email variable name with underscore prefix is unconventional. Consider a more descriptive name.

-  const [_email, setEmail] = useState<string>();
+  const [verificationEmail, setVerificationEmail] = useState<string>();

42-54: Improve error handling and state updates.

The success case doesn't explicitly check for data, and state updates could be more consistent.

     const { data, error } = await authClient.emailOtp.sendVerificationOtp({
       email: email,
       type: "sign-in",
     });

+    setLoading(false);
+
     if (error) {
-      setLoading(false);
       toast.error(error.message);
+      return;
     }

+    if (!data) {
+      toast.error("Failed to send verification code");
+      return;
+    }
+
     setSent(true);
-    setLoading(false);
apps/portal/tsconfig.json (1)

3-3: Consider updating the TypeScript target.

The target is set to ES2017 which might be too conservative. Consider updating to a more recent target like ES2020 or ES2021 to enable modern JavaScript features while maintaining broad browser compatibility.

packages/db/prisma/schema.prisma (2)

978-994: Implementation of PortalUser Model

The new PortalUser model defines essential fields for managing portal users along with relationships to sessions, accounts, and employees. Consider whether adding default values (such as @default(now())) for createdAt and updatedAt might improve consistency with other models in your schema.


1010-1028: Review of PortalAccount Model

The PortalAccount model effectively captures external account token information, along with expiration and credential details. You might want to review whether adding an index or even a uniqueness constraint on fields like accountId could be beneficial for query performance, depending on how these are used in your application.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bcfe13 and 7ce3492.

⛔ Files ignored due to path filters (10)
  • apps/portal/public/apple-touch-icon.png is excluded by !**/*.png
  • apps/portal/public/favicon-96x96.png is excluded by !**/*.png
  • apps/portal/public/favicon.ico is excluded by !**/*.ico
  • apps/portal/public/favicon.svg is excluded by !**/*.svg
  • apps/portal/public/fonts/GeneralSans-Variable.ttf is excluded by !**/*.ttf
  • apps/portal/public/fonts/GeneralSans-VariableItalic.ttf is excluded by !**/*.ttf
  • apps/portal/public/web-app-manifest-192x192.png is excluded by !**/*.png
  • apps/portal/public/web-app-manifest-512x512.png is excluded by !**/*.png
  • bun.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (38)
  • apps/app/src/actions/integrations/delete-integration-connection.ts (0 hunks)
  • apps/app/src/actions/integrations/retrieve-integration-session-token.ts (1 hunks)
  • apps/app/src/components/integrations/integrations-card.tsx (0 hunks)
  • apps/portal/.gitignore (1 hunks)
  • apps/portal/README.md (1 hunks)
  • apps/portal/next.config.ts (1 hunks)
  • apps/portal/package.json (1 hunks)
  • apps/portal/postcss.config.mjs (1 hunks)
  • apps/portal/public/site.webmanifest (1 hunks)
  • apps/portal/src/app/[locale]/(app)/page.tsx (1 hunks)
  • apps/portal/src/app/[locale]/(public)/auth/page.tsx (1 hunks)
  • apps/portal/src/app/[locale]/(public)/layout.tsx (1 hunks)
  • apps/portal/src/app/[locale]/layout.tsx (1 hunks)
  • apps/portal/src/app/[locale]/not-found.tsx (1 hunks)
  • apps/portal/src/app/[locale]/providers.tsx (1 hunks)
  • apps/portal/src/app/actions/login.ts (1 hunks)
  • apps/portal/src/app/api/auth/[...all]/route.ts (1 hunks)
  • apps/portal/src/app/components/otp-form.tsx (1 hunks)
  • apps/portal/src/app/components/otp-input.tsx (1 hunks)
  • apps/portal/src/app/components/otp.tsx (1 hunks)
  • apps/portal/src/app/global-error.tsx (1 hunks)
  • apps/portal/src/app/lib/auth-client.ts (1 hunks)
  • apps/portal/src/app/lib/auth.ts (1 hunks)
  • apps/portal/src/app/lib/resend.ts (1 hunks)
  • apps/portal/src/app/locales/client.ts (1 hunks)
  • apps/portal/src/app/locales/en.ts (1 hunks)
  • apps/portal/src/app/locales/es.ts (1 hunks)
  • apps/portal/src/app/locales/fr.ts (1 hunks)
  • apps/portal/src/app/locales/no.ts (1 hunks)
  • apps/portal/src/app/locales/pt.ts (1 hunks)
  • apps/portal/src/app/locales/server.ts (1 hunks)
  • apps/portal/src/env.mjs (1 hunks)
  • apps/portal/src/middleware.ts (1 hunks)
  • apps/portal/tailwind.config.ts (1 hunks)
  • apps/portal/tsconfig.json (1 hunks)
  • packages/db/prisma/migrations/20250218153849_employee_portal/migration.sql (1 hunks)
  • packages/db/prisma/schema.prisma (2 hunks)
  • packages/email/emails/otp.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/app/src/components/integrations/integrations-card.tsx
  • apps/app/src/actions/integrations/delete-integration-connection.ts
✅ Files skipped from review due to trivial changes (7)
  • apps/portal/src/app/locales/no.ts
  • apps/portal/src/app/locales/es.ts
  • apps/portal/src/app/locales/pt.ts
  • apps/portal/src/app/locales/fr.ts
  • apps/portal/README.md
  • apps/portal/public/site.webmanifest
  • apps/portal/.gitignore
🔇 Additional comments (19)
apps/app/src/actions/integrations/retrieve-integration-session-token.ts (1)

18-19:

Details

❓ Verification inconclusive

Verify the unused integrationId parameter.

The action accepts integrationId but doesn't use it in the token generation. This suggests the integration flow might be incomplete.

Let's verify the integration flow implementation:


🏁 Script executed:

#!/bin/bash
# Description: Check how integrationId is used across the codebase

# Search for references to integrationId
rg "integrationId" -A 5

# Search for other integration-related files
fd "integration" --type f

Length of output: 7512


Action Required: Confirm Usage of integrationId in Token Generation

The retrieve-integration-session-token.ts action currently destructures the integrationId from its input but does not utilize it when generating the session token (which is currently hardcoded as "123"). In contrast, other integration-related actions (e.g., in delete integration connection and integration settings) actively use this parameter. Please verify whether:

  • This omission is intentional—for example, if the token generation is a stub or placeholder implementation.
  • The integration flow is incomplete and whether the token generation should incorporate the integrationId.

If the parameter is not required, consider removing it or adding an explanatory comment. Otherwise, update the token generation flow accordingly.

apps/portal/src/app/lib/auth.ts (2)

1-9: Imports are clear and concise.
All imports from @bubba/db, @bubba/email, and better-auth are straightforward, and file scoping appears correct.


10-25: Configuration mappings look correct.
The specified models (PortalUser, PortalSession, PortalAccount, PortalVerification) properly align with the authentication structure. Verify that these model names match the actual Prisma schema definitions to avoid runtime issues.

apps/portal/src/app/api/auth/[...all]/route.ts (1)

1-4: LGTM! Clean and efficient implementation.

The implementation follows Next.js best practices by using a catch-all route handler and leveraging the better-auth package's toNextJsHandler utility to handle authentication requests.

apps/portal/postcss.config.mjs (1)

1-8: LGTM! Clean PostCSS configuration.

The configuration is properly typed and includes the necessary Tailwind CSS plugin.

apps/portal/src/app/lib/auth-client.ts (1)

5-10:

Details

❓ Verification inconclusive

Review security implications of client-side configuration.

Please consider the following security aspects:

  1. The base URL is exposed to the client side through NEXT_PUBLIC_ prefix. Verify if this exposure is necessary and intended.
  2. Consider adding CSRF protection and other security configurations supported by the better-auth package.

🌐 Web query:

What security configurations does better-auth support for client-side authentication?

💡 Result:

Better Auth provides several robust security configurations for client-side authentication, focusing on protecting user sessions, preventing attacks, and ensuring secure integration with various authentication methods. Here are the key security features and their configurations:

Session Management

  • Secure Cookies: Automatically enabled when using HTTPS, cookies are encrypted with SameSite=Lax and HttpOnly attributes to prevent cross-site request forgery (CSRF) and client-side script access[1][7].
  • Session Expiration: Default session duration is 7 days, with auto-renewal if used within the updateAge threshold (default: 1 day)[1].
  • Revocation: Sessions can be manually revoked server-side or by users to log out from specific devices[1].

CSRF and Open Redirect Protection

  • Origin Validation: Requests are blocked if the Origin or Referer headers don’t match the trustedOrigins list[7][10].
  • State Parameters: OAuth flows use stored state values to prevent CSRF during redirects[1][7].

Authentication Methods

  • OAuth with PKCE: Secures social logins (Google, GitHub, etc.) by generating a code verifier/challenge pair to prevent code interception[1][4].
  • Password Hashing: Uses scrypt (memory-hard and CPU-intensive) by default, with customizable hashing/verification functions[1].

Rate Limiting

  • Built-in protection against brute-force attacks across all routes, with stricter limits on high-risk endpoints[1][4].

Advanced Configurations

  • Trusted Origins: Define allowed domains to block unauthorized redirects[1][7].
  • Cross-Subdomain Cookies: Enable via crossSubDomain option for shared authentication across subdomains[1].
  • Custom Cookies: Rename cookies to reduce fingerprinting risks[1].

Extensions via Plugins

  • Multi-Factor Authentication (MFA): Add layers like TOTP or biometrics using plugins[2][5].
  • Certificate-Based Authentication: While not native, plugins could integrate mutual TLS (mTLS) for client certificates[9].

For client-side implementation, developers must invoke methods like signIn.email or signIn.social from the frontend while relying on server-side session validation[2][4]. Secure cookies ensure credentials remain protected during transit and storage[1][7].

Citations:


Action: Validate Client-Side Auth Security Settings

  • The file apps/portal/src/app/lib/auth-client.ts currently exposes the base URL via the NEXT_PUBLIC_BETTER_AUTH_URL environment variable. Confirm that this exposure is intentional and appropriate for your authentication flow.
  • While Better Auth offers built-in security features—such as secure cookies with SameSite, HttpOnly attributes, and CSRF protection via origin validation—please verify that these configurations are enabled and correctly applied on the server side.
  • If these protections are not already activated in your overall setup, consider integrating explicit configurations for secure cookies and CSRF defense as part of your authentication strategy.
apps/portal/tailwind.config.ts (1)

1-11: LGTM!

The Tailwind configuration is well-structured with:

  • Proper extension of the base configuration
  • Correct content paths for local and shared packages
  • Type-safe configuration using the satisfies operator
apps/portal/src/app/[locale]/not-found.tsx (1)

1-16: LGTM! Well-structured 404 page implementation.

The component follows Next.js best practices with proper async handling for i18n, clear error messaging, and a user-friendly return link. The layout is clean and responsive.

apps/portal/next.config.ts (1)

12-27: LGTM! Well-configured PostHog rewrites.

The rewrite rules are correctly configured for PostHog integration, providing proper endpoint mappings.

apps/portal/src/env.mjs (1)

11-15: LGTM! Well-structured client environment variables.

The client environment variables are correctly separated and typed, with appropriate optional flags for PostHog configuration.

apps/portal/src/app/[locale]/providers.tsx (1)

30-46: LGTM! Well-structured provider configuration.

The provider configuration is well-organized, with proper theme settings and conditional analytics rendering.

apps/portal/src/middleware.ts (2)

6-10: LGTM! Comprehensive i18n configuration.

The internationalization setup supports multiple locales and follows best practices.


25-37: LGTM! Secure path handling and redirects.

The path handling implementation correctly manages locales and implements secure redirects.

apps/portal/src/app/[locale]/(public)/auth/page.tsx (1)

21-43: LGTM! Well-structured responsive layout.

The layout implementation is clean and responsive, with proper spacing and alignment for different screen sizes.

apps/portal/src/app/components/otp.tsx (1)

74-82: LGTM! Well-configured input field.

The input field is properly configured with appropriate attributes for better user experience.

packages/db/prisma/migrations/20250218153849_employee_portal/migration.sql (1)

7-9: 🛠️ Refactor suggestion

Add NOT NULL constraints for essential fields.

The name and emailVerified fields in portal_user should be nullable as they might not be available immediately upon user creation.

Update the field constraints:

-    "name" TEXT NOT NULL,
+    "name" TEXT,
     "email" TEXT NOT NULL,
-    "emailVerified" BOOLEAN NOT NULL,
+    "emailVerified" BOOLEAN NOT NULL DEFAULT false,

Likely an incorrect or invalid review comment.

packages/db/prisma/schema.prisma (3)

830-832: New Employee Portal Linking Fields in Employee Model

The addition of linkId and the associated relation link in the Employee model correctly establishes an optional link to a PortalUser. Please verify that using onDelete: SetNull meets your business requirements for handling deletions of portal users.


996-1009: PortalSession Model for User Sessions

The PortalSession model is well-structured for tracking session details and associating sessions with portal users. As with the PortalUser model, consider defaulting the timestamp fields (createdAt and updatedAt) if it aligns with your design preferences.


1030-1039: PortalVerification Model for OTP Process

The PortalVerification model provides a simple yet sufficient structure for managing verification tokens. Confirm that handling of datetime values (especially for expiresAt) in the application logic accommodates timezone differences and validity checks consistently.

return {
success: true,
sessionToken: response.data.token,
sessionToken: "123",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Replace hardcoded session token with secure token generation.

The hardcoded token "123" is a significant security vulnerability that could lead to unauthorized access. This appears to be a temporary placeholder after removing the Nango integration.

Please implement proper session token generation with:

  • Cryptographically secure random values
  • Limited token lifetime
  • Proper authentication and authorization checks
  • Token validation against the specific integration and user

Comment on lines +26 to +40
plugins: [
nextCookies(),
emailOTP({
otpLength: 6,
expiresIn: 10 * 60,
async sendVerificationOTP({ email, otp }) {
await sendEmail({
to: email,
subject: "One-Time Password for Comp AI",
react: OTPVerificationEmail({ email, otp }),
});
}
})
]
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling around email sending.
The sendVerificationOTP function awaits sendEmail but does not handle potential errors. Consider wrapping this call in a try/catch block to log or handle failures gracefully.

     async sendVerificationOTP({ email, otp }) {
-      await sendEmail({
-        to: email,
-        subject: "One-Time Password for Comp AI",
-        react: OTPVerificationEmail({ email, otp }),
-      });
+      try {
+        await sendEmail({
+          to: email,
+          subject: "One-Time Password for Comp AI",
+          react: OTPVerificationEmail({ email, otp }),
+        });
+      } catch (error) {
+        // Log or handle the error accordingly
+        console.error("Failed to send verification email:", error);
+      }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
plugins: [
nextCookies(),
emailOTP({
otpLength: 6,
expiresIn: 10 * 60,
async sendVerificationOTP({ email, otp }) {
await sendEmail({
to: email,
subject: "One-Time Password for Comp AI",
react: OTPVerificationEmail({ email, otp }),
});
}
})
]
});
plugins: [
nextCookies(),
emailOTP({
otpLength: 6,
expiresIn: 10 * 60,
async sendVerificationOTP({ email, otp }) {
try {
await sendEmail({
to: email,
subject: "One-Time Password for Comp AI",
react: OTPVerificationEmail({ email, otp }),
});
} catch (error) {
// Log or handle the error accordingly
console.error("Failed to send verification email:", error);
}
}
})
]
});

Comment on lines +5 to +13
export default function GlobalError() {
return (
<html lang="en">
<body>
<NextError statusCode={0} />
</body>
</html>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance error handling implementation.

The current implementation has several areas for improvement:

  1. Status code 0 is not a standard HTTP status code
  2. The component doesn't utilize error details for debugging
  3. Missing error props that Next.js provides to error components

Consider this implementation:

-export default function GlobalError() {
+export default function GlobalError({
+  error,
+  reset,
+}: {
+  error: Error & { digest?: string }
+  reset: () => void
+}) {
+  console.error(error)
   return (
     <html lang="en">
       <body>
-        <NextError statusCode={0} />
+        <NextError statusCode={500} />
+        <button onClick={reset}>Try again</button>
       </body>
     </html>
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function GlobalError() {
return (
<html lang="en">
<body>
<NextError statusCode={0} />
</body>
</html>
);
}
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
console.error(error)
return (
<html lang="en">
<body>
<NextError statusCode={500} />
<button onClick={reset}>Try again</button>
</body>
</html>
);
}

en: () => import("./en"),
es: () => import("./es"),
fr: () => import("./fr"),
no: () => import("./no"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update Norwegian language code to follow ISO standards.

The language code 'no' for Norwegian is deprecated. Use 'nb' for Bokmål or 'nn' for Nynorsk according to ISO 639-1 standards.

Comment on lines +12 to +23
.action(async ({ parsedInput }) => {
await auth.api.signInEmailOTP({
body: {
email: parsedInput.email,
otp: parsedInput.otp,
},
});

return {
success: true,
};
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance error handling in the login action.

The current implementation doesn't handle potential errors from the signInEmailOTP call. Consider adding proper error handling to provide meaningful feedback to users.

Apply this diff to improve error handling:

  .action(async ({ parsedInput }) => {
-   await auth.api.signInEmailOTP({
-     body: {
-       email: parsedInput.email,
-       otp: parsedInput.otp,
-     },
-   });
-
-   return {
-     success: true,
-   };
+   try {
+     await auth.api.signInEmailOTP({
+       body: {
+         email: parsedInput.email,
+         otp: parsedInput.otp,
+       },
+     });
+     return { success: true };
+   } catch (error) {
+     return {
+       success: false,
+       error: error instanceof Error ? error.message : 'Authentication failed',
+     };
+   }
  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.action(async ({ parsedInput }) => {
await auth.api.signInEmailOTP({
body: {
email: parsedInput.email,
otp: parsedInput.otp,
},
});
return {
success: true,
};
});
.action(async ({ parsedInput }) => {
try {
await auth.api.signInEmailOTP({
body: {
email: parsedInput.email,
otp: parsedInput.otp,
},
});
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Authentication failed',
};
}
});

Comment thread apps/portal/package.json
Comment on lines +13 to +16
"next": "15.1.7",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-otp-input": "^3.1.1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Verify package versions.

Several package versions appear to be incorrect or not yet released:

  • next: "15.1.7" - Latest stable version is 14.x
  • react: "^19.0.0" - React 19 is not yet released
  • react-dom: "^19.0.0" - Should match React version

Update the versions to currently available releases:

-    "next": "15.1.7",
-    "react": "^19.0.0",
-    "react-dom": "^19.0.0",
+    "next": "^14.1.0",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next": "15.1.7",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-otp-input": "^3.1.1"
"next": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-otp-input": "^3.1.1"

Comment on lines +54 to +63
export const viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 1,
userScalable: false,
themeColor: [
{ media: "(prefers-color-scheme: light)" },
{ media: "(prefers-color-scheme: dark)" },
],
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve accessibility by allowing user scaling.

The current viewport settings prevent users from zooming, which is an accessibility concern. Users should be able to zoom for better readability.

Update the viewport settings:

 export const viewport = {
   width: "device-width",
   initialScale: 1,
-  maximumScale: 1,
-  userScalable: false,
+  maximumScale: 5,
+  userScalable: true,
   themeColor: [
     { media: "(prefers-color-scheme: light)" },
     { media: "(prefers-color-scheme: dark)" },
   ],
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 1,
userScalable: false,
themeColor: [
{ media: "(prefers-color-scheme: light)" },
{ media: "(prefers-color-scheme: dark)" },
],
};
export const viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 5,
userScalable: true,
themeColor: [
{ media: "(prefers-color-scheme: light)" },
{ media: "(prefers-color-scheme: dark)" },
],
};

Comment on lines +65 to +69
const font = localFont({
src: "/../../../public/fonts/GeneralSans-Variable.ttf",
display: "swap",
variable: "--font-general-sans",
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix font path resolution.

The font path seems incorrect. In Next.js, public files should be referenced from the root.

Update the font path:

 const font = localFont({
-  src: "/../../../public/fonts/GeneralSans-Variable.ttf",
+  src: "../../../public/fonts/GeneralSans-Variable.ttf",
   display: "swap",
   variable: "--font-general-sans",
 });

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +68 to +75
-- AddForeignKey
ALTER TABLE "Employee" ADD CONSTRAINT "Employee_linkId_fkey" FOREIGN KEY ("linkId") REFERENCES "portal_user"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "portal_session" ADD CONSTRAINT "portal_session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "portal_user"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "portal_account" ADD CONSTRAINT "portal_account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "portal_user"("id") ON DELETE CASCADE ON UPDATE CASCADE;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add indexes for foreign key columns.

Foreign key columns should be indexed for better query performance.

Add the following indexes:

-- Add indexes for foreign key columns
CREATE INDEX "Employee_linkId_idx" ON "Employee"("linkId");
CREATE INDEX "portal_session_userId_idx" ON "portal_session"("userId");
CREATE INDEX "portal_account_userId_idx" ON "portal_account"("userId");

Comment on lines +56 to +57
"createdAt" TIMESTAMP(3),
"updatedAt" TIMESTAMP(3),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add NOT NULL constraints for timestamp fields.

The createdAt and updatedAt fields in portal_verification should be NOT NULL to maintain consistency with other tables.

Update the field constraints:

     "expiresAt" TIMESTAMP(3) NOT NULL,
-    "createdAt" TIMESTAMP(3),
-    "updatedAt" TIMESTAMP(3),
+    "createdAt" TIMESTAMP(3) NOT NULL,
+    "updatedAt" TIMESTAMP(3) NOT NULL,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"createdAt" TIMESTAMP(3),
"updatedAt" TIMESTAMP(3),
"expiresAt" TIMESTAMP(3) NOT NULL,
- "createdAt" TIMESTAMP(3),
- "updatedAt" TIMESTAMP(3),
+ "createdAt" TIMESTAMP(3) NOT NULL,
+ "updatedAt" TIMESTAMP(3) NOT NULL,

This branch was successfully deployed

2 active deployments
Preview – app — 453a69db Deployed Feb 18, 2025 by vercel[bot]
Preview – web — 453a69db Deployed Feb 18, 2025 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant