add create resource trpc function

This commit is contained in:
Brandon Egger 2023-09-04 00:03:42 -05:00
parent c243fda8e1
commit cd1dc2a555
6 changed files with 82 additions and 58 deletions

View File

@ -42,13 +42,16 @@ const AdminActionButton = ({
label, label,
onClick, onClick,
symbol, symbol,
type = "button",
}: { }: {
label: string; label: string;
onClick: () => void; onClick?: () => void;
symbol: JSX.Element | undefined; symbol: JSX.Element | undefined;
type?: HTMLButtonElement["type"];
}) => { }) => {
return ( return (
<button <button
type={type}
className="py-auto group my-auto h-full space-x-2 rounded-lg border border-neutral-400 bg-neutral-800 px-2 hover:border-neutral-800 hover:bg-white" className="py-auto group my-auto h-full space-x-2 rounded-lg border border-neutral-400 bg-neutral-800 px-2 hover:border-neutral-800 hover:bg-white"
onClick={onClick} onClick={onClick}
> >

View File

@ -47,6 +47,7 @@ import { ResourcePhoto } from "~/components/ResourcePhoto";
Modal.setAppElement("#__next"); Modal.setAppElement("#__next");
export type ResourceUpdateInput = RouterInputs["auditoryResource"]["update"]; export type ResourceUpdateInput = RouterInputs["auditoryResource"]["update"];
export type ResourceCreateInput = RouterInputs["auditoryResource"]["create"];
/** /**
* Renders the image selector for resource form. * Renders the image selector for resource form.
@ -361,14 +362,14 @@ function ResourceSummarySubForm({
<InfoInputLine <InfoInputLine
details={register("manufacturer.name", { required: true })} details={register("manufacturer.name", { required: true })}
placeholder="manufacturer" placeholder="manufacturer"
value={resource?.manufacturer?.name ?? ""}
hint="manufacturer" hint="manufacturer"
value={resource?.name}
/> />
</span> </span>
<InfoInputLine <InfoInputLine
details={register("name", { required: true })} details={register("name", { required: true })}
placeholder="name" placeholder="name"
value={resource?.name ?? ""} value={resource?.name}
hint="name" hint="name"
/> />
<span className="my-1 block w-full text-center text-xs italic text-neutral-400"> <span className="my-1 block w-full text-center text-xs italic text-neutral-400">

View File

@ -2,7 +2,9 @@ import { type HTMLInputTypeAttribute, useState } from "react";
import { import {
type UseFormRegisterReturn, type UseFormRegisterReturn,
type InternalFieldName, type InternalFieldName,
useFormContext,
} from "react-hook-form"; } from "react-hook-form";
import { ResourceCreateInput } from "../admin/resources/form";
/** /**
* Single line input for the fields found to the right of the * Single line input for the fields found to the right of the
@ -14,7 +16,7 @@ function InfoInputLine<TFieldName extends InternalFieldName>({
hint, hint,
details, details,
}: { }: {
value: string; value?: string | undefined;
placeholder: string; placeholder: string;
hint?: string; hint?: string;
details: UseFormRegisterReturn<TFieldName>; details: UseFormRegisterReturn<TFieldName>;

View File

@ -40,8 +40,8 @@ const EditResourcePage = () => {
}); });
const { mutate } = api.auditoryResource.update.useMutation({ const { mutate } = api.auditoryResource.update.useMutation({
onSuccess: async (_resData) => { onSuccess: async (resData) => {
if (!data) { if (!resData) {
setServerError("An unexpected error has occured"); setServerError("An unexpected error has occured");
return; return;
} }

View File

@ -1,23 +1,40 @@
import { XCircleIcon, PlusCircleIcon } from "@heroicons/react/20/solid"; import { XCircleIcon, PlusCircleIcon } from "@heroicons/react/20/solid";
import { useState } from "react"; import { useRouter } from "next/router";
import { type SubmitHandler, useForm } from "react-hook-form"; import { useEffect, useState } from "react";
import {
type SubmitHandler,
useForm,
type UseFormReturn,
} from "react-hook-form";
import { AdminBarLayout } from "~/components/admin/ControlBar"; import { AdminBarLayout } from "~/components/admin/ControlBar";
import { AdminActionButton, AdminActionLink } from "~/components/admin/common"; import { AdminActionButton, AdminActionLink } from "~/components/admin/common";
import { import {
type ResourceCreateInput,
ResourceForm, ResourceForm,
type ResourceUpdateInput, type ResourceUpdateInput,
} from "~/components/admin/resources/form"; } from "~/components/admin/resources/form";
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout"; import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
import { api } from "~/utils/api";
const EditResourcePage = () => { const EditResourcePage = () => {
const formMethods = useForm<ResourceUpdateInput>(); const router = useRouter();
const formMethods = useForm<ResourceCreateInput>();
const [serverError, _setServerError] = useState<string | undefined>( const [serverError, setServerError] = useState<string | undefined>(undefined);
undefined
);
const onSubmit: SubmitHandler<ResourceUpdateInput> = () => { const { mutate } = api.auditoryResource.create.useMutation({
// TODO: TRPC request to create resource onSuccess: async (resData) => {
if (!resData) {
setServerError("An unexpected error has occured");
}
setServerError(undefined);
await router.push(`/resources/${resData.id}`);
},
});
const onSubmit: SubmitHandler<ResourceCreateInput> = (data) => {
mutate(data);
}; };
return ( return (
@ -28,9 +45,6 @@ const EditResourcePage = () => {
key="create" key="create"
symbol={<PlusCircleIcon className="w-4" />} symbol={<PlusCircleIcon className="w-4" />}
label="Create" label="Create"
onClick={() => {
onSubmit(formMethods.getValues());
}}
/>, />,
<AdminActionLink <AdminActionLink
key="cancel" key="cancel"
@ -41,7 +55,10 @@ const EditResourcePage = () => {
]} ]}
> >
<div className="mb-12"> <div className="mb-12">
<ResourceForm methods={formMethods} error={serverError} /> <ResourceForm
methods={formMethods as UseFormReturn<ResourceUpdateInput>}
error={serverError}
/>
</div> </div>
</AdminBarLayout> </AdminBarLayout>
</HeaderFooterLayout> </HeaderFooterLayout>

View File

@ -16,6 +16,38 @@ const emptyStringToUndefined = (val: string | undefined | null) => {
return val; return val;
}; };
const AuditoryResourceSchema = z.object({
id: z.string(),
icon: z.string().min(1),
name: z.string().min(1),
description: z.string().min(1),
manufacturer: z.object({
name: z.string().min(1),
required: z.boolean(),
notice: z
.string()
.nullable()
.transform(emptyStringToUndefined),
}),
ages: z.object({ min: z.number().int(), max: z.number().int() }),
skills: z.array(z.nativeEnum(Skill)),
skill_levels: z.array(z.nativeEnum(SkillLevel)),
payment_options: z.array(z.nativeEnum(PaymentType)),
photo: z
.object({
name: z.string(),
data: z.instanceof(Buffer),
})
.nullable(),
platform_links: z.array(
z.object({
platform: z.nativeEnum(Platform),
link: z.string().min(1),
})
),
});
export const auditoryResourceRouter = createTRPCRouter({ export const auditoryResourceRouter = createTRPCRouter({
byId: publicProcedure byId: publicProcedure
.input(z.object({ id: z.string() })) .input(z.object({ id: z.string() }))
@ -45,47 +77,16 @@ export const auditoryResourceRouter = createTRPCRouter({
return ctx.prisma.auditoryResource.findMany(); return ctx.prisma.auditoryResource.findMany();
}), }),
create: protectedProcedure
.input(AuditoryResourceSchema)
.mutation(async ({ input, ctx }) => {
return await ctx.prisma.auditoryResource.create({
data: input,
});
}),
update: protectedProcedure update: protectedProcedure
.input( .input(AuditoryResourceSchema.partial())
z.object({
id: z.string(),
icon: z.string().min(1).optional(),
name: z.string().min(1).optional(),
description: z.string().min(1).optional(),
manufacturer: z
.object({
name: z.string().min(1),
required: z.boolean(),
notice: z
.string()
.optional()
.nullable()
.transform(emptyStringToUndefined),
})
.optional(),
ages: z
.object({ min: z.number().int(), max: z.number().int() })
.optional(),
skills: z.array(z.nativeEnum(Skill)).optional(),
skill_levels: z.array(z.nativeEnum(SkillLevel)).optional(),
payment_options: z.array(z.nativeEnum(PaymentType)).optional(),
photo: z
.object({
name: z.string(),
data: z.instanceof(Buffer),
})
.nullable()
.optional(),
platform_links: z
.array(
z.object({
platform: z.nativeEnum(Platform),
link: z.string().min(1),
})
)
.optional(),
})
)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
return await ctx.prisma.auditoryResource.update({ return await ctx.prisma.auditoryResource.update({
where: { where: {