From a5143666cbd528cd19fd3f18d02d3cb7291740b4 Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Wed, 3 Dec 2025 19:02:17 -0800 Subject: [PATCH] Standardize UserRowItem --- .../(ee)/program/fraud/fraud-event-groups-table.tsx | 12 ++++++------ .../program/fraud/resolve-fraud-events-modal.tsx | 6 +++--- .../resolved/resolved-fraud-event-groups-table.tsx | 12 ++++++------ .../(ee)/program/payouts/payout-paid-cell.tsx | 13 ++++++++----- apps/web/lib/api/fraud/get-grouped-fraud-events.ts | 3 +++ apps/web/lib/swr/use-fraud-event-groups.ts | 4 ++-- apps/web/lib/types.ts | 4 ++-- apps/web/lib/zod/schemas/fraud.ts | 6 +----- apps/web/lib/zod/schemas/payouts.ts | 9 ++------- apps/web/lib/zod/schemas/users.ts | 1 + apps/web/tests/fraud/index.test.ts | 4 ++-- .../fraud-risks/commissions-on-hold-table.tsx | 4 ++-- .../fraud-risks/fraud-events-tables/index.tsx | 4 ++-- .../ui/partners/fraud-risks/fraud-review-sheet.tsx | 12 ++++++------ apps/web/ui/users/user-row-item.tsx | 6 +++--- 15 files changed, 49 insertions(+), 51 deletions(-) diff --git a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/fraud-event-groups-table.tsx b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/fraud-event-groups-table.tsx index e770fea5620..1b0ec897532 100644 --- a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/fraud-event-groups-table.tsx +++ b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/fraud-event-groups-table.tsx @@ -4,7 +4,7 @@ import { FRAUD_RULES_BY_TYPE } from "@/lib/api/fraud/constants"; import { mutatePrefix } from "@/lib/swr/mutate"; import { useFraudEventGroups } from "@/lib/swr/use-fraud-event-groups"; import { useFraudEventsCount } from "@/lib/swr/use-fraud-events-count"; -import { fraudEventGroupProps } from "@/lib/types"; +import { FraudEventGroupProps } from "@/lib/types"; import { useBanPartnerModal } from "@/ui/modals/ban-partner-modal"; import { useBulkBanPartnersModal } from "@/ui/modals/bulk-ban-partners-modal"; import { FraudReviewSheet } from "@/ui/partners/fraud-risks/fraud-review-sheet"; @@ -84,11 +84,11 @@ export function FraudEventGroupsTable() { }); const [pendingBanPartners, setPendingBanPartners] = useState< - Array> + Array> >([]); const tableRef = useRef< - ReturnType>["table"] | null + ReturnType>["table"] | null >(null); const { BulkBanPartnersModal, setShowBulkBanPartnersModal } = @@ -100,7 +100,7 @@ export function FraudEventGroupsTable() { }, }); - const { table, ...tableProps } = useTable({ + const { table, ...tableProps } = useTable({ data: fraudEvents || [], columns: [ { @@ -369,7 +369,7 @@ export function FraudEventGroupsTable() { ); } -function RowMenuButton({ row }: { row: Row }) { +function RowMenuButton({ row }: { row: Row }) { const fraudEvent = row.original; const [isOpen, setIsOpen] = useState(false); @@ -465,7 +465,7 @@ function useCurrentFraudEventGroup({ fraudEventGroups, groupKey, }: { - fraudEventGroups?: fraudEventGroupProps[]; + fraudEventGroups?: FraudEventGroupProps[]; groupKey: string | null; }) { let currentFraudEventGroup = groupKey diff --git a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolve-fraud-events-modal.tsx b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolve-fraud-events-modal.tsx index 7be5f49bf81..f502f794338 100644 --- a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolve-fraud-events-modal.tsx +++ b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolve-fraud-events-modal.tsx @@ -4,7 +4,7 @@ import { resolveFraudEventsAction } from "@/lib/actions/fraud/resolve-fraud-even import { parseActionError } from "@/lib/actions/parse-action-errors"; import { mutatePrefix } from "@/lib/swr/mutate"; import useWorkspace from "@/lib/swr/use-workspace"; -import { fraudEventGroupProps } from "@/lib/types"; +import { FraudEventGroupProps } from "@/lib/types"; import { MAX_RESOLUTION_REASON_LENGTH, resolveFraudEventsSchema, @@ -34,7 +34,7 @@ function ResolveFraudEventsModal({ }: { showResolveFraudEventModal: boolean; setShowResolveFraudEventModal: Dispatch>; - fraudEventGroup: fraudEventGroupProps; + fraudEventGroup: FraudEventGroupProps; onConfirm?: () => void; }) { const { id: workspaceId } = useWorkspace(); @@ -169,7 +169,7 @@ export function useResolveFraudEventsModal({ fraudEventGroup, onConfirm, }: { - fraudEventGroup: fraudEventGroupProps; + fraudEventGroup: FraudEventGroupProps; onConfirm?: () => void; }) { const [showResolveFraudEventModal, setShowResolveFraudEventModal] = diff --git a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-event-groups-table.tsx b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-event-groups-table.tsx index fdab6cdce96..d5bf079a89c 100644 --- a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-event-groups-table.tsx +++ b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-event-groups-table.tsx @@ -3,7 +3,7 @@ import { FRAUD_RULES_BY_TYPE } from "@/lib/api/fraud/constants"; import { useFraudEventGroups } from "@/lib/swr/use-fraud-event-groups"; import { useFraudEventsCount } from "@/lib/swr/use-fraud-events-count"; -import { fraudEventGroupProps } from "@/lib/types"; +import { FraudEventGroupProps } from "@/lib/types"; import { FraudReviewSheet } from "@/ui/partners/fraud-risks/fraud-review-sheet"; import { PartnerRowItem } from "@/ui/partners/partner-row-item"; import { AnimatedEmptyState } from "@/ui/shared/animated-empty-state"; @@ -83,7 +83,7 @@ export function ResolvedFraudEventGroupsTable() { header: "Event", minSize: 100, maxSize: 400, - cell: ({ row }: { row: Row }) => { + cell: ({ row }: { row: Row }) => { const reason = FRAUD_RULES_BY_TYPE[row.original.type]; const count = row.original.count ?? 1; @@ -121,7 +121,7 @@ export function ResolvedFraudEventGroupsTable() { { id: "resolvedAt", header: "Resolved on", - cell: ({ row }: { row: Row }) => { + cell: ({ row }: { row: Row }) => { const user = row.original.user; const resolvedAt = row.original.resolvedAt; @@ -135,7 +135,7 @@ export function ResolvedFraudEventGroupsTable() { { id: "partner", header: "Partner", - cell: ({ row }: { row: Row }) => { + cell: ({ row }: { row: Row }) => { const partner = row.original.partner; if (!partner) return "-"; @@ -150,7 +150,7 @@ export function ResolvedFraudEventGroupsTable() { ); }, meta: { - filterParams: ({ row }: { row: Row }) => + filterParams: ({ row }: { row: Row }) => row.original.partner ? { partnerId: row.original.partner.id, @@ -297,7 +297,7 @@ function useCurrentFraudEventGroup({ fraudEventGroups, groupKey, }: { - fraudEventGroups?: fraudEventGroupProps[]; + fraudEventGroups?: FraudEventGroupProps[]; groupKey: string | null; }) { let currentFraudEventGroup = groupKey diff --git a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-paid-cell.tsx b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-paid-cell.tsx index 938932f9ca6..bb8cde0ad97 100644 --- a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-paid-cell.tsx +++ b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-paid-cell.tsx @@ -5,6 +5,7 @@ import { formatDate, formatDateTime, OG_AVATAR_URL } from "@dub/utils"; type PayoutPaidCellUser = { id?: string; name?: string | null; + email?: string | null; image?: string | null; } | null; @@ -33,11 +34,13 @@ export function PayoutPaidCell({ {user && (
{user.name -

{user.name}

+

+ {user.name ?? user.email ?? user.id} +

)}
@@ -78,8 +81,8 @@ export function PayoutPaidCell({
{user && ( {user.name )} diff --git a/apps/web/lib/api/fraud/get-grouped-fraud-events.ts b/apps/web/lib/api/fraud/get-grouped-fraud-events.ts index f24b0ccf434..0796bb6503f 100644 --- a/apps/web/lib/api/fraud/get-grouped-fraud-events.ts +++ b/apps/web/lib/api/fraud/get-grouped-fraud-events.ts @@ -26,6 +26,7 @@ interface QueryResult { partnerImage: string | null; userId: string | null; userName: string | null; + userEmail: string | null; userImage: string | null; } @@ -80,6 +81,7 @@ export async function getGroupedFraudEvents({ p.email AS partnerEmail, p.image AS partnerImage, u.name AS userName, + u.email AS userEmail, u.image AS userImage FROM ( SELECT @@ -122,6 +124,7 @@ export async function getGroupedFraudEvents({ ? { id: event.userId, name: event.userName, + email: event.userEmail, image: event.userImage, } : null, diff --git a/apps/web/lib/swr/use-fraud-event-groups.ts b/apps/web/lib/swr/use-fraud-event-groups.ts index 5ab22359b42..db569266d34 100644 --- a/apps/web/lib/swr/use-fraud-event-groups.ts +++ b/apps/web/lib/swr/use-fraud-event-groups.ts @@ -2,7 +2,7 @@ import { useRouterStuff } from "@dub/ui"; import { fetcher } from "@dub/utils"; import useSWR from "swr"; import { z } from "zod"; -import { fraudEventGroupProps } from "../types"; +import { FraudEventGroupProps } from "../types"; import { groupedFraudEventsQuerySchema } from "../zod/schemas/fraud"; import useWorkspace from "./use-workspace"; @@ -26,7 +26,7 @@ export function useFraudEventGroups({ { exclude }, ); - const { data, error } = useSWR( + const { data, error } = useSWR( enabled && defaultProgramId ? `/api/fraud/events${queryString}` : undefined, fetcher, { diff --git a/apps/web/lib/types.ts b/apps/web/lib/types.ts index 9a6479d401b..795f90cc04f 100644 --- a/apps/web/lib/types.ts +++ b/apps/web/lib/types.ts @@ -59,8 +59,8 @@ import { DiscountCodeSchema, DiscountSchema } from "./zod/schemas/discount"; import { EmailDomainSchema } from "./zod/schemas/email-domains"; import { FolderSchema } from "./zod/schemas/folders"; import { - groupedFraudEventSchema, fraudRuleSchema, + groupedFraudEventSchema, updateFraudRuleSettingsSchema, } from "./zod/schemas/fraud"; import { GroupWithProgramSchema } from "./zod/schemas/group-with-program"; @@ -673,7 +673,7 @@ export type WorkflowAttribute = (typeof WORKFLOW_ATTRIBUTES)[number]; export type EmailDomainProps = z.infer; -export type fraudEventGroupProps = z.infer; +export type FraudEventGroupProps = z.infer; export type ExtendedFraudRuleType = | FraudRuleType diff --git a/apps/web/lib/zod/schemas/fraud.ts b/apps/web/lib/zod/schemas/fraud.ts index 788573a22ea..362ca328d7e 100644 --- a/apps/web/lib/zod/schemas/fraud.ts +++ b/apps/web/lib/zod/schemas/fraud.ts @@ -24,11 +24,7 @@ export const groupedFraudEventSchema = z.object({ email: true, image: true, }), - user: UserSchema.pick({ - id: true, - name: true, - image: true, - }).nullable(), + user: UserSchema.nullable(), }); export const groupedFraudEventsQuerySchema = z diff --git a/apps/web/lib/zod/schemas/payouts.ts b/apps/web/lib/zod/schemas/payouts.ts index efa494ea3f4..171502ab988 100644 --- a/apps/web/lib/zod/schemas/payouts.ts +++ b/apps/web/lib/zod/schemas/payouts.ts @@ -5,6 +5,7 @@ import { z } from "zod"; import { getPaginationQuerySchema } from "./misc"; import { EnrolledPartnerSchema, PartnerSchema } from "./partners"; import { ProgramSchema } from "./programs"; +import { UserSchema } from "./users"; export const createManualPayoutSchema = z.object({ workspaceId: z.string(), @@ -72,13 +73,7 @@ export const PayoutResponseSchema = PayoutSchema.merge( tenantId: z.string().nullable(), }), ), - user: z - .object({ - id: z.string(), - name: z.string().nullable(), - image: z.string().nullable(), - }) - .nullish(), + user: UserSchema.nullish(), }), ); diff --git a/apps/web/lib/zod/schemas/users.ts b/apps/web/lib/zod/schemas/users.ts index 4d957284918..d4c89a4b39f 100644 --- a/apps/web/lib/zod/schemas/users.ts +++ b/apps/web/lib/zod/schemas/users.ts @@ -3,5 +3,6 @@ import { z } from "zod"; export const UserSchema = z.object({ id: z.string(), name: z.string().nullable(), + email: z.string().nullable(), image: z.string().nullable(), }); diff --git a/apps/web/tests/fraud/index.test.ts b/apps/web/tests/fraud/index.test.ts index 53b5aa38d47..b654e04ab1e 100644 --- a/apps/web/tests/fraud/index.test.ts +++ b/apps/web/tests/fraud/index.test.ts @@ -1,4 +1,4 @@ -import { Customer, fraudEventGroupProps, TrackLeadResponse } from "@/lib/types"; +import { Customer, FraudEventGroupProps, TrackLeadResponse } from "@/lib/types"; import { FraudRuleType } from "@prisma/client"; import { randomCustomer, retry } from "tests/utils/helpers"; import { HttpClient } from "tests/utils/http"; @@ -234,7 +234,7 @@ async function waitForFraudEvent({ }) { return await retry( async () => { - const { data } = await http.get({ + const { data } = await http.get({ path: "/fraud/events", query: { partnerId, diff --git a/apps/web/ui/partners/fraud-risks/commissions-on-hold-table.tsx b/apps/web/ui/partners/fraud-risks/commissions-on-hold-table.tsx index 85f0e85c3f3..f6c2f3ac788 100644 --- a/apps/web/ui/partners/fraud-risks/commissions-on-hold-table.tsx +++ b/apps/web/ui/partners/fraud-risks/commissions-on-hold-table.tsx @@ -1,5 +1,5 @@ import useWorkspace from "@/lib/swr/use-workspace"; -import { CommissionResponse, fraudEventGroupProps } from "@/lib/types"; +import { CommissionResponse, FraudEventGroupProps } from "@/lib/types"; import { COMMISSIONS_MAX_PAGE_SIZE } from "@/lib/zod/schemas/commissions"; import { CustomerRowItem } from "@/ui/customers/customer-row-item"; import { @@ -23,7 +23,7 @@ import { CommissionTypeBadge } from "../commission-type-badge"; export function CommissionsOnHoldTable({ fraudEventGroup, }: { - fraudEventGroup: fraudEventGroupProps; + fraudEventGroup: FraudEventGroupProps; }) { const workspace = useWorkspace(); const { id: workspaceId, slug } = workspace; diff --git a/apps/web/ui/partners/fraud-risks/fraud-events-tables/index.tsx b/apps/web/ui/partners/fraud-risks/fraud-events-tables/index.tsx index 07d40db82fd..e2934b9e7de 100644 --- a/apps/web/ui/partners/fraud-risks/fraud-events-tables/index.tsx +++ b/apps/web/ui/partners/fraud-risks/fraud-events-tables/index.tsx @@ -1,4 +1,4 @@ -import { fraudEventGroupProps } from "@/lib/types"; +import { FraudEventGroupProps } from "@/lib/types"; import { FraudRuleType } from "@dub/prisma/client"; import React from "react"; import { FraudCrossProgramBanTable } from "./fraud-cross-program-ban-table"; @@ -21,7 +21,7 @@ const FRAUD_EVENTS_TABLES: Partial> = export function FraudEventsTableWrapper({ fraudEventGroup, }: { - fraudEventGroup: fraudEventGroupProps; + fraudEventGroup: FraudEventGroupProps; }) { const TableComponent = FRAUD_EVENTS_TABLES[fraudEventGroup.type]; diff --git a/apps/web/ui/partners/fraud-risks/fraud-review-sheet.tsx b/apps/web/ui/partners/fraud-risks/fraud-review-sheet.tsx index aa3ee49e58c..6ecc461d6db 100644 --- a/apps/web/ui/partners/fraud-risks/fraud-review-sheet.tsx +++ b/apps/web/ui/partners/fraud-risks/fraud-review-sheet.tsx @@ -1,7 +1,7 @@ "use client"; import { FRAUD_RULES_BY_TYPE } from "@/lib/api/fraud/constants"; -import { fraudEventGroupProps } from "@/lib/types"; +import { FraudEventGroupProps } from "@/lib/types"; import { useBanPartnerModal } from "@/ui/modals/ban-partner-modal"; import { X } from "@/ui/shared/icons"; import { @@ -25,7 +25,7 @@ import { CommissionsOnHoldTable } from "./commissions-on-hold-table"; import { FraudEventsTableWrapper } from "./fraud-events-tables"; interface FraudReviewSheetProps { - fraudEventGroup: fraudEventGroupProps; + fraudEventGroup: FraudEventGroupProps; setIsOpen: Dispatch>; onNext?: () => void; onPrevious?: () => void; @@ -199,8 +199,8 @@ function FraudReviewSheetContent({ {user && (
{user.name

{user.name}

@@ -220,8 +220,8 @@ function FraudReviewSheetContent({ > {user && ( {user.name )} diff --git a/apps/web/ui/users/user-row-item.tsx b/apps/web/ui/users/user-row-item.tsx index 6e742f0a28b..7e4f7b623b0 100644 --- a/apps/web/ui/users/user-row-item.tsx +++ b/apps/web/ui/users/user-row-item.tsx @@ -10,12 +10,12 @@ export function UserRowItem({ date, label, }: { - user: Pick; + user: UserProps; date: Date; label: string; }) { - const image = user.image || `${OG_AVATAR_URL}${user.name}`; - const name = user.name ?? user.id; + const image = user.image || `${OG_AVATAR_URL}${user.id}`; + const name = user.name ?? user.email ?? user.id; return (