Skip to content

refactor(portal-plugin-dashboard, portal-framework-auth): add OTP form component and verification flow#455

Merged
pcfreak30 merged 1 commit intodevelopfrom
libs/portal-plugin-dashboard
Aug 19, 2025
Merged

refactor(portal-plugin-dashboard, portal-framework-auth): add OTP form component and verification flow#455
pcfreak30 merged 1 commit intodevelopfrom
libs/portal-plugin-dashboard

Conversation

@pcfreak30
Copy link
Copy Markdown
Member

@pcfreak30 pcfreak30 commented Aug 19, 2025

  • Added new OtpForm component with schema validation
  • Implemented OTP route in dashboard plugin
  • Moved EmailVerificationBanner to widgets directory
  • Updated widget registrations and plugin config
  • Improved form imports and type usage
  • Modified useApiUrl hook to use framework context

Summary by CodeRabbit

  • New Features

    • Added One-Time Passcode (OTP) login screen with 6-digit validation, “Verify” action, “Back to Login” link and optional redirect after success.
  • Enhancements

    • Account verification route now themed for consistent styling.
    • API URL resolution improved to prefer framework-provided portal URL when available.
  • UI

    • Email Verification Banner exposed as a dashboard widget and placed in the dashboard header.
    • Dashboard widget registrations and targets updated for cleaner placements.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Aug 19, 2025

⚠️ No Changeset found

Latest commit: 13999a7

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 19, 2025

Walkthrough

Adds an OTP login flow and related exports: new OtpForm component, form factory, and Zod schema; updates portal-framework-auth public API. Dashboard plugin exposes, route, widget, and banner registrations adjusted. Minor UI-framework import and useApiUrl control-flow tweaks.

Changes

Cohort / File(s) Summary of changes
Auth: public export
libs/portal-framework-auth/src/index.ts
Exports OtpForm in the public API export list.
Auth: OTP UI component
libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx
New themed OtpForm component; reads optional to param, uses useLogin, navigates when authenticated; exports OtpParams.
Auth: OTP form factory & schema
libs/portal-framework-auth/src/ui/forms/otp.tsx, libs/portal-framework-auth/src/ui/forms/otp.schema.ts
Adds getOtpForm(login, to?) producing form config and Zod schema enforcing 6-character otp.
UI framework: form renderer
libs/portal-framework-ui/src/components/form/FormRenderer.tsx
Adjusted imports: direct FormGroup named import and type-only imports refactor; no runtime behavior changes.
UI framework: API hook
libs/portal-framework-ui/src/hooks/useApiUrl.ts
Integrates useFramework(); when framework loaded returns framework.portalUrl (fallbacks preserved).
Dashboard: plugin exposes & routes
libs/portal-plugin-dashboard/plugin.config.ts, libs/portal-plugin-dashboard/src/ui/routes/otp.tsx, libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx, libs/portal-plugin-dashboard/src/ui/routes/dashboard.tsx
Adds loginOtp expose, remaps account.verify, removes direct EmailVerificationBanner expose, adds OTP route re-exporting OtpForm, wraps account.verify with theme and tweaks SDK usage, changes dashboard WidgetArea to core:dashboard:header.
Dashboard: email verification banner & widget
libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx, libs/portal-plugin-dashboard/src/ui/widgets/account/emailVerificationBanner.tsx
Banner now default-exports component (removed bridge wrapper); new widget wrapper re-exports/renders the banner; app-name check uses getAppName().
Dashboard: widget registrations
libs/portal-plugin-dashboard/src/widgetRegistrations.ts
First registration area -> core:dashboard:header, componentName updated to new widget path; minor property-order reordering in other entries.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant OTPRoute as Dashboard OTP Route
  participant OtpForm as OtpForm (Auth)
  participant LoginHook as useLogin()
  participant AuthAPI as Auth Server
  participant Nav as Navigator (useGo)

  User->>OTPRoute: GET /login/otp?to=/dest
  OTPRoute->>OtpForm: render
  OtpForm->>User: show 6-digit form
  User->>OtpForm: submit OTP
  OtpForm->>LoginHook: login({ otp, redirectTo: to })
  LoginHook->>AuthAPI: verify OTP
  AuthAPI-->>LoginHook: success / failure
  alt success
    OtpForm->>Nav: push(to)
    Nav-->>User: redirect to /dest
  else failure
    OtpForm-->>User: show error
  end
Loading
sequenceDiagram
  autonumber
  participant Comp as Any Component
  participant Hook as useApiUrl()
  participant FW as useFramework()
  participant Base as getApiBaseUrl()

  Comp->>Hook: call
  Hook->>FW: useFramework()
  alt framework loaded (isLoading=false)
    Hook-->>Comp: framework.portalUrl || fallback
  else
    Hook->>Base: compute apiUrl (allowLocalhost)
    alt api found
      Hook-->>Comp: apiUrl
    else
      Hook-->>Comp: ""
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

I hop to fields of six-digit light,
Paws on keys in the quiet night.
Banners shift and widgets play,
Routes rewire my hopping way.
A tiny code-nibble, then—away! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch libs/portal-plugin-dashboard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
libs/portal-framework-ui/src/components/form/FormRenderer.tsx (1)

168-175: Loading-state can get stuck due to stale closure; stop guarding setIsLoading(false) with the captured isLoading.

isLoading is captured by the async closure, so if (isLoading) setIsLoading(false) often evaluates false after awaiting, leaving the spinner stuck. Unconditionally clear loading in a finally block.

Apply:

-          const showPromiseOrValue = field.show(currentValues);
-          if (showPromiseOrValue instanceof Promise) {
-            setIsLoading(true);
-            shouldShow = await showPromiseOrValue;
-            if (isLoading) setIsLoading(false);
-          } else {
-            shouldShow = showPromiseOrValue;
-            if (isLoading) setIsLoading(false);
-          }
+          const showPromiseOrValue = field.show(currentValues);
+          if (showPromiseOrValue instanceof Promise) {
+            setIsLoading(true);
+            try {
+              shouldShow = await showPromiseOrValue;
+            } finally {
+              setIsLoading(false);
+            }
+          } else {
+            shouldShow = showPromiseOrValue;
+          }
         } catch (error) {
           console.error(
             `Error checking show status for field ${String(field.name)}:`,
             error,
           );
           shouldShow = false;
-          if (isLoading) setIsLoading(false);
         }
       } else if (!field.show) {
-        if (isLoading) setIsLoading(false);
+        // no async work; ensure we are not in a loading state
+        setIsLoading(false);
       } else {
-        if (isLoading) setIsLoading(false);
+        setIsLoading(false);
       }

Also applies to: 177-188

libs/portal-framework-auth/src/index.ts (1)

7-8: Fix typo in exported type name

The export in libs/portal-framework-auth/src/index.ts mistakenly reads OPTGenerateResponse but the actual type declared in libs/portal-sdk/src/account.ts is OTPGenerateResponse. Please update the re-export:

  • File: libs/portal-framework-auth/src/index.ts
    Line: around 7

Diff:

- type OPTGenerateResponse,
+ type OTPGenerateResponse,
🧹 Nitpick comments (9)
libs/portal-framework-auth/src/ui/forms/otp.schema.ts (1)

4-4: Trim input to reduce user error; optionally enforce digits-only if desired.

Trimming avoids failing valid codes due to leading/trailing spaces from paste.

Apply:

-  otp: z.string().length(6, { message: "OTP must be 6 characters" }),
+  otp: z.string().trim().length(6, { message: "OTP must be 6 characters" }),

If OTPs are numeric-only, consider:

otp: z.string().trim().regex(/^\d{6}$/, { message: "OTP must be 6 digits" }),
libs/portal-plugin-dashboard/src/ui/routes/otp.tsx (1)

1-3: Use a one-line re-export to avoid creating an extra binding.

Minor nit: shorter and slightly cleaner.

Apply:

-import { OtpForm } from "@lumeweb/portal-framework-auth";
-
-export default OtpForm;
+export { OtpForm as default } from "@lumeweb/portal-framework-auth";
libs/portal-framework-auth/src/ui/forms/otp.tsx (2)

50-55: “Resend now” button is non-functional

Consider accepting a resend callback and wiring it to the button.

Apply this diff to support a resend handler:

@@
-export const getOtpForm = (
-  login: (values: { otp: string }) => void,
-  to?: string,
-): FormConfig => {
+export const getOtpForm = (
+  login: (values: { otp: string; redirectTo?: string }) => void,
+  to?: string,
+  onResend?: () => void,
+): FormConfig => {
@@
-          <button
-            className="text-md h-0 text-primary-1 hover:underline"
-            type="button">
+          <button
+            className="text-md h-0 text-primary-1 hover:underline"
+            onClick={onResend}
+            type="button">
             Resend now →
           </button>

Additionally, if supported by your form field API, consider numeric input affordances for better UX on mobile (e.g., inputMode="numeric", autoComplete="one-time-code", maxLength=6).


21-28: Optional: constrain OTP input UX

If the form system allows, set numeric keypad and length constraints to reduce input errors.

Example (pseudocode, adapt to your FormFieldType):

  • inputMode: "numeric"
  • autoComplete: "one-time-code"
  • pattern: "\d*"
  • maxLength: 6
libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx (1)

28-45: Optional: include userEmail in queryKey

The query depends on both token and userEmail; adding userEmail improves cache correctness.

Apply this diff:

-    queryKey: ["exchange-token", token],
+    queryKey: ["exchange-token", token, userEmail],
libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx (2)

1-1: Prefer type-only import for Identity

Avoids accidental runtime import and keeps bundles lean.

Apply this diff:

-import { Identity, useFramework } from "@lumeweb/portal-framework-core";
+import { useFramework, type Identity } from "@lumeweb/portal-framework-core";

19-21: Use strict equality for app-name gate

Prevents surprising coercions; strings should be compared strictly.

-  if (getAppName() != "dashboard") {
+  if (getAppName() !== "dashboard") {
     return null;
   }
libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (2)

19-31: Redirect safety + ensure redirectTo is passed to login

  • Safety: Piping a raw to param from the URL directly into navigation can cause unwanted routes or, depending on the router, potential open-redirect-like behavior. Prefer whitelisting internal paths and providing a sane default.
  • Flow: In the previous implementation, redirectTo was passed to the login mutation. Here, it’s only given to getOtpForm. If getOtpForm doesn’t inject it into the submission, the post-login redirect may be lost.

Proposed refactor (adds a normalized to variable, default fallback, and ensures redirectTo is sent with the mutation):

-  const parsed = useParsed<OtpParams>();
-  const login = useLogin();
+  const parsed = useParsed<OtpParams>();
+  const to = parsed.params?.to;
+  const login = useLogin();

-  useEffect(() => {
-    if (!isAuthLoading && authData?.authenticated) {
-      go({ to: parsed.params?.to, type: "push" });
-    }
-  }, [isAuthLoading, authData, parsed.params?.to, go]);
+  useEffect(() => {
+    if (!isAuthLoading && authData?.authenticated) {
+      // Only allow internal navigations; fallback to "/"
+      if (typeof to === "string" && to.startsWith("/")) {
+        go({ to, type: "push" });
+      } else {
+        go({ to: "/", type: "push" });
+      }
+    }
+  }, [isAuthLoading, authData, to, go]);

-  const otpFormConfig = getOtpForm(
-    (values) => login.mutate(values),
-    parsed.params?.to,
-  );
+  const otpFormConfig = getOtpForm(
+    (values) => login.mutate({ ...values, redirectTo: to }),
+    to,
+  );

If getOtpForm already guarantees redirectTo is included, you can skip passing it here. Please verify libs/portal-framework-auth/src/ui/forms/otp.tsx to avoid duplication.


20-21: Improve typing for login mutation values

If you have a request type for OTP login (e.g., OTPFormRequest or z.infer<typeof otpSchema>), parameterize useLogin for stronger type-safety:

Example:

// import type { OTPFormRequest } from "../../forms/otp.schema";
const login = useLogin<OTPFormRequest>();

This helps prevent shape drift between the form schema and the auth provider.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 22f4403 and b66167f.

📒 Files selected for processing (13)
  • libs/portal-framework-auth/src/index.ts (2 hunks)
  • libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (1 hunks)
  • libs/portal-framework-auth/src/ui/forms/otp.schema.ts (1 hunks)
  • libs/portal-framework-auth/src/ui/forms/otp.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/form/FormRenderer.tsx (1 hunks)
  • libs/portal-framework-ui/src/hooks/useApiUrl.ts (1 hunks)
  • libs/portal-plugin-dashboard/plugin.config.ts (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx (4 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/dashboard.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/otp.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/emailVerificationBanner.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/widgetRegistrations.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (1)
apps/portal-dashboard/app/routes/login/otp.tsx (1)
  • OtpForm (26-96)
🔇 Additional comments (11)
libs/portal-framework-ui/src/components/form/FormRenderer.tsx (1)

22-23: FormGroup is correctly exported as a named export
The script confirms export const FormGroup = … in
libs/portal-framework-ui/src/components/form/FormGroup.tsx, so
import { FormGroup } is valid.

libs/portal-plugin-dashboard/src/ui/routes/dashboard.tsx (1)

11-11: LGTM: WidgetArea target updated to header area.

Matches the widget registrations move to "core:dashboard:header".

libs/portal-plugin-dashboard/src/ui/widgets/account/emailVerificationBanner.tsx (1)

3-5: LGTM: simple wrapper fits widget exposure

Straightforward wrapper to register the banner as a widget. Path alias usage looks consistent with the rest of the plugin.

libs/portal-framework-auth/src/index.ts (2)

19-19: Public export for OtpForm is correct

Default import path aligns with the new component; export order is consistent with existing indices.


28-28: Exporting OtpForm in the public API is appropriate

This makes the OTP flow available to downstream consumers and matches the new dashboard route.

libs/portal-plugin-dashboard/plugin.config.ts (3)

16-16: Route mapping update to account.verify looks good

Matches the renamed route module; no issues spotted.


20-20: New loginOtp exposure is correctly wired

This aligns with the new OTP route and public export.


27-28: All exposed widget paths exist as expected

  • libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx
  • libs/portal-plugin-dashboard/src/ui/routes/otp.tsx
  • libs/portal-plugin-dashboard/src/ui/widgets/account/emailVerificationBanner.tsx

Widget exposure for emailVerificationBanner is consistent with the widget registry changes. Approving these code changes.

libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx (2)

2-2: withTheme integration looks correct

The HOC usage is consistent with other themed routes.


31-34: SDK call updated to verifyEmail is sound

Non-null assertions are guarded by enabled, so runtime safety is acceptable here.

libs/portal-plugin-dashboard/src/widgetRegistrations.ts (1)

5-7: Confirmed: Widget exposure and header area rendering verified

  • plugin.config.ts (lines 27–28) exposes "./widgets/account/emailVerificationBanner" pointing to ./src/ui/widgets/account/emailVerificationBanner.
  • dashboard.tsx (line 11) includes <WidgetArea widgetAreaId="core:dashboard:header" />, ensuring the header area is rendered.

…m component and verification flow

- Added new OtpForm component with schema validation
- Implemented OTP route in dashboard plugin
- Moved EmailVerificationBanner to widgets directory
- Updated widget registrations and plugin config
- Improved form imports and type usage
- Modified useApiUrl hook to use framework context
@pcfreak30 pcfreak30 force-pushed the libs/portal-plugin-dashboard branch from b66167f to 13999a7 Compare August 19, 2025 12:46
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
libs/portal-plugin-dashboard/src/ui/widgets/account/emailVerificationBanner.tsx (1)

3-5: Optional: simplify by re-exporting the component instead of wrapping.

If no widget-specific logic is needed (e.g., gating, context, props), you can avoid an extra component boundary.

Option A (one-liner re-export; smallest bundle impact):

-import EmailVerificationBanner from "@/ui/components/EmailVerificationBanner";
-
-export default function EmailVerificationBannerWidget() {
-  return <EmailVerificationBanner />;
-}
+export { default } from "@/ui/components/EmailVerificationBanner";

Option B (keep import; export directly):

 import EmailVerificationBanner from "@/ui/components/EmailVerificationBanner";
 
-export default function EmailVerificationBannerWidget() {
-  return <EmailVerificationBanner />;
-}
+export default EmailVerificationBanner;

Note: If you rely on a specific component displayName in DevTools/metrics, the current wrapper preserves a distinct name; otherwise, the re-export is cleaner.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b66167f and 13999a7.

📒 Files selected for processing (13)
  • libs/portal-framework-auth/src/index.ts (2 hunks)
  • libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (1 hunks)
  • libs/portal-framework-auth/src/ui/forms/otp.schema.ts (1 hunks)
  • libs/portal-framework-auth/src/ui/forms/otp.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/form/FormRenderer.tsx (1 hunks)
  • libs/portal-framework-ui/src/hooks/useApiUrl.ts (1 hunks)
  • libs/portal-plugin-dashboard/plugin.config.ts (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx (4 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/dashboard.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/otp.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/emailVerificationBanner.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/widgetRegistrations.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • libs/portal-plugin-dashboard/src/ui/routes/otp.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • libs/portal-framework-auth/src/ui/forms/otp.schema.ts
  • libs/portal-framework-auth/src/index.ts
  • libs/portal-plugin-dashboard/plugin.config.ts
  • libs/portal-framework-ui/src/components/form/FormRenderer.tsx
  • libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx
  • libs/portal-plugin-dashboard/src/ui/routes/dashboard.tsx
  • libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx
  • libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx
  • libs/portal-framework-ui/src/hooks/useApiUrl.ts
  • libs/portal-framework-auth/src/ui/forms/otp.tsx
  • libs/portal-plugin-dashboard/src/widgetRegistrations.ts
🔇 Additional comments (2)
libs/portal-plugin-dashboard/src/ui/widgets/account/emailVerificationBanner.tsx (2)

3-5: LGTM — simple wrapper is fine.

Straightforward wrapper keeps widget boundary clear and allows future widget-specific logic.


1-1: Verify @/ alias for dashboard plugin build and tests

  • libs/portal-plugin-dashboard/tsconfig.json already maps "@/*": ["./src/*"].
  • The Vite config in this package imports Config from @lumeweb/portal-framework-core/vite—please confirm that this wrapper applies the @ alias (e.g. via tsconfig-paths or an explicit alias: { '@/': ... }).
  • No Jest moduleNameMapper was found; if you’re running tests with Vitest or Jest, ensure the same @/ alias is configured in your test runner’s resolve/alias settings.

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