Skip to content

Commit dd1c6e7

Browse files
atrakhConvex, Inc.
authored and
Convex, Inc.
committed
dashboard: refactor multiple pages to common package (#33607)
Refactors the logs, history, and files pages to the dashboard-common package so it can be used in the self-hosted dashboard GitOrigin-RevId: c15652984d6812518f474ed3aad90ff1f3935a1f
1 parent 09c2c4a commit dd1c6e7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+248
-234
lines changed

Diff for: npm-packages/common/config/rush/pnpm-lock.yaml

+3-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: npm-packages/dashboard-common/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@radix-ui/react-icons": "~1.3.0",
3030
"@radix-ui/react-tooltip": "~1.0.6",
3131
"@sentry/nextjs": "7.120.2",
32+
"@tanstack/react-table": "~8.20.6",
3233
"acorn": "~8.8.1",
3334
"base64-js": "^1.5.1",
3435
"convex-analytics": "workspace:*",

Diff for: npm-packages/dashboard/src/elements/DateRangePicker.tsx renamed to npm-packages/dashboard-common/src/elements/DateRangePicker.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { CalendarIcon, CheckIcon } from "@radix-ui/react-icons";
44
import { endOfToday, parse, startOfDay, format } from "date-fns";
5-
import { useRouter } from "next/router";
5+
import { NextRouter } from "next/router";
66
import * as React from "react";
77
import { DateRange } from "react-day-picker";
88
import { Button, Calendar } from "dashboard-common";
@@ -108,15 +108,14 @@ export function DateRangePicker({
108108

109109
export const DATE_FORMAT = "yyyy-MM-dd";
110110

111-
export function useDateFilters() {
111+
export function useDateFilters(router: NextRouter) {
112112
// Current day
113113
const maxEndDate = endOfToday();
114114

115115
// A week from current day
116116
const initStartDate = new Date(maxEndDate);
117117
initStartDate.setDate(initStartDate.getDate() - 7);
118118

119-
const router = useRouter();
120119
const startDate = router.query.startDate
121120
? parse(router.query.startDate as string, DATE_FORMAT, new Date())
122121
: initStartDate;
@@ -127,6 +126,7 @@ export function useDateFilters() {
127126
const checkAndSetStartDate = React.useCallback(
128127
async (date: Date) => {
129128
const start = startOfDay(date);
129+
// eslint-disable-next-line no-param-reassign
130130
router.query.startDate = format(start, DATE_FORMAT);
131131
await router.replace({
132132
query: router.query,
@@ -138,6 +138,7 @@ export function useDateFilters() {
138138
const checkAndSetEndDate = React.useCallback(
139139
async (date: Date) => {
140140
const end = startOfDay(date);
141+
// eslint-disable-next-line no-param-reassign
141142
router.query.endDate = format(end, DATE_FORMAT);
142143
await router.replace({
143144
query: router.query,

Diff for: npm-packages/dashboard/src/components/logs/DeploymentEventContent.stories.tsx renamed to npm-packages/dashboard-common/src/elements/DeploymentEventContent.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// TODO: CX-4916 fix the types in this file.
33
import { StoryObj } from "@storybook/react";
44
import { Id } from "system-udfs/convex/_generated/dataModel";
5-
import { DeploymentEventContent } from "elements/DeploymentEventContent";
5+
import { DeploymentEventContent } from "./DeploymentEventContent";
66

77
export default { component: DeploymentEventContent };
88

Diff for: npm-packages/dashboard/src/elements/DeploymentEventContent.tsx renamed to npm-packages/dashboard-common/src/elements/DeploymentEventContent.tsx

+11-25
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,7 @@ import {
55
PlusIcon,
66
UpdateIcon,
77
} from "@radix-ui/react-icons";
8-
import {
9-
TimestampDistance,
10-
Button,
11-
Loading,
12-
ReadonlyCodeDiff,
13-
NENT_APP_PLACEHOLDER,
14-
NentNameOption,
15-
displaySchema,
16-
SchemaJson,
17-
DeploymentAuditLogEvent,
18-
} from "dashboard-common";
19-
import { useMemo } from "react";
8+
import { useContext, useMemo } from "react";
209
import { Infer } from "convex/values";
2110
import { Disclosure } from "@headlessui/react";
2211
import {
@@ -25,9 +14,14 @@ import {
2514
cronDiffType,
2615
schemaDiffType,
2716
} from "system-udfs/convex/tableDefs/deploymentAuditLogTable";
28-
import { TeamMemberLink } from "elements/TeamMemberLink";
29-
import { useGetCloudBackup } from "api/backups";
30-
import { BackupIdentifier } from "elements/BackupIdentifier";
17+
import { SchemaJson, displaySchema } from "../lib/format";
18+
import { DeploymentAuditLogEvent } from "../lib/useDeploymentAuditLog";
19+
import { DeploymentInfoContext } from "../lib/deploymentContext";
20+
import { TimestampDistance } from "./TimestampDistance";
21+
import { Button } from "./Button";
22+
import { ReadonlyCodeDiff } from "./ReadonlyCode";
23+
import { NentNameOption } from "./NentSwitcher";
24+
import { NENT_APP_PLACEHOLDER } from "../lib/useNents";
3125

3226
function useSchemaCode(schema: null | string): string {
3327
return useMemo(() => {
@@ -42,6 +36,7 @@ export function DeploymentEventContent({
4236
}: {
4337
event: DeploymentAuditLogEvent;
4438
}) {
39+
const { TeamMemberLink } = useContext(DeploymentInfoContext);
4540
let body;
4641
switch (event.action) {
4742
case "build_indexes":
@@ -106,6 +101,7 @@ export function DeploymentEventContent({
106101
}
107102

108103
export function ActionText({ event }: { event: DeploymentAuditLogEvent }) {
104+
const { CloudImport } = useContext(DeploymentInfoContext);
109105
switch (event.action) {
110106
case "create_environment_variable":
111107
return (
@@ -214,16 +210,6 @@ export function ActionText({ event }: { event: DeploymentAuditLogEvent }) {
214210
}
215211
}
216212

217-
function CloudImport({ sourceCloudBackupId }: { sourceCloudBackupId: number }) {
218-
const backup = useGetCloudBackup(sourceCloudBackupId);
219-
const ident = backup ? (
220-
<BackupIdentifier backup={backup} />
221-
) : (
222-
<Loading fullHeight={false} className="inline-block h-3 w-80" />
223-
);
224-
return <span>restored from backup: {ident}</span>;
225-
}
226-
227213
function Variable({ variableName }: { variableName: string }) {
228214
return <div className="font-mono font-semibold">{variableName}</div>;
229215
}

Diff for: npm-packages/dashboard/src/components/storage/FileStorageContent.test.tsx renamed to npm-packages/dashboard-common/src/features/files/components/FileStorageContent.test.tsx

+13-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ConvexProvider } from "convex/react";
22
import { act, render, within } from "@testing-library/react";
3-
import { mockConvexReactClient } from "dashboard-common";
43
import userEvent from "@testing-library/user-event";
54
import udfs from "udfs";
65
import { Id } from "system-udfs/convex/_generated/dataModel";
@@ -10,25 +9,9 @@ import {
109
Uploader,
1110
useUploadFiles,
1211
} from "./FileStorageContent";
13-
14-
jest.mock("api/roles", () => ({
15-
useHasProjectAdminPermissions: jest.fn(),
16-
}));
17-
jest.mock("api/profile", () => {});
18-
jest.mock("api/projects", () => ({
19-
useCurrentProject: jest.fn(),
20-
}));
21-
jest.mock("api/teams", () => ({ useCurrentTeam: jest.fn() }));
22-
jest.mock("api/deployments", () => ({ useCurrentDeployment: jest.fn() }));
23-
jest.mock("dashboard-common", () => ({
24-
...jest.requireActual("dashboard-common"),
25-
useNents: () => ({
26-
nents: [],
27-
selectedNent: null,
28-
setSelectedNent: jest.fn(),
29-
}),
30-
NentSwitcher: jest.fn(),
31-
}));
12+
import { mockConvexReactClient } from "../../../lib/mockConvexReactClient";
13+
import { DeploymentInfoContext } from "../../../lib/deploymentContext";
14+
import { deploymentInfo } from "../../../pages/_app";
3215

3316
const mockRouter = jest
3417
.fn()
@@ -72,9 +55,11 @@ describe("FileStorageContent", () => {
7255
const setup = () =>
7356
act(() =>
7457
render(
75-
<ConvexProvider client={mockClient}>
76-
<FileStorageContent />
77-
</ConvexProvider>,
58+
<DeploymentInfoContext.Provider value={deploymentInfo}>
59+
<ConvexProvider client={mockClient}>
60+
<FileStorageContent />
61+
</ConvexProvider>
62+
</DeploymentInfoContext.Provider>,
7863
),
7964
);
8065

@@ -110,9 +95,11 @@ describe("FileStorageContent", () => {
11095
const setup = () =>
11196
act(() =>
11297
render(
113-
<ConvexProvider client={mockClient}>
114-
<UploaderWithLogic />
115-
</ConvexProvider>,
98+
<DeploymentInfoContext.Provider value={deploymentInfo}>
99+
<ConvexProvider client={mockClient}>
100+
<UploaderWithLogic />
101+
</ConvexProvider>
102+
</DeploymentInfoContext.Provider>,
116103
),
117104
);
118105

Diff for: npm-packages/dashboard/src/components/storage/FileStorageContent.tsx renamed to npm-packages/dashboard-common/src/features/files/components/FileStorageContent.tsx

+24-19
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,33 @@ import {
77
UploadIcon,
88
} from "@radix-ui/react-icons";
99
import * as Sentry from "@sentry/nextjs";
10-
import {
11-
Button,
12-
buttonClasses,
13-
Loading,
14-
Tooltip,
15-
Spinner,
16-
NentSwitcher,
17-
useNents,
18-
toast,
19-
formatBytes,
20-
useCopy,
21-
Sheet,
22-
ConfirmationDialog,
23-
EmptySection,
24-
Checkbox,
25-
} from "dashboard-common";
2610
import { useMutation, usePaginatedQuery, useQuery } from "convex/react";
2711
import Link from "next/link";
28-
import React, { useEffect, useMemo, useRef, useState } from "react";
12+
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
2913
import udfs from "udfs";
3014
import { Id } from "system-udfs/convex/_generated/dataModel";
3115
import { FileMetadata } from "system-udfs/convex/_system/frontend/fileStorageV2";
3216
import Image from "next/image";
33-
import { useCurrentDeployment } from "api/deployments";
34-
import { useHasProjectAdminPermissions } from "api/roles";
3517
import {
3618
useReactTable,
3719
getCoreRowModel,
3820
flexRender,
3921
createColumnHelper,
4022
} from "@tanstack/react-table";
23+
import { toast } from "../../../lib/utils";
24+
import { NentSwitcher } from "../../../elements/NentSwitcher";
25+
import { useNents } from "../../../lib/useNents";
26+
import { DeploymentInfoContext } from "../../../lib/deploymentContext";
27+
import { Button, buttonClasses } from "../../../elements/Button";
28+
import { Tooltip } from "../../../elements/Tooltip";
29+
import { Spinner } from "../../../elements/Spinner";
30+
import { Checkbox } from "../../../elements/Checkbox";
31+
import { formatBytes } from "../../../lib/format";
32+
import { Loading } from "../../../elements/Loading";
33+
import { EmptySection } from "../../../elements/EmptySection";
34+
import { Sheet } from "../../../elements/Sheet";
35+
import { ConfirmationDialog } from "../../../elements/ConfirmationDialog";
36+
import { useCopy } from "../../../lib/useCopy";
4137

4238
const columnHelper = createColumnHelper<FileMetadata>();
4339

@@ -154,6 +150,9 @@ export function DeleteFilesButton({
154150
selectedFiles: Id<"_storage">[];
155151
}) {
156152
const [showDeleteModal, setShowDeleteModal] = useState(false);
153+
const { useCurrentDeployment, useHasProjectAdminPermissions } = useContext(
154+
DeploymentInfoContext,
155+
);
157156
const deployment = useCurrentDeployment();
158157
const hasAdminPermissions = useHasProjectAdminPermissions(
159158
deployment?.projectId,
@@ -197,6 +196,9 @@ export function DeleteFilesButton({
197196
}
198197

199198
export function useUploadFiles() {
199+
const { useCurrentDeployment, useHasProjectAdminPermissions } = useContext(
200+
DeploymentInfoContext,
201+
);
200202
const deployment = useCurrentDeployment();
201203
const hasAdminPermissions = useHasProjectAdminPermissions(
202204
deployment?.projectId,
@@ -588,6 +590,9 @@ function Files({
588590
}
589591

590592
function FileActions({ file }: { file: FileMetadata }) {
593+
const { useCurrentDeployment, useHasProjectAdminPermissions } = useContext(
594+
DeploymentInfoContext,
595+
);
591596
const deployment = useCurrentDeployment();
592597
const hasAdminPermissions = useHasProjectAdminPermissions(
593598
deployment?.projectId,

Diff for: npm-packages/dashboard/src/components/history/History.tsx renamed to npm-packages/dashboard-common/src/features/history/components/History.tsx

+19-12
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
1-
import { DateRangePicker, useDateFilters } from "elements/DateRangePicker";
1+
import { endOfDay, endOfToday, startOfDay } from "date-fns";
2+
import Link from "next/link";
3+
import { useRouter } from "next/router";
4+
import { useCallback, useContext, useEffect, useState } from "react";
5+
import { DeploymentEventContent } from "../../../elements/DeploymentEventContent";
6+
import {
7+
DateRangePicker,
8+
useDateFilters,
9+
} from "../../../elements/DateRangePicker";
10+
import { DeploymentInfoContext } from "../../../lib/deploymentContext";
211
import {
3-
Loading,
4-
toast,
5-
Sheet,
612
DeploymentAuditLogEvent,
713
DeploymentAuditLogFilters,
814
usePaginatedDeploymentEvents,
9-
} from "dashboard-common";
10-
import { DeploymentEventContent } from "elements/DeploymentEventContent";
11-
import { endOfDay, endOfToday, startOfDay } from "date-fns";
12-
import { useCurrentTeam, useTeamMembers, useTeamEntitlements } from "api/teams";
13-
import Link from "next/link";
14-
import { useRouter } from "next/router";
15-
import { useCallback, useEffect, useState } from "react";
15+
} from "../../../lib/useDeploymentAuditLog";
16+
import { Loading } from "../../../elements/Loading";
17+
import { toast } from "../../../lib/utils";
18+
import { Sheet } from "../../../elements/Sheet";
1619

1720
const INITIAL_EVENTS_TO_LOAD = 10;
1821
const PAGE_SIZE = 10;
1922
const DISTANCE_FROM_BOTTOM_THRESHOLD_PX = 300;
2023

2124
export function History() {
2225
const router = useRouter();
26+
const { useCurrentTeam, useTeamEntitlements } = useContext(
27+
DeploymentInfoContext,
28+
);
2329
const team = useCurrentTeam();
24-
const { startDate, endDate, setDate } = useDateFilters();
30+
const { startDate, endDate, setDate } = useDateFilters(router);
2531
const entitlements = useTeamEntitlements(team?.id);
2632
const auditLogsEnabled = entitlements?.auditLogsEnabled;
2733

@@ -63,6 +69,7 @@ export function History() {
6369
}
6470

6571
function HistoryList({ filters }: { filters: DeploymentAuditLogFilters }) {
72+
const { useCurrentTeam, useTeamMembers } = useContext(DeploymentInfoContext);
6673
const currentTeam = useCurrentTeam();
6774
const teamMembers = useTeamMembers(currentTeam?.id) ?? [];
6875

Diff for: npm-packages/dashboard/src/components/logs/DeploymentEventListItem.tsx renamed to npm-packages/dashboard-common/src/features/logs/components/DeploymentEventListItem.tsx

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
import { useState } from "react";
2-
import {
3-
Button,
4-
formatDateTime,
5-
DeploymentAuditLogEvent,
6-
DetailPanel,
7-
} from "dashboard-common";
1+
import { useContext, useState } from "react";
82
import classNames from "classnames";
93
import { GearIcon } from "@radix-ui/react-icons";
10-
import { TeamMemberLink } from "elements/TeamMemberLink";
114
import {
125
DeploymentEventContent,
136
ActionText,
14-
} from "elements/DeploymentEventContent";
7+
} from "../../../elements/DeploymentEventContent";
158
import { ITEM_SIZE } from "./LogListItem";
9+
import { Button } from "../../../elements/Button";
10+
import { formatDateTime } from "../../../lib/format";
11+
import { DeploymentAuditLogEvent } from "../../../lib/useDeploymentAuditLog";
12+
import { DetailPanel } from "../../../elements/DetailPanel";
13+
import { DeploymentInfoContext } from "../../../lib/deploymentContext";
1614

1715
export function DeploymentEventListItem({
1816
event,
@@ -21,6 +19,7 @@ export function DeploymentEventListItem({
2119
event: DeploymentAuditLogEvent;
2220
inline?: boolean;
2321
}) {
22+
const { TeamMemberLink } = useContext(DeploymentInfoContext);
2423
const [showDetails, setShowDetails] = useState(false);
2524
const timestamp = formatDateTime(new Date(event._creationTime));
2625

0 commit comments

Comments
 (0)