Small changes to label form

This commit is contained in:
Ryan Pandya 2024-11-18 10:04:25 -08:00
parent b103b5418b
commit 0802e36796
8 changed files with 173 additions and 150 deletions

View File

@ -0,0 +1,3 @@
2024-11-18T16:54:25.722Z error: Authentication error. User: "ryan@pandu.ski", Message: "no such table: user", IP-Address: "::ffff:127.0.0.1"
2024-11-18T16:54:31.817Z error: Authentication error. User: "ryan@ryanpandya.com", Message: "no such table: user", IP-Address: "::ffff:127.0.0.1"
2024-11-18T16:54:43.805Z error: Authentication error. User: "ryan@ryanpandya.com", Message: "no such table: user", IP-Address: "::ffff:127.0.0.1"

View File

@ -34,6 +34,7 @@ import { useForm } from "react-hook-form";
import { z } from "zod";
import { zLabelSchema } from "@lifetracker/shared/types/labels";
import { Textarea } from "@/components/ui/textarea";
type CreateLabelSchema = z.infer<typeof zLabelSchema>;
@ -46,13 +47,6 @@ export default function AddLabelDialog({
const [isOpen, onOpenChange] = useState(false);
const form = useForm<CreateLabelSchema>({
resolver: zodResolver(zLabelSchema),
defaultValues: {
id: "69",
name: "Fuckdicks",
code: 420,
description: "This shit sucks",
color: "#004400",
},
});
const { mutate, isPending } = api.labels.createLabel.useMutation({
onSuccess: () => {
@ -87,17 +81,36 @@ export default function AddLabelDialog({
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Add User</DialogTitle>
<DialogTitle>Create Label</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit((val) => mutate(val))}>
<div className="flex w-full flex-col space-y-2">
<div style={{ display: "grid", gridTemplateColumns: "5em 1fr 75px", gap: "10px" }}>
<FormField
control={form.control}
name="code"
render={({ field }) => (
<FormItem>
{/* <FormLabel>Code</FormLabel> */}
<FormControl>
<Input
type="text"
placeholder="Code"
{...field}
className="w-full rounded border p-2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
{/* <FormLabel>Name</FormLabel> */}
<FormControl>
<Input
type="text"
@ -112,14 +125,14 @@ export default function AddLabelDialog({
/>
<FormField
control={form.control}
name="code"
name="color"
render={({ field }) => (
<FormItem>
<FormLabel>Code</FormLabel>
{/* <FormLabel>Color</FormLabel> */}
<FormControl>
<Input
type="number"
placeholder="Code"
type="text"
placeholder="Color"
{...field}
className="w-full rounded border p-2"
/>
@ -128,15 +141,15 @@ export default function AddLabelDialog({
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
{/* <FormLabel>Description</FormLabel> */}
<FormControl>
<Input
type="text"
<Textarea
placeholder="Description"
{...field}
className="w-full rounded border p-2"
@ -146,24 +159,6 @@ export default function AddLabelDialog({
</FormItem>
)}
/>
<FormField
control={form.control}
name="color"
render={({ field }) => (
<FormItem>
<FormLabel>Color, hope you like hex codes</FormLabel>
<FormControl>
<Input
type="text"
placeholder="Color"
{...field}
className="w-full rounded border p-2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter className="sm:justify-end">
<DialogClose asChild>
<Button type="button" variant="secondary">

View File

@ -3,26 +3,26 @@ import { ActionButton } from "@/components/ui/action-button";
import ActionConfirmingDialog from "@/components/ui/action-confirming-dialog";
import { toast } from "@/components/ui/use-toast";
import { useDeleteTag } from "@hoarder/shared-react/hooks/tags";
import { useDeleteLabel } from "@lifetracker/shared-react/hooks/labels";
export default function DeleteTagConfirmationDialog({
tag,
export default function DeleteLabelConfirmationDialog({
label,
open,
setOpen,
}: {
tag: { id: string; name: string };
label: { id: string; code: number; name: string };
open?: boolean;
setOpen?: (v: boolean) => void;
}) {
const currentPath = usePathname();
const router = useRouter();
const { mutate: deleteTag, isPending } = useDeleteTag({
const { mutate: deleteTag, isPending } = useDeleteLabel({
onSuccess: () => {
toast({
description: `Tag "${tag.name}" has been deleted!`,
description: `Label "${label.name}" has been deleted!`,
});
if (currentPath.includes(tag.id)) {
router.push("/dashboard/tags");
if (currentPath.includes(label.code)) {
router.push("/dashboard/labels");
}
},
onError: () => {
@ -36,8 +36,8 @@ export default function DeleteTagConfirmationDialog({
<ActionConfirmingDialog
open={open}
setOpen={setOpen}
title={`Delete ${tag.name}?`}
description={`Are you sure you want to delete the tag "${tag.name}"?`}
title={`Delete ${label.name}?`}
description={`Are you sure you want to delete the label "${label.name}"?`}
actionButton={(setDialogOpen) => (
<ActionButton
type="button"
@ -45,7 +45,7 @@ export default function DeleteTagConfirmationDialog({
loading={isPending}
onClick={() =>
deleteTag(
{ tagId: tag.id },
{ labelId: label.id },
{ onSuccess: () => setDialogOpen(false) },
)
}

View File

@ -17,12 +17,30 @@ import { Check, KeyRound, Pencil, Trash, FilePlus, X } from "lucide-react";
import { useSession } from "next-auth/react";
import AddLabelDialog from "./AddLabelDialog";
import EditLabelDialog from "./EditLabelDialog";
import DeleteLabelConfirmationDialog from "./DeleteLabelConfirmationDialog";
export default function LabelsView() {
const { data: session } = useSession();
const { data: labels } = api.labels.list.useQuery();
const { data: labelStats } = api.labels.labelStats.useQuery();
const invalidateLabelList = api.useUtils().labels.list.invalidate;
const { mutate: deleteLabel, isPending: isDeletionPending } =
api.labels.delete.useMutation({
onSuccess: () => {
toast({
description: "Label deleted",
});
invalidateLabelList();
},
onError: (e) => {
toast({
variant: "destructive",
description: `Something went wrong: ${e.message}`,
});
},
});
const LabelsTable = ({ labels }) => (
<Table>
<TableHeader className="bg-gray-200">

View File

@ -7,17 +7,19 @@ import path from "path";
import dbConfig from "./drizzle.config";
console.log("dbURL: ", dbConfig.dbCredentials.url);
const sqlite = new Database(dbConfig.dbCredentials.url);
export const db = drizzle(sqlite, {
schema,
// logger: true
logger: true
});
export function getInMemoryDB(runMigrations: boolean) {
const mem = new Database(":memory:");
const db = drizzle(mem, { schema, logger: true });
if (runMigrations) {
migrate(db, { migrationsFolder: path.resolve(__dirname, "./drizzle") });
migrate(db, { migrationsFolder: path.resolve(__dirname, "./migrations") });
}
return db;
}

View File

@ -0,0 +1,50 @@
import { api } from "../trpc";
export function useUpdateLabel(
...opts: Parameters<typeof api.labels.update.useMutation>
) {
const apiUtils = api.useUtils();
return api.tags.update.useMutation({
...opts[0],
onSuccess: (res, req, meta) => {
apiUtils.labels.list.invalidate();
apiUtils.labels.get.invalidate({ labelId: res.id });
// apiUtils.bookmarks.getBookmarks.invalidate({
// labelId: res.id;
// TODO: Maybe we can only look at the cache and invalidate only affected bookmarks
// apiUtils.bookmarks.getBookmark.invalidate();
return opts[0]?.onSuccess?.(res, req, meta);
},
});
}
export function useDeleteLabel(
...opts: Parameters<typeof api.labels.delete.useMutation>
) {
const apiUtils = api.useUtils();
return api.labels.delete.useMutation({
...opts[0],
onSuccess: (res, req, meta) => {
apiUtils.labels.list.invalidate();
// apiUtils.bookmarks.getBookmark.invalidate();
return opts[0]?.onSuccess?.(res, req, meta);
},
});
}
export function useDeleteUnusedTags(
...opts: Parameters<typeof api.tags.deleteUnused.useMutation>
) {
const apiUtils = api.useUtils();
return api.tags.deleteUnused.useMutation({
...opts[0],
onSuccess: (res, req, meta) => {
apiUtils.tags.list.invalidate();
return opts[0]?.onSuccess?.(res, req, meta);
},
});
}

View File

@ -1,69 +0,0 @@
import { api } from "../trpc";
export function useUpdateTag(
...opts: Parameters<typeof api.tags.update.useMutation>
) {
const apiUtils = api.useUtils();
return api.tags.update.useMutation({
...opts[0],
onSuccess: (res, req, meta) => {
apiUtils.tags.list.invalidate();
apiUtils.tags.get.invalidate({ tagId: res.id });
apiUtils.bookmarks.getBookmarks.invalidate({ tagId: res.id });
// TODO: Maybe we can only look at the cache and invalidate only affected bookmarks
apiUtils.bookmarks.getBookmark.invalidate();
return opts[0]?.onSuccess?.(res, req, meta);
},
});
}
export function useMergeTag(
...opts: Parameters<typeof api.tags.merge.useMutation>
) {
const apiUtils = api.useUtils();
return api.tags.merge.useMutation({
...opts[0],
onSuccess: (res, req, meta) => {
apiUtils.tags.list.invalidate();
[res.mergedIntoTagId, ...res.deletedTags].forEach((tagId) => {
apiUtils.tags.get.invalidate({ tagId });
apiUtils.bookmarks.getBookmarks.invalidate({ tagId });
});
// TODO: Maybe we can only look at the cache and invalidate only affected bookmarks
apiUtils.bookmarks.getBookmark.invalidate();
return opts[0]?.onSuccess?.(res, req, meta);
},
});
}
export function useDeleteTag(
...opts: Parameters<typeof api.tags.delete.useMutation>
) {
const apiUtils = api.useUtils();
return api.tags.delete.useMutation({
...opts[0],
onSuccess: (res, req, meta) => {
apiUtils.tags.list.invalidate();
apiUtils.bookmarks.getBookmark.invalidate();
return opts[0]?.onSuccess?.(res, req, meta);
},
});
}
export function useDeleteUnusedTags(
...opts: Parameters<typeof api.tags.deleteUnused.useMutation>
) {
const apiUtils = api.useUtils();
return api.tags.deleteUnused.useMutation({
...opts[0],
onSuccess: (res, req, meta) => {
apiUtils.tags.list.invalidate();
return opts[0]?.onSuccess?.(res, req, meta);
},
});
}

View File

@ -185,9 +185,6 @@ export const labelsAppRouter = router({
}),
)
.mutation(async ({ input, ctx }) => {
console.log(input);
console.log(ctx);
console.log("TEEEEEEEEEEEEEEEEEEEEEST\n\n\n\n\n");
try {
const res = await ctx.db
.update(bookmarkTags)
@ -238,4 +235,31 @@ export const labelsAppRouter = router({
throw e;
}
}),
delete: authedProcedure
.input(
z.object({
labelId: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
// const affectedBookmarks = await ctx.db
// .select({
// bookmarkId: tagsOnBookmarks.bookmarkId,
// })
// .from(tagsOnBookmarks)
// .where(
// and(
// eq(tagsOnBookmarks.tagId, input.tagId),
// // Tag ownership is checked in the ensureTagOwnership middleware
// // eq(bookmarkTags.userId, ctx.user.id),
// ),
// );
const res = await ctx.db
.delete(labels)
.where(conditionFromInput(input, ctx.user.id));
if (res.changes == 0) {
throw new TRPCError({ code: "NOT_FOUND" });
}
}),
});