refactor: pr feedbacks
This commit is contained in:
parent
caeb867554
commit
9422815843
10 changed files with 55 additions and 66 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { useMutation, useQuery, useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { Copy, Plus, Trash2 } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "~/client/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card";
|
||||
|
|
@ -38,9 +38,9 @@ type MirrorAssignment = {
|
|||
lastCopyError: string | null;
|
||||
};
|
||||
|
||||
export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, repositories, initialData }: Props) => {
|
||||
const buildAssignments = (mirrors: GetScheduleMirrorsResponse) => {
|
||||
const map = new Map<string, MirrorAssignment>();
|
||||
for (const mirror of initialData) {
|
||||
for (const mirror of mirrors) {
|
||||
map.set(mirror.repositoryId, {
|
||||
repositoryId: mirror.repositoryId,
|
||||
enabled: mirror.enabled,
|
||||
|
|
@ -49,8 +49,11 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
|||
lastCopyError: mirror.lastCopyError,
|
||||
});
|
||||
}
|
||||
return map;
|
||||
};
|
||||
|
||||
const [assignments, setAssignments] = useState<Map<string, MirrorAssignment>>(map);
|
||||
export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, repositories, initialData }: Props) => {
|
||||
const [assignments, setAssignments] = useState<Map<string, MirrorAssignment>>(() => buildAssignments(initialData));
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [isAddingNew, setIsAddingNew] = useState(false);
|
||||
|
||||
|
|
@ -58,6 +61,12 @@ export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, reposit
|
|||
...getScheduleMirrorsOptions({ path: { scheduleId: scheduleId.toString() } }),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasChanges) {
|
||||
setAssignments(buildAssignments(currentMirrors));
|
||||
}
|
||||
}, [currentMirrors, hasChanges]);
|
||||
|
||||
const { data: compatibility } = useQuery({
|
||||
...getMirrorCompatibilityOptions({ path: { scheduleId: scheduleId.toString() } }),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export const clientLoader = async () => {
|
|||
};
|
||||
|
||||
export function BackupsPage() {
|
||||
const { data: schedules, isLoading } = useSuspenseQuery({
|
||||
const { data: schedules } = useSuspenseQuery({
|
||||
...listBackupSchedulesOptions(),
|
||||
});
|
||||
|
||||
|
|
@ -69,14 +69,6 @@ export function BackupsPage() {
|
|||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<p className="text-muted-foreground">Loading backup schedules...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!schedules || schedules.length === 0) {
|
||||
return (
|
||||
<EmptyState
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export function CreateBackupPage() {
|
|||
const formId = useId();
|
||||
const [selectedVolumeId, setSelectedVolumeId] = useState<number | undefined>();
|
||||
|
||||
const { data: volumesData, isLoading: loadingVolumes } = useSuspenseQuery({
|
||||
const { data: volumesData } = useSuspenseQuery({
|
||||
...listVolumesOptions(),
|
||||
});
|
||||
|
||||
|
|
@ -79,14 +79,6 @@ export function CreateBackupPage() {
|
|||
|
||||
const selectedVolume = volumesData.find((v) => v.id === selectedVolumeId);
|
||||
|
||||
if (loadingVolumes) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<p className="text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!volumesData.length) {
|
||||
return (
|
||||
<EmptyState
|
||||
|
|
|
|||
|
|
@ -122,32 +122,34 @@ export function SnapshotDetailsPage({ repositoryId, snapshotId }: { repositoryId
|
|||
<span className="text-muted-foreground">Time:</span>
|
||||
<p>{formatDateTime(data.time)}</p>
|
||||
</div>
|
||||
<>
|
||||
<div>
|
||||
<span className="text-muted-foreground">Backup Schedule:</span>
|
||||
<p>
|
||||
<Link
|
||||
to="/backups/$scheduleId"
|
||||
className="text-primary hover:underline"
|
||||
params={{ scheduleId: backupSchedule?.shortId || "" }}
|
||||
>
|
||||
{backupSchedule?.name}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">Volume:</span>
|
||||
<p>
|
||||
<Link
|
||||
to={`/volumes/$volumeId`}
|
||||
className="text-primary hover:underline"
|
||||
params={{ volumeId: backupSchedule?.volume.shortId || "" }}
|
||||
>
|
||||
{backupSchedule?.volume.name}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
{backupSchedule && (
|
||||
<>
|
||||
<div>
|
||||
<span className="text-muted-foreground">Backup Schedule:</span>
|
||||
<p>
|
||||
<Link
|
||||
to="/backups/$scheduleId"
|
||||
className="text-primary hover:underline"
|
||||
params={{ scheduleId: backupSchedule.shortId }}
|
||||
>
|
||||
{backupSchedule?.name}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">Volume:</span>
|
||||
<p>
|
||||
<Link
|
||||
to={`/volumes/$volumeId`}
|
||||
className="text-primary hover:underline"
|
||||
params={{ volumeId: backupSchedule.volume.shortId }}
|
||||
>
|
||||
{backupSchedule?.volume.name}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="col-span-2">
|
||||
<span className="text-muted-foreground">Paths:</span>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export const authMiddleware = createMiddleware().server(async ({ next, request }
|
|||
const isAuthRoute = ["/login", "/onboarding"].includes(new URL(request.url).pathname);
|
||||
|
||||
if (!session?.user?.id && !isAuthRoute) {
|
||||
const status = await getStatus();
|
||||
const status = await getStatus().catch(() => ({ data: { hasUsers: false } }));
|
||||
if (!status.data?.hasUsers) {
|
||||
throw redirect({ to: "/onboarding" });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,16 @@ import { createFileRoute } from "@tanstack/react-router";
|
|||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { getRequestHeaders } from "@tanstack/react-start/server";
|
||||
import { Layout } from "~/client/components/layout";
|
||||
import type { AppContext } from "~/context";
|
||||
import { authMiddleware } from "~/middleware/auth";
|
||||
import { auth } from "~/server/lib/auth";
|
||||
import { authService } from "~/server/modules/auth/auth.service";
|
||||
|
||||
export const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
|
||||
const headers = getRequestHeaders();
|
||||
const session = await auth.api.getSession({ headers });
|
||||
const hasUsers = await authService.hasUsers();
|
||||
|
||||
return { user: session?.user, hasUsers: true };
|
||||
return { user: session?.user ?? null, hasUsers };
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/(dashboard)")({
|
||||
|
|
@ -21,7 +22,7 @@ export const Route = createFileRoute("/(dashboard)")({
|
|||
},
|
||||
loader: async () => {
|
||||
const authContext = await fetchUser();
|
||||
return authContext as AppContext;
|
||||
return authContext;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ const envSchema = type({
|
|||
appVersion: s.APP_VERSION,
|
||||
trustedOrigins: s.TRUSTED_ORIGINS?.split(",")
|
||||
.map((origin) => origin.trim())
|
||||
.filter(Boolean)
|
||||
.concat(s.BASE_URL) ?? [s.BASE_URL],
|
||||
disableRateLimiting: s.DISABLE_RATE_LIMITING === "true",
|
||||
appSecret: s.APP_SECRET,
|
||||
|
|
|
|||
5
bun.lock
5
bun.lock
|
|
@ -99,7 +99,6 @@
|
|||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vite-bundle-analyzer": "^1.2.3",
|
||||
"vite-plugin-babel": "^1.4.1",
|
||||
"vite-tsconfig-paths": "^6.0.5",
|
||||
"wait-for-expect": "^4.0.0",
|
||||
},
|
||||
|
|
@ -1349,7 +1348,7 @@
|
|||
|
||||
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
|
||||
|
||||
"nf3": ["nf3@0.3.9", "", {}, "sha512-vgmoL6pUXpKXx+WepG4H1xc2nf1N/5vKgU9QerryuSFIyef8EDkj7esYcIgqMe/mE6nGsy6M6b8wfssEBQizVQ=="],
|
||||
"nf3": ["nf3@0.3.10", "", {}, "sha512-UlqmHkZiHGgSkRj17yrOXEsSu5ECvtlJ3Xm1W5WsWrTKgu9m7OjrMZh9H/ME2LcWrTlMD0/vmmNVpyBG4yRdGg=="],
|
||||
|
||||
"nitro": ["nitro@3.0.1-alpha.2", "", { "dependencies": { "consola": "^3.4.2", "crossws": "^0.4.3", "db0": "^0.3.4", "h3": "^2.0.1-rc.11", "jiti": "^2.6.1", "nf3": "^0.3.5", "ofetch": "^2.0.0-alpha.3", "ohash": "^2.0.11", "oxc-minify": "^0.110.0", "oxc-transform": "^0.110.0", "srvx": "^0.10.1", "undici": "^7.18.2", "unenv": "^2.0.0-rc.24", "unstorage": "^2.0.0-alpha.5" }, "peerDependencies": { "rolldown": ">=1.0.0-beta.0", "rollup": "^4", "vite": "^7 || ^8 || >=8.0.0-0", "xml2js": "^0.6.2" }, "optionalPeers": ["rolldown", "rollup", "vite", "xml2js"], "bin": { "nitro": "dist/cli/index.mjs" } }, "sha512-YviDY5J/trS821qQ1fpJtpXWIdPYiOizC/meHavlm1Hfuhx//H+Egd1+4C5SegJRgtWMnRPW9n//6Woaw81cTQ=="],
|
||||
|
||||
|
|
@ -1621,8 +1620,6 @@
|
|||
|
||||
"vite-bundle-analyzer": ["vite-bundle-analyzer@1.3.6", "", { "bin": { "analyze": "dist/bin.js" } }, "sha512-elFXkF9oGy4CJEeOdP1DSEHRDKWfmaEDfT9/GuF4jmyfmUF6nC//iN+ythqMEVvrtchOEHGKLumZwnu1NjoI0w=="],
|
||||
|
||||
"vite-plugin-babel": ["vite-plugin-babel@1.4.1", "", { "peerDependencies": { "@babel/core": "^7.0.0", "vite": "^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-quO+viHGSv1cjbfhbeiMZ7SZpo8P29NiUh9LJfKhpmIDwy0THRiTRUbanBbkNcZcSyHFgp1n7TByd1C2kanqLQ=="],
|
||||
|
||||
"vite-tsconfig-paths": ["vite-tsconfig-paths@6.0.5", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-f/WvY6ekHykUF1rWJUAbCU7iS/5QYDIugwpqJA+ttwKbxSbzNlqlE8vZSrsnxNQciUW+z6lvhlXMaEyZn9MSig=="],
|
||||
|
||||
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@
|
|||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vite-bundle-analyzer": "^1.2.3",
|
||||
"vite-plugin-babel": "^1.4.1",
|
||||
"vite-tsconfig-paths": "^6.0.5",
|
||||
"wait-for-expect": "^4.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,18 +4,10 @@ import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|||
import { nitro } from "nitro/vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import viteReact from "@vitejs/plugin-react";
|
||||
import babel from "vite-plugin-babel";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tsconfigPaths(),
|
||||
babel({
|
||||
filter: /\.[jt]sx?$/,
|
||||
babelConfig: {
|
||||
presets: ["@babel/preset-typescript"],
|
||||
plugins: [["babel-plugin-react-compiler"]],
|
||||
},
|
||||
}),
|
||||
tanstackStart({
|
||||
srcDirectory: "app",
|
||||
router: {
|
||||
|
|
@ -23,7 +15,11 @@ export default defineConfig({
|
|||
},
|
||||
}),
|
||||
nitro({ preset: "bun" }),
|
||||
viteReact(),
|
||||
viteReact({
|
||||
babel: {
|
||||
plugins: ["babel-plugin-react-compiler"],
|
||||
},
|
||||
}),
|
||||
tailwindcss(),
|
||||
],
|
||||
build: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue