add create resource trpc function
This commit is contained in:
parent
c243fda8e1
commit
cd1dc2a555
@ -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}
|
||||||
>
|
>
|
||||||
|
@ -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">
|
||||||
|
@ -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>;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user