Skip to content

Commit

Permalink
filter by partnerId, payoutId & status in useSalesCount
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey committed Nov 11, 2024
1 parent 0359f33 commit d376467
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 73 deletions.
75 changes: 42 additions & 33 deletions apps/web/app/api/programs/[programId]/sales/count/route.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,51 @@
import { getProgramOrThrow } from "@/lib/api/programs/get-program";
import { withWorkspace } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { getSalesCountQuerySchema } from "@/lib/zod/schemas/partners";
import { SaleStatus } from "@prisma/client";
import { NextResponse } from "next/server";

// GET /api/programs/[programId]/sales/count
export const GET = withWorkspace(async ({ workspace, params }) => {
const { programId } = params;
export const GET = withWorkspace(
async ({ workspace, params, searchParams }) => {
const { programId } = params;

await getProgramOrThrow({
workspaceId: workspace.id,
programId,
});

const salesCount = await prisma.sale.groupBy({
by: ["status"],
where: {
await getProgramOrThrow({
workspaceId: workspace.id,
programId,
},
_count: true,
});

const counts = salesCount.reduce(
(acc, p) => {
acc[p.status] = p._count;
return acc;
},
{} as Record<SaleStatus | "all", number>,
);

// fill in missing statuses with 0
Object.values(SaleStatus).forEach((status) => {
if (!(status in counts)) {
counts[status] = 0;
}
});

counts.all = salesCount.reduce((acc, p) => acc + p._count, 0);

return NextResponse.json(counts);
});
});

const { status, partnerId, payoutId } =
getSalesCountQuerySchema.parse(searchParams);

const salesCount = await prisma.sale.groupBy({
by: ["status"],
where: {
programId,
status,
partnerId,
payoutId,
},
_count: true,
});

const counts = salesCount.reduce(
(acc, p) => {
acc[p.status] = p._count;
return acc;
},
{} as Record<SaleStatus | "all", number>,
);

// fill in missing statuses with 0
Object.values(SaleStatus).forEach((status) => {
if (!(status in counts)) {
counts[status] = 0;
}
});

counts.all = salesCount.reduce((acc, p) => acc + p._count, 0);

return NextResponse.json(counts);
},
);
21 changes: 3 additions & 18 deletions apps/web/app/api/programs/[programId]/sales/route.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
import { INTERVAL_DATA, intervals } from "@/lib/analytics/constants";
import { INTERVAL_DATA } from "@/lib/analytics/constants";
import { validDateRangeForPlan } from "@/lib/analytics/utils";
import { getProgramOrThrow } from "@/lib/api/programs/get-program";
import { withWorkspace } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { getPaginationQuerySchema } from "@/lib/zod/schemas/misc";
import {
CustomerSchema,
getSalesQuerySchema,
PartnerSchema,
SaleSchema,
} from "@/lib/zod/schemas/partners";
import { parseDateSchema } from "@/lib/zod/schemas/utils";
import { SaleStatus } from "@prisma/client";
import { NextResponse } from "next/server";
import { z } from "zod";

const salesQuerySchema = z
.object({
status: z.nativeEnum(SaleStatus).optional(),
order: z.enum(["asc", "desc"]).default("desc"),
sortBy: z.enum(["createdAt", "amount"]).default("createdAt"),
interval: z.enum(intervals).default("30d"),
start: parseDateSchema.optional(),
end: parseDateSchema.optional(),
payoutId: z.string().optional(),
partnerId: z.string().optional(),
})
.merge(getPaginationQuerySchema({ pageSize: 100 }));

const responseSchema = SaleSchema.and(
z.object({
customer: CustomerSchema,
Expand All @@ -38,7 +23,7 @@ const responseSchema = SaleSchema.and(
export const GET = withWorkspace(
async ({ workspace, params, searchParams }) => {
const { programId } = params;
const parsed = salesQuerySchema.parse(searchParams);
const parsed = getSalesQuerySchema.parse(searchParams);
const { page, pageSize, status, order, sortBy, payoutId, partnerId } =
parsed;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { generateRandomName } from "@/lib/names";
import useSalesCount from "@/lib/swr/use-sales-count";
import {
CustomerSchema,
PartnerSchema,
Expand Down Expand Up @@ -106,6 +107,7 @@ export function SaleTableBusiness({ limit }: { limit?: number }) {
...(payoutId && { payoutId }),
});

const { salesCount } = useSalesCount();
const { data: sales, error } = useSWR<z.infer<typeof salesSchema>[]>(
`/api/programs/${programId}/sales?${searchQuery}`,
fetcher,
Expand Down Expand Up @@ -195,7 +197,7 @@ export function SaleTableBusiness({ limit }: { limit?: number }) {
thClassName: "border-l-0",
tdClassName: "border-l-0",
resourceName: (p) => `sale${p ? "s" : ""}`,
rowCount: sales ? sales.length : 0,
rowCount: salesCount?.[status || "all"] ?? 0,
loading,
error: error ? "Failed to load sales" : undefined,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ export function useSaleFilters(extraSearchParams: Record<string, string>) {

const filters = useMemo(
() => [
{
key: "partnerId",
icon: Users,
label: "Partner",
options: (partners || []).map(({ id, name, logo }) => {
return {
value: id,
label: name,
icon: (
<img
src={logo || `${DICEBEAR_AVATAR_URL}${name}`}
alt={`${name} avatar`}
className="size-4 rounded-full"
/>
),
};
}),
},
{
key: "status",
icon: CircleDotted,
Expand All @@ -43,25 +61,6 @@ export function useSaleFilters(extraSearchParams: Record<string, string>) {
};
}),
},

{
key: "partnerId",
icon: Users,
label: "Partner",
options: (partners || []).map(({ id, name, logo }) => {
return {
value: id,
label: name,
icon: (
<img
src={logo || `${DICEBEAR_AVATAR_URL}${name}`}
alt={`${name} avatar`}
className="size-4 rounded-full"
/>
),
};
}),
},
],
[salesCount, partners],
);
Expand Down
6 changes: 4 additions & 2 deletions apps/web/lib/swr/use-sales-count.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useRouterStuff } from "@dub/ui";
import { fetcher } from "@dub/utils";
import { useParams } from "next/navigation";
import useSWR from "swr";
Expand All @@ -6,10 +7,11 @@ import useWorkspace from "./use-workspace";

export default function useSalesCount() {
const { programId } = useParams();
const { id: workspaceId } = useWorkspace();
const { id: workspaceId } = useWorkspace() as { id: string };
const { getQueryString } = useRouterStuff();

const { data: salesCount, error } = useSWR<SalesCount>(
`/api/programs/${programId}/sales/count?workspaceId=${workspaceId}`,
`/api/programs/${programId}/sales/count?workspaceId=${workspaceId}&${getQueryString()}`,
fetcher,
);

Expand Down
22 changes: 22 additions & 0 deletions apps/web/lib/zod/schemas/partners.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { intervals } from "@/lib/analytics/constants";
import {
CommissionInterval,
CommissionType,
Expand All @@ -10,6 +11,7 @@ import {
import { z } from "zod";
import { LinkSchema } from "./links";
import { getPaginationQuerySchema } from "./misc";
import { parseDateSchema } from "./utils";

export const partnersQuerySchema = z
.object({
Expand Down Expand Up @@ -128,3 +130,23 @@ export const PayoutWithSalesSchema = PayoutSchema.and(
),
}),
);

export const getSalesQuerySchema = z
.object({
status: z.nativeEnum(SaleStatus).optional(),
order: z.enum(["asc", "desc"]).default("desc"),
sortBy: z.enum(["createdAt", "amount"]).default("createdAt"),
interval: z.enum(intervals).default("30d"),
start: parseDateSchema.optional(),
end: parseDateSchema.optional(),
payoutId: z.string().optional(),
partnerId: z.string().optional(),
})
.merge(getPaginationQuerySchema({ pageSize: 100 }));

export const getSalesCountQuerySchema = getSalesQuerySchema.omit({
page: true,
pageSize: true,
order: true,
sortBy: true,
});

0 comments on commit d376467

Please sign in to comment.