Refactor CdnTable and related components for improved type safety and functionality; implement character creation and editing modals; enhance form handling with autosave; update Sentry configuration for production; clean up unused files and code.
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
export type CdnFile = {
|
||||
id: string;
|
||||
name: string | null;
|
||||
size: string;
|
||||
};
|
||||
|
||||
interface CdnChooserContextType {
|
||||
call(options?: { type?: string }): Promise<string | null>;
|
||||
call(options?: { type?: string }): Promise<CdnFile | null>;
|
||||
}
|
||||
|
||||
export const CdnChooserContext = createContext<CdnChooserContextType | null>(
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import { Check, FileSearch2 } from "lucide-react";
|
||||
import { type ComponentProps, useContext, useRef, useState } from "react";
|
||||
import { CdnTable } from "@/app/(ui)/admin/cdn/table";
|
||||
import {
|
||||
type ComponentProps,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { b2sClient, CdnTable } from "@/app/(ui)/admin/cdn/table";
|
||||
import type { cdn } from "@/lib/db/schema";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "../ui/button";
|
||||
import { Dialog, DialogContent, DialogTitle } from "../ui/dialog";
|
||||
import { Kbd } from "../ui/kbd";
|
||||
import { Spinner } from "../ui/spinner";
|
||||
import { listFiles } from "./api";
|
||||
import { CdnChooserContext } from "./context";
|
||||
import { CdnChooserContext, type CdnFile } from "./context";
|
||||
|
||||
export function CdnChooserProvider({
|
||||
children,
|
||||
@@ -20,9 +27,9 @@ export function CdnChooserProvider({
|
||||
const [files, setFiles] = useState<
|
||||
Omit<typeof cdn.$inferSelect, "data">[] | null
|
||||
>(null);
|
||||
const callback = useRef<((value: string | null) => void) | null>(null);
|
||||
const callback = useRef<((value: CdnFile | null) => void) | null>(null);
|
||||
|
||||
async function call(options?: { type?: string }): Promise<string | null> {
|
||||
async function call(options?: { type?: string }): Promise<CdnFile | null> {
|
||||
setOpen(true);
|
||||
const list = await listFiles().catch(() => []);
|
||||
setFiles(
|
||||
@@ -30,7 +37,7 @@ export function CdnChooserProvider({
|
||||
? list.filter((file) => file.type.startsWith(options.type!))
|
||||
: list,
|
||||
);
|
||||
return new Promise<string | null>((resolve) => {
|
||||
return new Promise<CdnFile | null>((resolve) => {
|
||||
callback.current = resolve;
|
||||
}).finally(() => setOpen(false));
|
||||
}
|
||||
@@ -71,19 +78,39 @@ export function CdnChooser({
|
||||
type,
|
||||
children,
|
||||
defaultValue,
|
||||
value: externalValue,
|
||||
...props
|
||||
}: ComponentProps<"input"> & {
|
||||
onChange?: (value: string) => void;
|
||||
type?: string;
|
||||
value?: string;
|
||||
}) {
|
||||
const [value, setValue] = useState<string>(`${defaultValue || ""}`);
|
||||
const [meta, setMeta] = useState<CdnFile>();
|
||||
const chooser = useContext(CdnChooserContext);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: first run only
|
||||
useEffect(() => {
|
||||
setValue(externalValue || "");
|
||||
const newVal = externalValue || defaultValue || "";
|
||||
if (!newVal) {
|
||||
if (meta) setMeta(undefined);
|
||||
return;
|
||||
}
|
||||
(async () => {
|
||||
const fileList = await listFiles();
|
||||
const file = fileList.find((f) => f.id === newVal);
|
||||
if (!file) return setValue("");
|
||||
setMeta(file);
|
||||
})();
|
||||
}, [externalValue]);
|
||||
|
||||
async function choose() {
|
||||
const res = await chooser?.call({ type });
|
||||
if (res) {
|
||||
setValue(res);
|
||||
onChange?.(res);
|
||||
setValue(res.id);
|
||||
onChange?.(res.id);
|
||||
setMeta(res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,15 +118,21 @@ export function CdnChooser({
|
||||
<>
|
||||
<Button
|
||||
onClick={choose}
|
||||
className={cn(
|
||||
"min-w-50 justify-between",
|
||||
value && "bg-emerald-900!",
|
||||
className,
|
||||
)}
|
||||
size="sm"
|
||||
className={cn("min-w-50 justify-between", className)}
|
||||
// size="sm"
|
||||
variant="outline"
|
||||
type="button"
|
||||
>
|
||||
{children || "Select File"}
|
||||
{children ||
|
||||
(meta ? (
|
||||
<span>
|
||||
{meta.name} <Kbd>{b2sClient(Number(meta.size))}</Kbd>
|
||||
</span>
|
||||
) : value ? (
|
||||
"File Selected"
|
||||
) : (
|
||||
"Select File"
|
||||
))}
|
||||
{value ? <Check /> : <FileSearch2 />}
|
||||
</Button>
|
||||
<input value={value} hidden {...props} readOnly />
|
||||
|
||||
Reference in New Issue
Block a user