Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions src/app/(app)/[account_id]/-/analytics/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { Box, Callout, Heading, Link as RadixLink } from "@radix-ui/themes";
import { InfoCircledIcon } from "@radix-ui/react-icons";
import { getPageSession } from "@/lib/api/utils";
import { canManageAccount } from "@/lib/api/authz";
import { accountsTable } from "@/lib/clients/database";
import { ADMIN_DIMENSIONS, type AdminDimension } from "@/lib/clients/analytics";
import { AccountTabs } from "@/components/features/analytics";
import { BreakdownExplorer } from "@/components/features/analytics/BreakdownExplorer";
import { accountAnalyticsUrl } from "@/lib/urls";

interface PageProps {
params: Promise<{ account_id: string }>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}

/** Every dimension except the one the URL already pins. */
const DIMENSIONS = (Object.keys(ADMIN_DIMENSIONS) as AdminDimension[]).filter(
(dim) => dim !== "account",
);

/** Owners/maintainers/admins only; everyone else gets the same 404 an
* unknown path would — including from generateMetadata, which streams
* independently of the page body. */
async function authorizedAccount(account_id: string) {
const account = await accountsTable.fetchById(account_id);
if (!account || !canManageAccount(await getPageSession(), account)) {
notFound();
}
return account;
}

export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const { account_id } = await params;
const account = await authorizedAccount(account_id);
return { title: `${account.name || account_id} — Analytics` };
}

export default async function AccountAnalyticsPage({
params,
searchParams,
}: PageProps) {
const { account_id } = await params;
const account = await authorizedAccount(account_id);

return (
<Box mt="4">
<AccountTabs accountId={account_id} active="analytics" />
<Heading size="7" my="4">
{account.name || account_id}
</Heading>

<BreakdownExplorer
baseUrl={accountAnalyticsUrl(account_id)}
searchParams={await searchParams}
dimensions={DIMENSIONS}
scopeFilters={{ account: account_id }}
notice={
<Callout.Root color="blue" role="status">
<Callout.Icon>
<InfoCircledIcon />
</Callout.Icon>
<Callout.Text>
Analytics is a preview feature. The metrics shown here and who can
access them may change in the near future. Let us know what you
think at{" "}
<RadixLink href="mailto:hello@source.coop">
hello@source.coop
</RadixLink>
.
</Callout.Text>
</Callout.Root>
}
/>
</Box>
);
}
11 changes: 11 additions & 0 deletions src/app/(app)/[account_id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@

import { Metadata } from "next";
import { notFound } from "next/navigation";
import { Box } from "@radix-ui/themes";
import { OrganizationProfilePage } from "@/app/(app)/[account_id]/OrganizationProfilePage";
import { accountsTable, isOrganizationalAccount } from "@/lib/clients/database";
import { IndividualProfilePage } from "./IndividualProfilePage";
import { AccountTabs } from "@/components/features/analytics";
import { getPageSession } from "@/lib/api/utils";
import { canManageAccount } from "@/lib/api/authz";
import {
generateNotFoundMetadata,
generateAccountMetadata,
Expand Down Expand Up @@ -49,6 +53,13 @@ export default async function AccountPage({ params, searchParams }: PageProps) {
return (
<>
<AccountSchemaMetadata account={account} />
{/* Tab strip only for people who can manage the account — the
analytics route 404s everyone else. */}
{canManageAccount(await getPageSession(), account) && (
<Box mt="4">
<AccountTabs accountId={account_id} active="profile" />
</Box>
)}
{isOrganizationalAccount(account) ? (
<OrganizationProfilePage account={account} />
) : (
Expand Down
Loading
Loading