Skip to content

Commit

Permalink
Merge pull request #65 from hubcio2115/project-view
Browse files Browse the repository at this point in the history
  • Loading branch information
hubcio2115 authored Sep 29, 2024
2 parents f9bc3d9 + decdc3b commit 945c5d8
Show file tree
Hide file tree
Showing 19 changed files with 1,347 additions and 157 deletions.
26 changes: 23 additions & 3 deletions apps/web/src/app/dashboard/[name]/create-project/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
"use client";

import { useRouter } from "next/navigation";
import ProjectForm from "~/components/create-project/project-form";
import ProjectCreateForm from "~/components/create-project/project-create-form";
import { useCreateProjectMutation } from "~/lib/mutations/useCreateProjectMutation";
import type { InsertProject } from "~/lib/validators/project";

const defaultValues: InsertProject = {
license: "youtube",
title: "",
description: "",
categoryId: null,
defaultLanguage: "none",
tags: "",
embeddable: true,
privacyStatus: "unlisted",
publicStatsViewable: true,
selfDeclaredMadeForKids: false,
notifySubscribers: true,
channelId: "",
};

interface CreateProjectPageProps {
params: { name: string };
Expand All @@ -11,7 +27,7 @@ interface CreateProjectPageProps {
export default function CreateProjectPage({ params }: CreateProjectPageProps) {
const router = useRouter();

const mutation = useCreateProjectMutation(params.name, {
const { mutate, isPending } = useCreateProjectMutation(params.name, {
onSuccess: (project) => {
router.push(`/dashboard/${params.name}/project/${project.id}`);
},
Expand All @@ -22,7 +38,11 @@ export default function CreateProjectPage({ params }: CreateProjectPageProps) {

return (
<div className="container flex max-w-[800px] flex-1 flex-col items-center pb-11">
<ProjectForm mutation={mutation} />
<ProjectCreateForm
mutate={mutate}
isPending={isPending}
defaultValues={defaultValues}
/>
</div>
);
}
34 changes: 29 additions & 5 deletions apps/web/src/app/dashboard/[name]/project/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { redirect } from "next/navigation";
import ProjectView from "~/components/dashboard/project-view";
import { getProjectById } from "~/server/actions/project";
import {
getChannel,
getYoutubeCategories,
getYoutubeSupportedLanguages,
} from "~/server/api/utils/project";

type ProjectPageProps = {
params: {
Expand All @@ -12,18 +18,36 @@ export default async function ProjectPage({ params }: ProjectPageProps) {
const [project, err] = await getProjectById(+params.id);

if (err !== null) {
if (err === "UNAUTHORIZED") {
return redirect("/404");
}

throw new Error(err);
}

if (project === null) {
return redirect("/404");
}

const [channel, channelErr] = await getChannel(project.channelId);

if (channelErr !== null) {
return redirect("/404");
}

const [[languages, languagesErr], [categories, categoriesErr]] =
await Promise.all([getYoutubeSupportedLanguages(), getYoutubeCategories()]);

if (languagesErr !== null || categoriesErr !== null) {
throw new Error("Something went wrong on our end");
}

return (
<div>
<p>
{project.id}: {project.title}
</p>
</div>
<ProjectView
project={project}
channel={channel}
languages={languages}
categories={categories}
/>
);
}
18 changes: 2 additions & 16 deletions apps/web/src/components/create-project/categories-select.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
"use client";

import { useState } from "react";
import { useSuspenseQuery } from "@tanstack/react-query";
import { env } from "~/env";
import type { youtube_v3 } from "@googleapis/youtube";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import {
Command,
Expand All @@ -17,25 +14,14 @@ import { Check, ChevronsUpDown } from "lucide-react";
import { cn } from "~/lib/utils";
import type { ControllerRenderProps } from "react-hook-form";
import { Button } from "../ui/button";
import ky from "ky";
import { useCategoriesSuspenseQuery } from "~/lib/queries/useCategoriesQuery";

export default function CategoriesSelect({
value,
onChange,
disabled,
}: Omit<ControllerRenderProps, "ref">) {
const { data: categories } = useSuspenseQuery({
queryKey: ["youtubeVideoCategories"],
queryFn: async () => {
const data = await ky
.get<
youtube_v3.Schema$VideoCategory[]
>(`${env.NEXT_PUBLIC_API_URL}/api/youtube/categories`)
.json();

return data.filter((category) => category.snippet?.assignable);
},
});
const { data: categories } = useCategoriesSuspenseQuery();

const [open, setOpen] = useState(false);

Expand Down
15 changes: 2 additions & 13 deletions apps/web/src/components/create-project/language-select.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
"use client";

import { useState, type PropsWithChildren } from "react";
import { useSuspenseQuery } from "@tanstack/react-query";
import { env } from "~/env";
import type { youtube_v3 } from "@googleapis/youtube";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import type { ControllerRenderProps } from "react-hook-form";
import { Button } from "../ui/button";
Expand All @@ -17,22 +14,14 @@ import {
CommandList,
} from "../ui/command";
import { cn } from "~/lib/utils";
import ky from "ky";
import { useLanguagesSuspenseQuery } from "~/lib/queries/useLanguagesQuery";

export default function LanguagesSelect({
value,
onChange,
disabled,
}: PropsWithChildren<Omit<ControllerRenderProps, "ref">>) {
const { data: languages } = useSuspenseQuery({
queryKey: ["youtubeLanguages"],
queryFn: async () =>
ky
.get<
youtube_v3.Schema$I18nLanguage[]
>(`${env.NEXT_PUBLIC_API_URL}/api/youtube/languages`)
.json(),
});
const { data: languages } = useLanguagesSuspenseQuery();

const [open, setOpen] = useState(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,17 @@ import { cn } from "~/lib/utils";
import ChannelsSelect from "./channel-select";
import type { UseCreateProjectMutationResult } from "~/lib/mutations/useCreateProjectMutation";

const defaultValues: InsertProject = {
license: "youtube",
title: "",
description: "",
categoryId: null,
defaultLanguage: "none",
tags: "",
embeddable: true,
privacyStatus: "unlisted",
publicStatsViewable: true,
selfDeclaredMadeForKids: false,
notifySubscribers: true,
channelId: "",
};

interface ProjectFormProps {
mutation: UseCreateProjectMutationResult;
defaultValues: InsertProject;
mutate: UseCreateProjectMutationResult["mutate"];
isPending?: UseCreateProjectMutationResult["isPending"];
}

export default function ProjectForm({ mutation }: ProjectFormProps) {
export default function ProjectCreateForm({
mutate,
isPending = false,
defaultValues,
}: ProjectFormProps) {
const form = useForm<TProjectForm>({
resolver: zodResolver(projectFormSchema),
defaultValues,
Expand All @@ -84,26 +75,7 @@ export default function ProjectForm({ mutation }: ProjectFormProps) {
}, [video, channel]);

function onSuccess(data: TProjectForm) {
const formData = new FormData();

formData.append("license", data.license!);
formData.append("title", data.title!);
formData.append("description", data.description!);
formData.append("categoryId", data.categoryId!);
formData.append("defaultLanguage", data.defaultLanguage!);
formData.append("tags", data.tags!);
formData.append("embeddable", `${data.embeddable!}`);
formData.append("privacyStatus", data.privacyStatus!);
formData.append("publicStatsViewable", `${data.publicStatsViewable!}`);
formData.append(
"selfDeclaredMadeForKids",
`${data.selfDeclaredMadeForKids!}`,
);
formData.append("notifySubscribers", `${data.notifySubscribers!}`);
formData.append("channelId", data.channelId);
formData.append("video", data.video);

mutation.mutate(formData);
mutate(data);
}

return (
Expand All @@ -121,7 +93,7 @@ export default function ProjectForm({ mutation }: ProjectFormProps) {
name="channelId"
render={({ field: { ref: _ref, ...field } }) => (
<FormItem className="w-full">
<FormLabel className="font-bold text-lg">Category:</FormLabel>
<FormLabel className="font-bold text-lg">Channel:</FormLabel>

<FormControl>
<Suspense fallback={<InputSkeleton />}>
Expand Down Expand Up @@ -253,7 +225,7 @@ export default function ProjectForm({ mutation }: ProjectFormProps) {
<FormControl>
<Textarea
{...field}
className="resize-none"
className="resize-none h-64"
value={field.value ?? ""}
placeholder="Tell viewers about your video (type @ to mention a channel)"
/>
Expand Down Expand Up @@ -584,10 +556,10 @@ export default function ProjectForm({ mutation }: ProjectFormProps) {
<Button
className="max-w-max self-end"
type="submit"
disabled={mutation.isPending}
disabled={isPending}
>
Create project
{mutation.isPending ? (
{isPending ? (
<Loader2 className="size-4 animate-spin ml-2" />
) : null}
</Button>
Expand Down
5 changes: 2 additions & 3 deletions apps/web/src/components/dashboard/project-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ export default function VideoCard({ project }: ProjectCardProps) {
const { data: channel } = useChannelSuspenseQuery(project.channelId);

return (
<div className="max-w-[350px] sm:max-w-96 mx-auto">
<div className="mx-auto max-w-96">
<Link
href={`/dashboard/${name}/project/${project.id}`}
className="flex items-center max-h-min flex-col gap-2"
>
<div className="h-60 w-[320px] sm:w-96 rounded-lg overflow-hidden">
<div className="rounded-lg overflow-hidden">
<Image
className="h-60 w-[320px] sm:w-96"
alt="project thumbnail"
src={video.snippet?.thumbnails?.standard?.url ?? ""}
width={video.snippet?.thumbnails?.standard?.width ?? 320}
Expand Down
Loading

0 comments on commit 945c5d8

Please sign in to comment.