Skip to content

Commit

Permalink
Convert everything to PaginatedTableCards
Browse files Browse the repository at this point in the history
  • Loading branch information
benvinegar committed Jun 18, 2024
1 parent 2eab93a commit 3652ecc
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 51 deletions.
46 changes: 40 additions & 6 deletions app/analytics/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,12 +470,34 @@ export class AnalyticsEngineAPI {
});
}

async getCountByUserAgent(siteId: string, interval: string, tz?: string) {
return this.getVisitorCountByColumn(siteId, "userAgent", interval, tz);
async getCountByUserAgent(
siteId: string,
interval: string,
tz?: string,
page: number = 1,
) {
return this.getVisitorCountByColumn(
siteId,
"userAgent",
interval,
tz,
page,
);
}

async getCountByCountry(siteId: string, interval: string, tz?: string) {
return this.getVisitorCountByColumn(siteId, "country", interval, tz);
async getCountByCountry(
siteId: string,
interval: string,
tz?: string,
page: number = 1,
) {
return this.getVisitorCountByColumn(
siteId,
"country",
interval,
tz,
page,
);
}

async getCountByReferrer(
Expand All @@ -492,21 +514,33 @@ export class AnalyticsEngineAPI {
page,
);
}
async getCountByBrowser(siteId: string, interval: string, tz?: string) {
async getCountByBrowser(
siteId: string,
interval: string,
tz?: string,
page: number = 1,
) {
return this.getVisitorCountByColumn(
siteId,
"browserName",
interval,
tz,
page,
);
}

async getCountByDevice(siteId: string, interval: string, tz?: string) {
async getCountByDevice(
siteId: string,
interval: string,
tz?: string,
page: number = 1,
) {
return this.getVisitorCountByColumn(
siteId,
"deviceModel",
interval,
tz,
page,
);
}

Expand Down
1 change: 0 additions & 1 deletion app/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ export function paramsFromUrl(url: string) {
searchParams.forEach((value, key) => {
params[key] = value;
});
console.log(params);
return params;
}
56 changes: 13 additions & 43 deletions app/routes/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { AnalyticsEngineAPI } from "../analytics/query";
import TableCard from "~/components/TableCard";

Check failure on line 20 in app/routes/dashboard.tsx

View workflow job for this annotation

GitHub Actions / test

'TableCard' is defined but never used
import { ReferrerCard } from "./resources.referrer";
import { PathsCard } from "./resources.paths";
import { BrowserCard } from "./resources.browser";
import { CountryCard } from "./resources.country";
import { DeviceCard } from "./resources.device";

import TimeSeriesChart from "~/components/TimeSeriesChart";
import dayjs from "dayjs";
Expand Down Expand Up @@ -177,38 +180,13 @@ export const loader = async ({ context, request }: LoaderFunctionArgs) => {
throw new Error("Failed to fetch data from Analytics Engine");
}

// normalize country codes to country names
// NOTE: this must be done ONLY on server otherwise hydration mismatches
// can occur because Intl.DisplayNames produces different results
// in different browsers (see )
out.countByCountry = convertCountryCodesToNames(out.countByCountry);

out.pagination = {
referrer: Number(url.searchParams.get("referrer_page") || 1),
};

return json(out);
};

function convertCountryCodesToNames(
countByCountry: [string, number][],
): [string, number][] {
const regionNames = new Intl.DisplayNames(["en"], { type: "region" });
return countByCountry.map((countByBrowserRow) => {
let countryName;
try {
// throws an exception if country code isn't valid
// use try/catch to be defensive and not explode if an invalid
// country code gets insrted into Analytics Engine
countryName = regionNames.of(countByBrowserRow[0])!; // "United States"
} catch (err) {
countryName = "(unknown)";
}
const count = countByBrowserRow[1];
return [countryName, count];
});
}

export default function Dashboard() {
const [, setSearchParams] = useSearchParams();

Expand Down Expand Up @@ -337,25 +315,17 @@ export default function Dashboard() {
/>
</div>
<div className="grid md:grid-cols-3 gap-4 mb-4">
<Card>
<TableCard
countByProperty={data.countByBrowser}
columnHeaders={["Browser", "Visitors"]}
/>
</Card>
<Card>
<TableCard
countByProperty={data.countByCountry}
columnHeaders={["Country", "Visitors"]}
/>
</Card>
<BrowserCard
siteId={data.siteId}
interval={data.interval}
/>

<Card>
<TableCard
countByProperty={data.countByDevice}
columnHeaders={["Device", "Visitors"]}
></TableCard>
</Card>
<CountryCard
siteId={data.siteId}
interval={data.interval}
/>

<DeviceCard siteId={data.siteId} interval={data.interval} />
</div>
</div>
</div>
Expand Down
42 changes: 42 additions & 0 deletions app/routes/resources.browser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useFetcher } from "@remix-run/react";

import type { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { json } from "@remix-run/cloudflare";

import { paramsFromUrl } from "~/lib/utils";
import PaginatedTableCard from "~/components/PaginatedTableCard";

export async function loader({ context, request }: LoaderFunctionArgs) {
const { analyticsEngine } = context;

const { interval, site, page = 1 } = paramsFromUrl(request.url);
const tz = context.requestTimezone as string;

return json({
countsByProperty: await analyticsEngine.getCountByBrowser(
site,
interval,
tz,
Number(page),
),
page: Number(page),
});
}

export const BrowserCard = ({
siteId,
interval,
}: {
siteId: string;
interval: string;
}) => {
return (
<PaginatedTableCard
siteId={siteId}
interval={interval}
columnHeaders={["Browser", "Visitors"]}
dataFetcher={useFetcher<typeof loader>()}
loaderUrl="/resources/browser"
/>
);
};
69 changes: 69 additions & 0 deletions app/routes/resources.country.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useFetcher } from "@remix-run/react";

import type { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { json } from "@remix-run/cloudflare";

import { paramsFromUrl } from "~/lib/utils";
import PaginatedTableCard from "~/components/PaginatedTableCard";

function convertCountryCodesToNames(
countByCountry: [string, number][],
): [string, number][] {
const regionNames = new Intl.DisplayNames(["en"], { type: "region" });
return countByCountry.map((countByBrowserRow) => {
let countryName;
try {
// throws an exception if country code isn't valid
// use try/catch to be defensive and not explode if an invalid
// country code gets insrted into Analytics Engine
countryName = regionNames.of(countByBrowserRow[0])!; // "United States"
} catch (err) {
countryName = "(unknown)";
}
const count = countByBrowserRow[1];
return [countryName, count];
});
}

export async function loader({ context, request }: LoaderFunctionArgs) {
const { analyticsEngine } = context;

const { interval, site, page = 1 } = paramsFromUrl(request.url);
const tz = context.requestTimezone as string;

const countByCountry = await analyticsEngine.getCountByCountry(
site,
interval,
tz,
Number(page),
);

// normalize country codes to country names
// NOTE: this must be done ONLY on server otherwise hydration mismatches
// can occur because Intl.DisplayNames produces different results
// in different browsers (see )
const normalizedCountByCountry = convertCountryCodesToNames(countByCountry);

return json({
countsByProperty: normalizedCountByCountry,
page: Number(page),
});
}

export const CountryCard = ({
siteId,
interval,
}: {
siteId: string;
interval: string;
}) => {
return (
<PaginatedTableCard
siteId={siteId}
interval={interval}
columnHeaders={["Browser", "Visitors"]}
dataFetcher={useFetcher<typeof loader>()}
loaderUrl="/resources/country"
/>
);
};
42 changes: 42 additions & 0 deletions app/routes/resources.device.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useFetcher } from "@remix-run/react";

import type { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { json } from "@remix-run/cloudflare";

import { paramsFromUrl } from "~/lib/utils";
import PaginatedTableCard from "~/components/PaginatedTableCard";

export async function loader({ context, request }: LoaderFunctionArgs) {
const { analyticsEngine } = context;

const { interval, site, page = 1 } = paramsFromUrl(request.url);
const tz = context.requestTimezone as string;

return json({
countsByProperty: await analyticsEngine.getCountByDevice(
site,
interval,
tz,
Number(page),
),
page: Number(page),
});
}

export const DeviceCard = ({
siteId,
interval,
}: {
siteId: string;
interval: string;
}) => {
return (
<PaginatedTableCard
siteId={siteId}
interval={interval}
columnHeaders={["Path", "Visitors"]}
dataFetcher={useFetcher<typeof loader>()}
loaderUrl="/resources/device"
/>
);
};
2 changes: 1 addition & 1 deletion app/routes/resources.paths.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const PathsCard = ({
<PaginatedTableCard
siteId={siteId}
interval={interval}
columnHeaders={["Referrer", "Views", "Visitors"]}
columnHeaders={["Path", "Visitors", "Views"]}
dataFetcher={useFetcher<typeof loader>()}
loaderUrl="/resources/paths"
/>
Expand Down

0 comments on commit 3652ecc

Please sign in to comment.