music and animation system

This commit is contained in:
2026-03-13 15:49:53 +01:00
parent 4916a2fc7b
commit 166ae50c49
24 changed files with 785 additions and 15 deletions

View File

@@ -20,6 +20,9 @@ export default async function AdminSideBar() {
<Link href={"/admin/project/techStack/create"}> Create Stack </Link>
<Link href={"/admin/project/list"}> Project List </Link>
</SimpleSidebarGroup>
<SimpleSidebarGroup lable="Music">
<Link href={"/admin/music"}> Manage Music </Link>
</SimpleSidebarGroup>
<SimpleSidebarGroup lable="Blog">
<Link href={"/"}> Some Blog Action </Link>
</SimpleSidebarGroup>

View File

@@ -14,13 +14,13 @@ import { SelectItem } from '~/components/ui/select';
import {FormMutationContextProvider} from '~/app/_components/Form/Components/MutationProvider';
export default function CreateUpdateCvCategoryForm(params: { className?: string, entity?: IterableElement<RouterOutputs['category']['select']> }) {
const schemas = entitySchemas('cvCategory')
const [id, setId] = useState<string | undefined>(params.entity ? params.entity.id : undefined)
const [id, setId] = useState<string | undefined>(params.entity?.id)
const form = useForm<z.infer<typeof schemas.insert>>({
resolver: zodResolver(schemas.insert),
defaultValues: {
id: params.entity ? params.entity.id : crypto.randomUUID(),
name: params.entity ? params.entity.name : "",
layoutPosition: params.entity ? params.entity.layoutPosition : "col1"
id: params.entity?.id || crypto.randomUUID(),
name: params.entity?.name || "",
layoutPosition: params.entity?.layoutPosition || "col1"
}
})
let path = usePathname()

View File

@@ -0,0 +1,105 @@
'use client'
import { useState } from "react";
import { trpc } from "~/app/_trpc/Client";
import { UploadDropzone } from "~/lib/uploadthing";
import { Label } from "~/components/ui/label";
import { FormScaffold } from "~/app/_components/Form/Components";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import type { RouterOutputs } from "~/server/routers/_app";
import type { IterableElement } from "type-fest";
import { Toaster } from "~/components/ui/sonner";
import { toast } from "sonner";
import { FormMutationContextProvider } from "~/app/_components/Form/Components/MutationProvider";
import { TextInputFormField } from "~/app/_components/Form/Fields";
import { createMusicInputSchema } from "~/lib/trpc/music/schemas";
export default function CreateUpdateMusicForm(props: {
entity?: IterableElement<RouterOutputs['music']['list']>,
className?: string
}) {
const entity = props.entity;
const [id, setId] = useState<string | undefined>(entity?.id)
const utils = trpc.useUtils();
const form = useForm<z.infer<typeof createMusicInputSchema>>({
resolver: zodResolver(createMusicInputSchema),
defaultValues: {
id: entity?.id || crypto.randomUUID(),
title: entity?.title || "",
description: entity?.description || "",
fileUrl: entity?.fileUrl,
fileKey: entity?.fileKey,
fileName: entity?.fileName,
}
})
const createMutation = trpc.music.create.useMutation({
onSuccess: (values) => {
setId(values?.id);
utils.music.list.invalidate();
},
onError: (e) => {
toast(e.message)
}
})
const updateMutation = trpc.music.update.useMutation({
onSuccess: (_) => {
utils.music.list.invalidate();
},
onError: (e) => {
toast(e.message)
}
})
const deleteMutation = trpc.music.delete.useMutation({
onSuccess: (_) => {
utils.music.list.invalidate();
},
onError: (e) => {
toast(e.message)
}
})
function onSubmit(values: z.infer<typeof createMusicInputSchema>) {
id ?
updateMutation.mutate(values) :
createMutation.mutate(values)
}
return (
<>
<Toaster />
<FormMutationContextProvider value={{
createMutation: createMutation,
updateMutation: updateMutation,
deleteMutation: deleteMutation
}}>
<FormScaffold form={form} onSubmit={onSubmit} title='Music' id={id} className={props.className}>
<TextInputFormField control={form.control} name='title' label='Title'/>
<TextInputFormField control={form.control} name='description' label='Description'/>
<div className="flex flex-col gap-1.5">
<Label>Audio File</Label>
<UploadDropzone
endpoint="musicUploader"
config={{mode: 'auto'}}
onUploadError={(e) => {
toast(e.message)
}}
onClientUploadComplete={(res) => {
console.log(res)
if (res[0]) {
form.setValue('fileKey',res[0].serverData.fileKey);
form.setValue('fileName',res[0].serverData.fileName);
form.setValue('title',res[0].serverData.fileName);
form.setValue('description',res[0].serverData.fileName);
form.setValue('fileUrl',res[0].serverData.fileUrl);
}
console.log(form.getValues());
}}
/>
</div>
</FormScaffold>
</FormMutationContextProvider>
</>
);
}

View File

@@ -0,0 +1,26 @@
'use client'
import { trpc } from "~/app/_trpc/Client";
import * as Card from "~/components/ui/card";
import UploadMusicForm from "./_components/UploadMusicForm";
import { CollapsibleForm } from "~/app/_components/Form/Components";
import { useEffect } from "react";
export default function AdminMusicPage() {
const { data: tracks } = trpc.music.list.useQuery();
useEffect(() => {console.log(tracks)}, [tracks])
return (
<div className="w-5/6 lg:w-1/2 flex flex-col gap-3">
{tracks && <>
{tracks.map((t) => (
<Card.Card key={t.id}>
<Card.CardContent>
<UploadMusicForm entity={t} className="w-full"/>
</Card.CardContent>
</Card.Card>
))}
</>}
<CollapsibleForm entityName="Track" form={UploadMusicForm}/>
</div>
);
}