Skip to content

Commit

Permalink
setup db and started frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
maxwiseman committed Jul 8, 2024
1 parent df274d8 commit 8f4800e
Show file tree
Hide file tree
Showing 24 changed files with 476 additions and 338 deletions.
1 change: 1 addition & 0 deletions apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"next": "^14.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.52.1",
"srt-parser-2": "^1.2.3",
"superjson": "2.2.1",
"zod": "^3.22.4"
Expand Down
15 changes: 15 additions & 0 deletions apps/nextjs/src/app/_components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Card } from "@quotes/ui/card";
import { SearchBar } from "./search-bar";
import { db } from "@quotes/db";

export async function Navbar(): Promise<React.ReactElement> {
const showData = await db.query.show.findMany()

return (
<Card className="rounded-none sticky top-0 flex gap-4 items-center p-4 justify-between border-0 border-b">
<div className="text-lg font-bold">Quotes</div>
<SearchBar shows={showData} />
<div />
</Card>
)
}
180 changes: 0 additions & 180 deletions apps/nextjs/src/app/_components/posts.tsx

This file was deleted.

17 changes: 17 additions & 0 deletions apps/nextjs/src/app/_components/quote.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Card, CardContent, CardFooter } from "@quotes/ui/card";

export function Quote(quoteData: { id: number, scrollTo?: boolean, text: string, startTime: number, endTime: number, character?: string }): React.ReactElement {
const startDate = new Date(quoteData.startTime * 1000)
const endDate = new Date(quoteData.endTime * 1000)

return (
<Card className="overflow-hidden">
<CardContent className="pb-2 p-4 border-b shadow dark:bg-muted/50">
<span className="text-muted-foreground">{quoteData.character ? `${quoteData.character}: ` : undefined}</span>{quoteData.text}
</CardContent>
<CardFooter className="pt-1 p-4 text-muted-foreground bg-muted/50 dark:bg-card text-sm py-2">
{`${startDate.getUTCHours().toString().padStart(2, "0")}:${startDate.getUTCMinutes().toString().padStart(2, "0")}:${startDate.getUTCSeconds().toString().padStart(2, "0")} - ${endDate.getUTCHours().toString().padStart(2, "0")}:${endDate.getUTCMinutes().toString().padStart(2, "0")}:${endDate.getUTCSeconds().toString().padStart(2, "0")}`}
</CardFooter>
</Card>
)
}
31 changes: 31 additions & 0 deletions apps/nextjs/src/app/_components/result-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Button } from "@quotes/ui/button";
import { Card, CardContent, CardHeader } from "@quotes/ui/card";
import { Separator } from "@quotes/ui/separator";
import { IconPlayerPlay, IconShare2 } from "@tabler/icons-react";
import { Quote } from "./quote";
import Link from "next/link";

export function ResultCard(resultData: { title: string | null, description: string | null, number: number | null, id: number, quotes: { text: string, startTime: number, endTime: number, id: number }[] }): React.ReactElement {
return (
<Link href={`/episode/${resultData.id.toString()}`}>
<Card>
<CardHeader className="p-4">
<div className="flex gap-10 justify-between items-center flex-row">
<div className="flex flex-col">
<h4 className="font-bold text-lg">{resultData.title ? resultData.title : `Episode ${resultData.number?.toString() ?? "Unknown"}`}</h4>
<h5 className="text-muted-foreground">{resultData.description}</h5>
</div>
<div className="flex gap-2">
<Button size="icon" variant="outline" icon={<IconShare2 />} />
<Button icon={<IconPlayerPlay />}>Watch</Button>
</div>
</div>
</CardHeader>
<Separator />
<CardContent className="flex gap-4 flex-col p-4">
{resultData.quotes.map((quote) => <Quote key={quote.id} text={quote.text} startTime={quote.startTime} endTime={quote.endTime} id={quote.id} />)}
</CardContent>
</Card>
</Link>
)
}
58 changes: 58 additions & 0 deletions apps/nextjs/src/app/_components/search-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client"

import type { show } from "@quotes/db";
import { Form, FormField } from "@quotes/ui/form";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@quotes/ui/select"
import { usePathname, useRouter } from "next/navigation"
import { useId } from "react"
import { useForm } from 'react-hook-form'
import type { SubmitHandler } from 'react-hook-form';
import { z } from "zod";

export function SearchBar({ shows }: { shows: (typeof show.$inferSelect)[] }): React.ReactElement {
const router = useRouter();
const form = useForm<z.infer<typeof formSchema>>();
const id = useId()
const pathname = usePathname();

const onSubmit: SubmitHandler<z.infer<typeof formSchema>> = (data) => {
console.log("Submitted search:", data)
const params = new URLSearchParams(pathname)
params.set('q', data.query)
if (data.filterId) params.set('f', data.filterId)
router.push(`/search?${params.toString()}`)
}

const formSchema = z.object({
query: z.string(),
filterId: z.string().optional()
})

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full lg:max-w-[35rem]">
<label htmlFor={id} className="flex cursor-text h-9 w-full rounded-md border border-input items-center gap-2 bg-transparent px-0 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-within:border-ring disabled:cursor-not-allowed disabled:opacity-50">
<span className="sr-only">Search bar</span>
<FormField name="filterId" render={({ field }) => (
<Select onValueChange={field.onChange} defaultValue={field.value as string}>
<SelectTrigger className="w-max pr-4 bg-muted pl-2 rounded-md border-0 shadow-none flex-row-reverse ">
<SelectValue placeholder="Select Show" className="w-max" />
</SelectTrigger>
<SelectContent>
{shows.map((show) => (
<SelectItem key={show.id} value={show.id.toString()}>
<div className="flex gap-1 items-center w-max">
<img src={show.iconUrl} alt={`Icon for ${show.title}`} className="w-5 h-5 rounded-full content-center object-cover" />
{show.title}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
)} />
<input {...form.register("query", { required: true })} id={id} className="focus-visible:outline-none w-full py-1" placeholder="Search..." type="text" />
</label>
</form>
</Form>
)
}
31 changes: 31 additions & 0 deletions apps/nextjs/src/app/episode/[episodeId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { db, episode, eq } from "@quotes/db"
import type { quote } from "@quotes/db";
import { Separator } from "@quotes/ui/separator"
import { notFound } from "next/navigation"
import { Quote } from "~/app/_components/quote"

export default async function Page({ params, searchParams }: { params: { episodeId: string }, searchParams: { qId?: string } }): Promise<React.ReactElement> {
const data = await db.query.episode.findFirst({
where: eq(episode.id, parseInt(params.episodeId)),
with: {
quote: true
}
}) as typeof episode.$inferSelect & { quote: typeof quote.$inferSelect[] } | undefined
if (data === undefined) notFound()

return (
<div className="flex items-center flex-col p-8">
<div className="flex flex-col gap-4 max-w-[50rem] w-full">
<div className="flex flex-col gap-2">
<h1 className="text-4xl font-bold">{data.title ? data.title : `Episode ${data.number?.toString() ?? ""}`}</h1>
<h2>{data.description}</h2>
</div>
<Separator className="mb-8" />
<div className="flex gap-4 flex-col">
{data.quote.map((item) => <Quote id={item.id} scrollTo={parseInt(searchParams.qId ?? "") === item.id} text={item.text} startTime={item.startTime} endTime={item.endTime} key={item.id} />)}
</div>

</div>
</div>
)
}
3 changes: 2 additions & 1 deletion apps/nextjs/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { env } from "~/env";
import { TRPCReactProvider } from "~/trpc/react";

import "~/app/globals.css";
import { Navbar } from "./_components/navbar";

export const metadata: Metadata = {
metadataBase: new URL(
Expand Down Expand Up @@ -53,7 +54,7 @@ export default function RootLayout(props: {
>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<TRPCReactProvider>
<div className="sticky top-0 flex min-h-14 w-full items-center border-b bg-background shadow" />
<Navbar />
{props.children}
</TRPCReactProvider>
<div className="fixed bottom-4 right-4">
Expand Down
9 changes: 2 additions & 7 deletions apps/nextjs/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { Button } from "@quotes/ui/button";
import srtParser2 from "srt-parser-2"
import { promises as fs } from "fs"
import { Quote } from "./_components/quote";

export default async function HomePage(): Promise<React.ReactElement> {
const srtFile = await fs.readFile("~/Projects/quotes/apps/backend/subs.srt")
const parser = new srtParser2()
const srtData = parser.fromSrt(srtFile.toString())

return (
<main className="container h-screen py-16">
<div className="flex flex-col items-center justify-center gap-4">
<h1>Heading one</h1>
<h2>Heading two</h2>
<Button>{srtData[0]?.text}</Button>
<Quote text="Hello there, my name is Jeremy Clarkson" startTime={100} endTime={100} id={123} />
</div>
</main>
);
Expand Down
Loading

0 comments on commit 8f4800e

Please sign in to comment.