Conversation
There was a problem hiding this comment.
cubic analysis
7 issues found across 76 files • Review in cubic
React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| error_message: z.string().optional(), | ||
| error: z.string().optional(), | ||
| }); | ||
| const errorData = errorSchema.parse(await response.json().catch(() => ({}))); |
There was a problem hiding this comment.
Using errorSchema.parse here will throw a ZodError if the response shape deviates (for example if types differ), which then surfaces an unfriendly technical message to the user. Consider safeParse and falling back to a default error string instead. (Based on your team's feedback about providing user-friendly error handling.)
Prompt for AI agents
Address the following comment on frontend/app/settings/page.tsx at line 130:
<comment>Using errorSchema.parse here will throw a ZodError if the response shape deviates (for example if types differ), which then surfaces an unfriendly technical message to the user. Consider safeParse and falling back to a default error string instead. (Based on your team's feedback about providing user-friendly error handling.)</comment>
<file context>
@@ -123,11 +123,16 @@ const LeaveWorkspaceSection = () => {
});
if (!response.ok) {
- const errorData = await response.json().catch(() => null);
- throw new Error(errorData?.error_message || errorData?.error || "Failed to leave workspace");
+ const errorSchema = z.object({
+ error_message: z.string().optional(),
+ error: z.string().optional(),
+ });
</file context>
| zip_code: data.zip_code ?? "", | ||
| street_address: data.street_address ?? "", | ||
| birth_date: data.birth_date ? parseDate(data.birth_date) : null, | ||
| citizenship_country_code: data.citizenship_country_code ?? "", |
There was a problem hiding this comment.
Defaulting citizenship_country_code to an empty string treats the user as a foreign taxpayer and changes validation / UI flows even when their citizenship is actually unknown.
Prompt for AI agents
Address the following comment on frontend/app/settings/tax/page.tsx at line 117:
<comment>Defaulting citizenship_country_code to an empty string treats the user as a foreign taxpayer and changes validation / UI flows even when their citizenship is actually unknown.</comment>
<file context>
@@ -114,6 +114,9 @@ export default function TaxPage() {
zip_code: data.zip_code ?? "",
street_address: data.street_address ?? "",
birth_date: data.birth_date ? parseDate(data.birth_date) : null,
+ citizenship_country_code: data.citizenship_country_code ?? "",
+ legal_name: data.legal_name ?? "",
+ country_code: data.country_code ?? "",
</file context>
| citizenship_country_code: data.citizenship_country_code ?? "", | |
| citizenship_country_code: data.citizenship_country_code ?? "US", |
| <ComboBox | ||
| {...field} | ||
| options={data.workers | ||
| .sort((a, b) => a.user.name.localeCompare(b.user.name)) |
There was a problem hiding this comment.
Sorting mutates the data.workers array in place, potentially corrupting the TRPC cache and causing side-effects elsewhere. Use a copied array before sort to keep fetched data immutable. (Based on your team's feedback about avoiding in-place mutation of shared data.)
Prompt for AI agents
Address the following comment on frontend/app/(dashboard)/equity/grants/NewEquityGrantModal.tsx at line 233:
<comment>Sorting mutates the data.workers array in place, potentially corrupting the TRPC cache and causing side-effects elsewhere. Use a copied array before sort to keep fetched data immutable. (Based on your team's feedback about avoiding in-place mutation of shared data.)</comment>
<file context>
@@ -0,0 +1,606 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { CalendarDate, getLocalTimeZone, today } from "@internationalized/date";
+import { ChevronDown, ChevronRight } from "lucide-react";
+import { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+import TemplateSelector from "@/app/(dashboard)/document_templates/TemplateSelector";
</file context>
| .sort((a, b) => a.user.name.localeCompare(b.user.name)) | |
| .slice().sort((a, b) => a.user.name.localeCompare(b.user.name)) |
| }), | ||
| }); | ||
|
|
||
| const errorInfo = errorInfoSchema.parse(JSON.parse(error.message)); |
There was a problem hiding this comment.
Assumes error.message is always valid JSON; if it is not, JSON.parse will throw and mask the original error, breaking the error handler. Wrap parsing in try/catch or safely parse.
Prompt for AI agents
Address the following comment on frontend/app/(dashboard)/equity/grants/NewEquityGrantModal.tsx at line 159:
<comment>Assumes error.message is always valid JSON; if it is not, JSON.parse will throw and mask the original error, breaking the error handler. Wrap parsing in try/catch or safely parse.</comment>
<file context>
@@ -0,0 +1,606 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { CalendarDate, getLocalTimeZone, today } from "@internationalized/date";
+import { ChevronDown, ChevronRight } from "lucide-react";
+import { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+import TemplateSelector from "@/app/(dashboard)/document_templates/TemplateSelector";
</file context>
| ? (Number(data.sharePriceUsd) * numberOfShares).toFixed(2) | ||
| : null; | ||
|
|
||
| const isFormValid = form.formState.isValid; |
There was a problem hiding this comment.
isValid only updates after a validation trigger; without specifying mode:"onChange" or manually calling trigger, the button will remain disabled until the user submits once, leading to confusing UX.
Prompt for AI agents
Address the following comment on frontend/app/(dashboard)/equity/grants/NewEquityGrantModal.tsx at line 110:
<comment>isValid only updates after a validation trigger; without specifying mode:"onChange" or manually calling trigger, the button will remain disabled until the user submits once, leading to confusing UX.</comment>
<file context>
@@ -0,0 +1,606 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { CalendarDate, getLocalTimeZone, today } from "@internationalized/date";
+import { ChevronDown, ChevronRight } from "lucide-react";
+import { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+import TemplateSelector from "@/app/(dashboard)/document_templates/TemplateSelector";
</file context>
| </DialogContent> | ||
| </Dialog> | ||
|
|
||
| <NewEquityGrantModal open={showNewGrantModal} onOpenChange={setShowNewGrantModal} /> |
There was a problem hiding this comment.
NewEquityGrantModal is rendered unconditionally, so its heavy data-fetching hooks run even when the modal is closed, causing an unnecessary network request and wasted work (Based on your team's feedback about avoiding unnecessary queries on initial render).
Prompt for AI agents
Address the following comment on frontend/app/(dashboard)/equity/grants/page.tsx at line 169:
<comment>NewEquityGrantModal is rendered unconditionally, so its heavy data-fetching hooks run even when the modal is closed, causing an unnecessary network request and wasted work (Based on your team's feedback about avoiding unnecessary queries on initial render).</comment>
<file context>
@@ -182,6 +165,8 @@ export default function GrantsPage() {
) : null}
</DialogContent>
</Dialog>
+
+ <NewEquityGrantModal open={showNewGrantModal} onOpenChange={setShowNewGrantModal} />
</>
);
</file context>
| <NewEquityGrantModal open={showNewGrantModal} onOpenChange={setShowNewGrantModal} /> | |
| {showNewGrantModal && ( | |
| <NewEquityGrantModal open={showNewGrantModal} onOpenChange={setShowNewGrantModal} /> | |
| )} |
| {user.roles.worker ? ( | ||
| !hasLegalDetails ? ( | ||
| <Alert> | ||
| <Info /> |
There was a problem hiding this comment.
Icon size is missing; omit of "className="size-5"" makes this Lucide icon larger than the rest of the alert icons, breaking visual consistency.
Prompt for AI agents
Address the following comment on frontend/app/(dashboard)/invoices/page.tsx at line 326:
<comment>Icon size is missing; omit of "className=\"size-5\"" makes this Lucide icon larger than the rest of the alert icons, breaking visual consistency.</comment>
<file context>
@@ -347,11 +320,45 @@ export default function InvoicesPage() {
/>
<div className="grid gap-4">
- {workerNotice ? (
- <Alert>
- <Info className="size-5" />
- <AlertDescription>{workerNotice}</AlertDescription>
- </Alert>
+ {user.roles.worker ? (
</file context>
| <Info /> | |
| <Info className="size-5" /> |
Summary by cubic
Removed legacy onboarding logic and pages, updated onboarding to use a checklist for investors and workers, and added a new modal-based equity grant flow.
New Features
Refactors