Fix updating categories

This commit is contained in:
Ryan Pandya 2024-12-04 12:44:24 -08:00
parent 020b75b72e
commit cd0ac2bf43
4 changed files with 42 additions and 63 deletions

View File

@ -68,7 +68,11 @@ export default function CategoriesView() {
> >
<Trash size={16} color="red" /> <Trash size={16} color="red" />
</ActionButtonWithTooltip> </ActionButtonWithTooltip>
<EditCategoryDialog categoryId={c.id} > <EditCategoryDialog category={{
...c,
categoryId: c.id
}
} >
<ButtonWithTooltip <ButtonWithTooltip
tooltip="Edit" tooltip="Edit"
variant="outline" variant="outline"

View File

@ -33,22 +33,25 @@ import { TRPCClientError } from "@trpc/client";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { z } from "zod"; import { z } from "zod";
import { zCategorySchema } from "@lifetracker/shared/types/categories"; import { zUpdateCategoryRequestSchema, ZUpdateCategoryRequest } from "@lifetracker/shared/types/categories";
type CreateCategorySchema = z.infer<typeof zCategorySchema>;
export default function EditCategoryDialog({ export default function EditCategoryDialog({
category: initialCategory,
children, children,
}: { }: {
category: ZUpdateCategoryRequest;
children?: React.ReactNode; children?: React.ReactNode;
}) { }) {
const apiUtils = api.useUtils(); const apiUtils = api.useUtils();
const [isOpen, onOpenChange] = useState(false); const [isOpen, onOpenChange] = useState(false);
const form = useForm<CreateCategorySchema>({
resolver: zodResolver(zCategorySchema), const form = useForm<ZUpdateCategoryRequest>({
resolver: zodResolver(zUpdateCategoryRequestSchema),
defaultValues: initialCategory,
}); });
const { mutate, isPending } = api.categories.update.useMutation({ const { mutate, isPending } = api.categories.update.useMutation({
onSuccess: () => { onSuccess: () => {
apiUtils.categories.list.invalidate();
toast({ toast({
description: "Category updated successfully", description: "Category updated successfully",
}); });
@ -139,9 +142,9 @@ export default function EditCategoryDialog({
</FormItem> </FormItem>
)} )}
/> />
<FormField {/* <FormField
control={form.control} control={form.control}
name="color" name="colorId"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Color</FormLabel> <FormLabel>Color</FormLabel>
@ -156,7 +159,7 @@ export default function EditCategoryDialog({
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> /> */}
<DialogFooter className="sm:justify-end"> <DialogFooter className="sm:justify-end">
<DialogClose asChild> <DialogClose asChild>
<Button type="button" variant="secondary"> <Button type="button" variant="secondary">

View File

@ -32,7 +32,11 @@ export const zGetCategoryResponseSchema = z.object({
export type ZGetCategoryResponse = z.infer<typeof zGetCategoryResponseSchema>; export type ZGetCategoryResponse = z.infer<typeof zGetCategoryResponseSchema>;
export const zUpdateCategoryRequestSchema = z.object({ export const zUpdateCategoryRequestSchema = z.object({
labelId: z.string(), code: z.number().nullish(),
code: z.number(),
name: z.string().optional(), name: z.string().optional(),
description: z.string().optional(),
colorId: z.string().nullish(),
parentId: z.string().nullish(),
categoryId: z.string(),
}); });
export type ZUpdateCategoryRequest = z.infer<typeof zUpdateCategoryRequestSchema>;

View File

@ -17,8 +17,9 @@ import { authedProcedure, router } from "../index";
import { zColorSchema } from "@lifetracker/shared/types/colors"; import { zColorSchema } from "@lifetracker/shared/types/colors";
function conditionFromInput(input: { categoryCode: string }, userId: string) { function conditionFromInput(input: { categoryCode?: string, categoryId?: string }, userId: string) {
return and(eq(categories.code, input.categoryCode), eq(categories.userId, userId)); const queryType = input.categoryCode ? eq(categories.code, input.categoryCode) : eq(categories.id, input.categoryId);
return and(queryType, eq(categories.userId, userId));
} }
async function createCategory( async function createCategory(
@ -221,55 +222,22 @@ export const categoriesAppRouter = router({
}), }),
) )
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { console.log("Updating");
const res = await ctx.db const res = await ctx.db
.update(bookmarkTags) .update(categories)
.set({ .set({
name: input.name, name: input.name,
}) description: input.description,
.where( code: input.code,
and( })
eq(bookmarkTags.id, input.tagId), .where(
eq(bookmarkTags.userId, ctx.user.id), and(
), conditionFromInput({ categoryId: input.categoryId }, ctx.user.id),
) eq(categories.userId, ctx.user.id),
.returning(); ),
).returning();
if (res.length == 0) { console.log(res);
throw new TRPCError({ code: "NOT_FOUND" }); return res[0];
}
try {
const affectedBookmarks = await ctx.db.query.tagsOnBookmarks.findMany(
{
where: eq(tagsOnBookmarks.tagId, input.tagId),
columns: {
bookmarkId: true,
},
},
);
await Promise.all(
affectedBookmarks
.map((b) => b.bookmarkId)
.map((id) => triggerSearchReindex(id)),
);
} catch (e) {
console.error("Something ELSE Went Wrong", e);
}
return res[0];
} catch (e) {
if (e instanceof SqliteError) {
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
throw new TRPCError({
code: "BAD_REQUEST",
message:
"Label name already exists.",
});
}
}
throw e;
}
}), }),
delete: authedProcedure delete: authedProcedure
.input( .input(