Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table cards now paginate independently #80

Merged
merged 21 commits into from
Jun 19, 2024
Merged
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
103 changes: 83 additions & 20 deletions app/analytics/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,9 @@
column: T,
interval: string,
tz?: string,
limit?: number,
page: number = 1,
limit: number = 10,
) {
limit = limit || 10;

const intervalSql = intervalToSql(interval, tz);

const _column = ColumnMappings[column];
Expand All @@ -320,7 +319,7 @@
AND ${ColumnMappings.siteId} = '${siteId}'
GROUP BY ${_column}
ORDER BY count DESC
LIMIT ${limit}`;
LIMIT ${limit * page}`;

type SelectionSet = {
count: number;
Expand All @@ -342,8 +341,16 @@

const responseData =
(await response.json()) as AnalyticsQueryResult<SelectionSet>;

// since CF AE doesn't support OFFSET clauses, we select up to LIMIT and
// then slice that into the individual requested page
const pageData = responseData.data.slice(
limit * (page - 1),
limit * page,
);

resolve(
responseData.data.map((row) => {
pageData.map((row) => {
const key =
row[_column] === "" ? "(none)" : row[_column];
return [key, row["count"]] as const;
Expand All @@ -359,11 +366,9 @@
column: T,
interval: string,
tz?: string,
limit?: number,
page: number = 1,
limit: number = 10,

Check warning on line 370 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L369-L370

Added lines #L369 - L370 were not covered by tests
) {
// defaults to 1 day if not specified
limit = limit || 10;

const intervalSql = intervalToSql(interval, tz);

const _column = ColumnMappings[column];
Expand All @@ -377,7 +382,7 @@
AND ${ColumnMappings.siteId} = '${siteId}'
GROUP BY ${_column}, ${ColumnMappings.newVisitor}, ${ColumnMappings.newSession}
ORDER BY count DESC
LIMIT ${limit}`;
LIMIT ${limit * page}`;

Check warning on line 385 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L385

Added line #L385 was not covered by tests

type SelectionSet = {
readonly count: number;
Expand All @@ -401,7 +406,14 @@
const responseData =
(await response.json()) as AnalyticsQueryResult<SelectionSet>;

const result = responseData.data.reduce(
// since CF AE doesn't support OFFSET clauses, we select up to LIMIT and
// then slice that into the individual requested page
const pageData = responseData.data.slice(
limit * (page - 1),
limit * page,
);

const result = pageData.reduce(

Check warning on line 416 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L409-L416

Added lines #L409 - L416 were not covered by tests
(acc, row) => {
const key =
row[_column] === ""
Expand All @@ -426,12 +438,18 @@
return returnPromise;
}

async getCountByPath(siteId: string, interval: string, tz?: string) {
async getCountByPath(
siteId: string,
interval: string,
tz?: string,
page: number = 1,
) {

Check warning on line 446 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L442-L446

Added lines #L442 - L446 were not covered by tests
const allCountsResultPromise = this.getAllCountsByColumn(
siteId,
"path",
interval,
tz,
page,

Check warning on line 452 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L452

Added line #L452 was not covered by tests
);

return allCountsResultPromise.then((allCountsResult) => {
Expand All @@ -445,32 +463,77 @@
});
}

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,
);

Check warning on line 478 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L467-L478

Added lines #L467 - L478 were not covered by tests
}

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,
);

Check warning on line 493 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L482-L493

Added lines #L482 - L493 were not covered by tests
}

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

Check warning on line 508 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L497-L508

Added lines #L497 - L508 were not covered by tests
}
async getCountByBrowser(siteId: string, interval: string, tz?: string) {
async getCountByBrowser(
siteId: string,
interval: string,
tz?: string,
page: number = 1,
) {

Check warning on line 515 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L511-L515

Added lines #L511 - L515 were not covered by tests
return this.getVisitorCountByColumn(
siteId,
"browserName",
interval,
tz,
page,

Check warning on line 521 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L521

Added line #L521 was not covered by tests
);
}

async getCountByDevice(siteId: string, interval: string, tz?: string) {
async getCountByDevice(
siteId: string,
interval: string,
tz?: string,
page: number = 1,
) {

Check warning on line 530 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L526-L530

Added lines #L526 - L530 were not covered by tests
return this.getVisitorCountByColumn(
siteId,
"deviceModel",
interval,
tz,
page,

Check warning on line 536 in app/analytics/query.ts

View check run for this annotation

Codecov / codecov/patch

app/analytics/query.ts#L536

Added line #L536 was not covered by tests
);
}

Expand Down
65 changes: 65 additions & 0 deletions app/components/PaginatedTableCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useEffect } from "react";
import TableCard from "~/components/TableCard";

import { Card } from "./ui/card";
import PaginationButtons from "./PaginationButtons";

const ReferrerCard = ({
siteId,
interval,
dataFetcher,
columnHeaders,
loaderUrl,
}: {
siteId: string;
interval: string;
dataFetcher: any; // ignore type for now

Check warning on line 16 in app/components/PaginatedTableCard.tsx

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
columnHeaders: string[];
loaderUrl: string;
}) => {
const countsByProperty = dataFetcher.data?.countsByProperty || [];
const page = dataFetcher.data?.page || 1;

useEffect(() => {
if (dataFetcher.state === "idle") {
dataFetcher.load(
`${loaderUrl}?site=${siteId}&interval=${interval}`,
);
}
}, []);

Check warning on line 29 in app/components/PaginatedTableCard.tsx

View workflow job for this annotation

GitHub Actions / test

React Hook useEffect has missing dependencies: 'dataFetcher', 'interval', 'loaderUrl', and 'siteId'. Either include them or remove the dependency array

useEffect(() => {
if (dataFetcher.state === "idle") {
dataFetcher.load(
`${loaderUrl}?site=${siteId}&interval=${interval}`,
);
}
}, [siteId, interval]);

Check warning on line 37 in app/components/PaginatedTableCard.tsx

View workflow job for this annotation

GitHub Actions / test

React Hook useEffect has missing dependencies: 'dataFetcher' and 'loaderUrl'. Either include them or remove the dependency array

function handlePagination(page: number) {
dataFetcher.load(
`${loaderUrl}?site=${siteId}&interval=${interval}&page=${page}`,
);
}

Check warning on line 43 in app/components/PaginatedTableCard.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/PaginatedTableCard.tsx#L40-L43

Added lines #L40 - L43 were not covered by tests

const hasMore = countsByProperty.length === 10;
return (
<Card className={dataFetcher.state === "loading" ? "opacity-60" : ""}>
{countsByProperty ? (
<div className="grid grid-rows-[auto,40px] h-full">
<TableCard
countByProperty={countsByProperty}
columnHeaders={columnHeaders}
/>
<PaginationButtons
page={page}
hasMore={hasMore}
handlePagination={handlePagination}
/>
</div>
) : null}

Check warning on line 60 in app/components/PaginatedTableCard.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/PaginatedTableCard.tsx#L60

Added line #L60 was not covered by tests
</Card>
);
};

export default ReferrerCard;
47 changes: 47 additions & 0 deletions app/components/PaginationButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from "react";

import { ArrowLeft, ArrowRight } from "lucide-react";

interface PaginationButtonsProps {
page: number;
hasMore: boolean;
handlePagination: (page: number) => void;
}

const PaginationButtons: React.FC<PaginationButtonsProps> = ({
page,
hasMore,
handlePagination,
}) => {
return (
<div className="p-2 pr-0 grid grid-cols-[auto,2rem,2rem] text-right">
<div></div>
<button
onClick={() => {
if (page > 1) handlePagination(page - 1);
}}

Check warning on line 22 in app/components/PaginationButtons.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/PaginationButtons.tsx#L21-L22

Added lines #L21 - L22 were not covered by tests
className={
page > 1
? `text-primary hover:cursor-pointer`

Check warning on line 25 in app/components/PaginationButtons.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/PaginationButtons.tsx#L25

Added line #L25 was not covered by tests
: `text-orange-300`
}
>
<ArrowLeft />
</button>
<button
onClick={() => {
if (hasMore) handlePagination(page + 1);
}}

Check warning on line 34 in app/components/PaginationButtons.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/PaginationButtons.tsx#L33-L34

Added lines #L33 - L34 were not covered by tests
className={
hasMore
? "text-primary hover:cursor-pointer"

Check warning on line 37 in app/components/PaginationButtons.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/PaginationButtons.tsx#L37

Added line #L37 was not covered by tests
: "text-orange-300"
}
>
<ArrowRight />
</button>
</div>
);
};

export default PaginationButtons;
78 changes: 37 additions & 41 deletions app/components/TableCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
TableRow,
} from "~/components/ui/table";

import { Card } from "~/components/ui/card";

type CountByProperty = [string, string][];

function calculateCountPercentages(countByProperty: CountByProperty) {
Expand Down Expand Up @@ -40,49 +38,47 @@
? "grid-cols-[minmax(0,1fr),minmax(0,8ch),minmax(0,8ch)]"
: "grid-cols-[minmax(0,1fr),minmax(0,8ch)]";
return (
<Card>
<Table>
<TableHeader>
<TableRow className={`${gridCols}`}>
{(columnHeaders || []).map((header: string, index) => (
<TableHead
key={header}
className={
index === 0
? "text-left"
: "text-right pr-4 pl-0"
}
>
{header}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{(countByProperty || []).map((item, key) => (
<TableRow
key={item[0]}
className={`group [&_td]:last:rounded-b-md ${gridCols}`}
width={barChartPercentages[key]}
<Table>
<TableHeader>
<TableRow className={`${gridCols}`}>
{(columnHeaders || []).map((header: string, index) => (
<TableHead
key={header}
className={
index === 0
? "text-left"
: "text-right pr-4 pl-0"
}
>
<TableCell className="font-medium min-w-48 break-all">
{item[0]}
</TableCell>
{header}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{(countByProperty || []).map((item, key) => (
<TableRow
key={item[0]}
className={`group [&_td]:last:rounded-b-md ${gridCols}`}
width={barChartPercentages[key]}
>
<TableCell className="font-medium min-w-48 break-all">
{item[0]}
</TableCell>

<TableCell className="text-right min-w-16">
{countFormatter.format(item[1] as number)}
</TableCell>

{item.length > 2 && (
<TableCell className="text-right min-w-16">
{countFormatter.format(item[1] as number)}
{countFormatter.format(item[2] as number)}

Check warning on line 75 in app/components/TableCard.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/TableCard.tsx#L75

Added line #L75 was not covered by tests
</TableCell>

{item.length > 2 && (
<TableCell className="text-right min-w-16">
{countFormatter.format(item[2] as number)}
</TableCell>
)}
</TableRow>
))}
</TableBody>
</Table>
</Card>
)}
</TableRow>
))}
</TableBody>
</Table>
);
}

Expand Down
Loading
Loading