aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-02-08 15:14:23 +0000
committerMohamedBassem <me@mbassem.com>2024-02-08 15:15:21 +0000
commit80bb8a108f29331cdb2f2695f6801beee104dc89 (patch)
treeb1ae2a512963a9c916c4bfed71f7633f508de131 /web
parent333429adbaaa592cc96b480a5228f0e3f1de4cc2 (diff)
downloadkarakeep-80bb8a108f29331cdb2f2695f6801beee104dc89.tar.zst
[refactor] Move the different packages to the package subdir
Diffstat (limited to 'web')
-rw-r--r--web/.env.sample8
-rw-r--r--web/Dockerfile57
-rw-r--r--web/README.md36
-rw-r--r--web/app/api/auth/[...nextauth]/route.tsx3
-rw-r--r--web/app/api/v1/links/[linkId]/route.ts32
-rw-r--r--web/app/api/v1/links/route.ts49
-rw-r--r--web/app/dashboard/bookmarks/components/AddLink.tsx67
-rw-r--r--web/app/dashboard/bookmarks/components/LinkCard.tsx96
-rw-r--r--web/app/dashboard/bookmarks/components/LinksGrid.tsx21
-rw-r--r--web/app/dashboard/bookmarks/page.tsx20
-rw-r--r--web/app/dashboard/components/Sidebar.tsx56
-rw-r--r--web/app/dashboard/layout.tsx15
-rw-r--r--web/app/favicon.icobin25931 -> 0 bytes
-rw-r--r--web/app/globals.css76
-rw-r--r--web/app/layout.tsx27
-rw-r--r--web/app/page.tsx19
-rwxr-xr-xweb/bun.lockbbin158558 -> 0 bytes
-rw-r--r--web/components.json17
-rw-r--r--web/components/auth/login.tsx17
-rw-r--r--web/components/auth/logout.tsx17
-rw-r--r--web/components/ui/badge.tsx36
-rw-r--r--web/components/ui/button.tsx56
-rw-r--r--web/components/ui/card.tsx86
-rw-r--r--web/components/ui/dropdown-menu.tsx200
-rw-r--r--web/components/ui/form.tsx176
-rw-r--r--web/components/ui/imageCard.tsx56
-rw-r--r--web/components/ui/input.tsx25
-rw-r--r--web/components/ui/label.tsx26
-rw-r--r--web/components/ui/toast.tsx127
-rw-r--r--web/components/ui/toaster.tsx35
-rw-r--r--web/components/ui/use-toast.ts189
-rw-r--r--web/lib/api.ts81
-rw-r--r--web/lib/auth.ts25
-rw-r--r--web/lib/config.ts22
-rw-r--r--web/lib/services/links.ts78
-rw-r--r--web/lib/types/api/links.ts33
-rw-r--r--web/lib/types/api/tags.ts6
-rw-r--r--web/lib/types/next-auth.d.ts12
-rw-r--r--web/lib/utils.ts6
-rw-r--r--web/next.config.mjs4
-rw-r--r--web/package.json41
-rw-r--r--web/postcss.config.js6
-rw-r--r--web/public/next.svg1
-rw-r--r--web/public/vercel.svg1
-rw-r--r--web/tailwind.config.ts80
-rw-r--r--web/tsconfig.json27
46 files changed, 0 insertions, 2068 deletions
diff --git a/web/.env.sample b/web/.env.sample
deleted file mode 100644
index a48054f0..00000000
--- a/web/.env.sample
+++ /dev/null
@@ -1,8 +0,0 @@
-DATABASE_URL="file:./dev.db"
-NEXTAUTH_URL=
-NEXTAUTH_SECRET=
-
-# Oauth
-AUTHENTIK_ID=
-AUTHENTIK_SECRET=
-AUTHENTIK_ISSUER=
diff --git a/web/Dockerfile b/web/Dockerfile
deleted file mode 100644
index 30a46bd3..00000000
--- a/web/Dockerfile
+++ /dev/null
@@ -1,57 +0,0 @@
-FROM oven/bun:1.0-alpine AS base
-
-# Install dependencies only when needed
-FROM base AS deps
-# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
-# RUN apk add --no-cache libc6-compat
-WORKDIR /app
-
-# Install dependencies based on the preferred package manager
-COPY package.json bun.lockb ./
-RUN bun install --frozen-lockfile
-
-# Rebuild the source code only when needed
-FROM base AS builder
-WORKDIR /app
-COPY --from=deps /app/node_modules ./node_modules
-COPY . .
-
-ENV NODE_ENV production
-
-ENV NEXT_TELEMETRY_DISABLED 1
-
-RUN bun run build
-
-# Production image, copy all the files and run next
-FROM base AS runner
-WORKDIR /app
-
-ENV NODE_ENV production
-
-ENV NEXT_TELEMETRY_DISABLED 1
-
-RUN addgroup --system --gid 1001 nodejs
-RUN adduser --system --uid 1001 nextjs
-
-COPY --from=builder /app/public ./public
-
-# Set the correct permission for prerender cache
-RUN mkdir .next
-RUN chown nextjs:nodejs .next
-
-# Automatically leverage output traces to reduce image size
-# https://nextjs.org/docs/advanced-features/output-file-tracing
-COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
-COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
-
-USER nextjs
-
-EXPOSE 3000
-
-ENV PORT 3000
-# set hostname to localhost
-ENV HOSTNAME "0.0.0.0"
-
-# server.js is created by next build from the standalone output
-# https://nextjs.org/docs/pages/api-reference/next-config-js/output
-CMD ["node", "server.js"]
diff --git a/web/README.md b/web/README.md
deleted file mode 100644
index c4033664..00000000
--- a/web/README.md
+++ /dev/null
@@ -1,36 +0,0 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
-
-## Getting Started
-
-First, run the development server:
-
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
-
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
-
-This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
-
-## Learn More
-
-To learn more about Next.js, take a look at the following resources:
-
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
-
-## Deploy on Vercel
-
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
-
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/web/app/api/auth/[...nextauth]/route.tsx b/web/app/api/auth/[...nextauth]/route.tsx
deleted file mode 100644
index e722926b..00000000
--- a/web/app/api/auth/[...nextauth]/route.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import { authHandler } from "@/lib/auth";
-
-export { authHandler as GET, authHandler as POST };
diff --git a/web/app/api/v1/links/[linkId]/route.ts b/web/app/api/v1/links/[linkId]/route.ts
deleted file mode 100644
index 39449d6d..00000000
--- a/web/app/api/v1/links/[linkId]/route.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { authOptions } from "@/lib/auth";
-import { unbookmarkLink } from "@/lib/services/links";
-import { Prisma } from "@remember/db";
-
-import { getServerSession } from "next-auth";
-import { NextRequest } from "next/server";
-
-export async function DELETE(
- _request: NextRequest,
- { params }: { params: { linkId: string } },
-) {
- // TODO: We probably should be using an API key here instead of the session;
- const session = await getServerSession(authOptions);
- if (!session) {
- return new Response(null, { status: 401 });
- }
-
- try {
- await unbookmarkLink(params.linkId, session.user.id);
- } catch (e: unknown) {
- if (
- e instanceof Prisma.PrismaClientKnownRequestError &&
- e.code === "P2025" // RecordNotFound
- ) {
- return new Response(null, { status: 404 });
- } else {
- throw e;
- }
- }
-
- return new Response(null, { status: 201 });
-}
diff --git a/web/app/api/v1/links/route.ts b/web/app/api/v1/links/route.ts
deleted file mode 100644
index 87541634..00000000
--- a/web/app/api/v1/links/route.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { authOptions } from "@/lib/auth";
-import { bookmarkLink, getLinks } from "@/lib/services/links";
-
-import {
- zNewBookmarkedLinkRequestSchema,
- ZGetLinksResponse,
- ZBookmarkedLink,
-} from "@/lib/types/api/links";
-import { getServerSession } from "next-auth";
-import { NextRequest, NextResponse } from "next/server";
-
-export async function POST(request: NextRequest) {
- // TODO: We probably should be using an API key here instead of the session;
- const session = await getServerSession(authOptions);
- if (!session) {
- return new Response(null, { status: 401 });
- }
-
- const linkRequest = zNewBookmarkedLinkRequestSchema.safeParse(
- await request.json(),
- );
-
- if (!linkRequest.success) {
- return NextResponse.json(
- {
- error: linkRequest.error.toString(),
- },
- { status: 400 },
- );
- }
-
- const link = await bookmarkLink(linkRequest.data.url, session.user.id);
-
- let response: ZBookmarkedLink = { ...link };
- return NextResponse.json(response, { status: 201 });
-}
-
-export async function GET() {
- // TODO: We probably should be using an API key here instead of the session;
- const session = await getServerSession(authOptions);
- if (!session) {
- return new Response(null, { status: 401 });
- }
-
- const links = await getLinks(session.user.id);
-
- let response: ZGetLinksResponse = { links };
- return NextResponse.json(response);
-}
diff --git a/web/app/dashboard/bookmarks/components/AddLink.tsx b/web/app/dashboard/bookmarks/components/AddLink.tsx
deleted file mode 100644
index fb77786c..00000000
--- a/web/app/dashboard/bookmarks/components/AddLink.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-"use client";
-
-import { Button } from "@/components/ui/button";
-import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
-import { Input } from "@/components/ui/input";
-import APIClient from "@/lib/api";
-import { Plus } from "lucide-react";
-import { useRouter } from "next/navigation";
-import { useForm, SubmitErrorHandler } from "react-hook-form";
-import { z } from "zod";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { toast } from "@/components/ui/use-toast";
-
-const formSchema = z.object({
- url: z.string().url({ message: "The link must be a valid URL" }),
-});
-
-export default function AddLink() {
- const router = useRouter();
-
- const form = useForm<z.infer<typeof formSchema>>({
- resolver: zodResolver(formSchema),
- });
-
- async function onSubmit(value: z.infer<typeof formSchema>) {
- const [_resp, error] = await APIClient.bookmarkLink(value.url);
- if (error) {
- toast({ description: error.message, variant: "destructive" });
- return;
- }
- router.refresh();
- }
-
- const onError: SubmitErrorHandler<z.infer<typeof formSchema>> = (errors) => {
- toast({
- description: Object.values(errors)
- .map((v) => v.message)
- .join("\n"),
- variant: "destructive",
- });
- };
-
- return (
- <Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit, onError)}>
- <div className="py-4 container flex w-full items-center space-x-2">
- <FormField
- control={form.control}
- name="url"
- render={({ field }) => {
- return (
- <FormItem className="flex-1">
- <FormControl>
- <Input type="text" placeholder="Link" {...field} />
- </FormControl>
- </FormItem>
- );
- }}
- />
- <Button type="submit">
- <Plus />
- </Button>
- </div>
- </form>
- </Form>
- );
-}
diff --git a/web/app/dashboard/bookmarks/components/LinkCard.tsx b/web/app/dashboard/bookmarks/components/LinkCard.tsx
deleted file mode 100644
index da59d9da..00000000
--- a/web/app/dashboard/bookmarks/components/LinkCard.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-"use client";
-
-import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import {
- ImageCard,
- ImageCardBody,
- ImageCardFooter,
- ImageCardTitle,
-} from "@/components/ui/imageCard";
-import { useToast } from "@/components/ui/use-toast";
-import APIClient from "@/lib/api";
-import { ZBookmarkedLink } from "@/lib/types/api/links";
-import { MoreHorizontal, Trash2 } from "lucide-react";
-import Link from "next/link";
-import { useRouter } from "next/navigation";
-
-export function LinkOptions({ linkId }: { linkId: string }) {
- const { toast } = useToast();
- const router = useRouter();
-
- const unbookmarkLink = async () => {
- let [_, error] = await APIClient.unbookmarkLink(linkId);
-
- if (error) {
- toast({
- variant: "destructive",
- title: "Something went wrong",
- description: "There was a problem with your request.",
- });
- } else {
- toast({
- description: "The link has been deleted!",
- });
- }
-
- router.refresh();
- };
- return (
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button variant="ghost">
- <MoreHorizontal />
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent className="w-fit">
- <DropdownMenuItem className="text-destructive" onClick={unbookmarkLink}>
- <Trash2 className="mr-2 h-4 w-4" />
- <span>Delete</span>
- </DropdownMenuItem>
- </DropdownMenuContent>
- </DropdownMenu>
- );
-}
-
-export default function LinkCard({ link }: { link: ZBookmarkedLink }) {
- const parsedUrl = new URL(link.url);
-
- return (
- <ImageCard
- className={
- "bg-gray-50 duration-300 ease-in border border-grey-100 hover:transition-all hover:border-blue-300"
- }
- image={link.details?.imageUrl ?? undefined}
- >
- <ImageCardTitle>
- <Link className="line-clamp-3" href={link.url}>
- {link.details?.title ?? parsedUrl.host}
- </Link>
- </ImageCardTitle>
- <ImageCardBody className="py-2 overflow-clip">
- {link.tags.map((t) => (
- <Badge variant="default" className="bg-gray-300 text-gray-500" key={t.id}>
- #{t.name}
- </Badge>
- ))}
- </ImageCardBody>
- <ImageCardFooter>
- <div className="flex justify-between text-gray-500">
- <div className="my-auto">
- <Link className="line-clamp-1 hover:text-black" href={link.url}>
- {parsedUrl.host}
- </Link>
- </div>
- <LinkOptions linkId={link.id} />
- </div>
- </ImageCardFooter>
- </ImageCard>
- );
-}
diff --git a/web/app/dashboard/bookmarks/components/LinksGrid.tsx b/web/app/dashboard/bookmarks/components/LinksGrid.tsx
deleted file mode 100644
index 66f0d766..00000000
--- a/web/app/dashboard/bookmarks/components/LinksGrid.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { getServerSession } from "next-auth";
-import { redirect } from "next/navigation";
-import { authOptions } from "@/lib/auth";
-import { getLinks } from "@/lib/services/links";
-import LinkCard from "./LinkCard";
-
-export default async function LinksGrid() {
- const session = await getServerSession(authOptions);
- if (!session) {
- redirect("/");
- }
- const links = await getLinks(session.user.id);
-
- return (
- <div className="container grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
- {links.map((l) => (
- <LinkCard key={l.id} link={l} />
- ))}
- </div>
- );
-}
diff --git a/web/app/dashboard/bookmarks/page.tsx b/web/app/dashboard/bookmarks/page.tsx
deleted file mode 100644
index b4158893..00000000
--- a/web/app/dashboard/bookmarks/page.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import AddLink from "./components/AddLink";
-import LinksGrid from "./components/LinksGrid";
-import type { Metadata } from "next";
-
-export const metadata: Metadata = {
- title: "Remember - Bookmarks",
-};
-
-export default async function Bookmarks() {
- return (
- <div className="flex flex-col">
- <div>
- <AddLink />
- </div>
- <div>
- <LinksGrid />
- </div>
- </div>
- );
-}
diff --git a/web/app/dashboard/components/Sidebar.tsx b/web/app/dashboard/components/Sidebar.tsx
deleted file mode 100644
index 0ed87daf..00000000
--- a/web/app/dashboard/components/Sidebar.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Button } from "@/components/ui/button";
-import { authOptions } from "@/lib/auth";
-import { Archive, MoreHorizontal, Star, Tag, Home, Brain} from "lucide-react";
-import { getServerSession } from "next-auth";
-import Link from "next/link";
-import { redirect } from "next/navigation";
-
-function SidebarItem({
- name,
- logo,
- path,
-}: {
- name: string;
- logo: React.ReactNode;
- path: string;
-}) {
- return (
- <li className="rounded-lg px-3 py-2 hover:bg-slate-100">
- <Link href={path} className="flex w-full space-x-2">
- {logo}
- <span className="my-auto"> {name} </span>
- </Link>
- </li>
- );
-}
-
-export default async function Sidebar() {
- const session = await getServerSession(authOptions);
- if (!session) {
- redirect("/");
- }
-
- return (
- <aside className="flex flex-col h-full w-60 border-r p-4">
- <div className="flex px-1 mb-5 items-center rounded-lg text-slate-900">
- <Brain />
- <span className="ml-2 text-base font-semibold">Remember</span>
- </div>
- <hr />
- <div>
- <ul className="space-y-2 mt-5 text-sm font-medium">
- <SidebarItem logo={<Home />} name="Home" path="#" />
- <SidebarItem logo={<Star />} name="Favourites" path="#" />
- <SidebarItem logo={<Archive />} name="Archived" path="#" />
- <SidebarItem logo={<Tag />} name="Tags" path="#" />
- </ul>
- </div>
- <div className="mt-auto flex justify-between">
- <div className="my-auto"> {session.user.name} </div>
- <Button variant="ghost" className="h-10 w-30">
- <MoreHorizontal />
- </Button>
- </div>
- </aside>
- );
-}
diff --git a/web/app/dashboard/layout.tsx b/web/app/dashboard/layout.tsx
deleted file mode 100644
index 9b21271e..00000000
--- a/web/app/dashboard/layout.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import Bookmarks from "@/app/dashboard/bookmarks/page";
-import Sidebar from "@/app/dashboard/components/Sidebar";
-
-export default async function Dashboard() {
- return (
- <div className="flex w-screen h-screen">
- <div className="flex-none">
- <Sidebar />
- </div>
- <div className="flex-1 bg-gray-100">
- <Bookmarks />
- </div>
- </div>
- );
-}
diff --git a/web/app/favicon.ico b/web/app/favicon.ico
deleted file mode 100644
index 718d6fea..00000000
--- a/web/app/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/web/app/globals.css b/web/app/globals.css
deleted file mode 100644
index 8abdb15c..00000000
--- a/web/app/globals.css
+++ /dev/null
@@ -1,76 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
- :root {
- --background: 0 0% 100%;
- --foreground: 222.2 84% 4.9%;
-
- --card: 0 0% 100%;
- --card-foreground: 222.2 84% 4.9%;
-
- --popover: 0 0% 100%;
- --popover-foreground: 222.2 84% 4.9%;
-
- --primary: 222.2 47.4% 11.2%;
- --primary-foreground: 210 40% 98%;
-
- --secondary: 210 40% 96.1%;
- --secondary-foreground: 222.2 47.4% 11.2%;
-
- --muted: 210 40% 96.1%;
- --muted-foreground: 215.4 16.3% 46.9%;
-
- --accent: 210 40% 96.1%;
- --accent-foreground: 222.2 47.4% 11.2%;
-
- --destructive: 0 84.2% 60.2%;
- --destructive-foreground: 210 40% 98%;
-
- --border: 214.3 31.8% 91.4%;
- --input: 214.3 31.8% 91.4%;
- --ring: 222.2 84% 4.9%;
-
- --radius: 0.5rem;
- }
-
- .dark {
- --background: 222.2 84% 4.9%;
- --foreground: 210 40% 98%;
-
- --card: 222.2 84% 4.9%;
- --card-foreground: 210 40% 98%;
-
- --popover: 222.2 84% 4.9%;
- --popover-foreground: 210 40% 98%;
-
- --primary: 210 40% 98%;
- --primary-foreground: 222.2 47.4% 11.2%;
-
- --secondary: 217.2 32.6% 17.5%;
- --secondary-foreground: 210 40% 98%;
-
- --muted: 217.2 32.6% 17.5%;
- --muted-foreground: 215 20.2% 65.1%;
-
- --accent: 217.2 32.6% 17.5%;
- --accent-foreground: 210 40% 98%;
-
- --destructive: 0 62.8% 30.6%;
- --destructive-foreground: 210 40% 98%;
-
- --border: 217.2 32.6% 17.5%;
- --input: 217.2 32.6% 17.5%;
- --ring: 212.7 26.8% 83.9%;
- }
-}
-
-@layer base {
- * {
- @apply border-border;
- }
- body {
- @apply bg-background text-foreground;
- }
-}
diff --git a/web/app/layout.tsx b/web/app/layout.tsx
deleted file mode 100644
index a2d34046..00000000
--- a/web/app/layout.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import type { Metadata } from "next";
-import { Inter } from "next/font/google";
-import "./globals.css";
-import React from "react";
-import { Toaster } from "@/components/ui/toaster";
-
-const inter = Inter({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Remember",
- description: "Your AI powered second brain",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
- <html lang="en">
- <body className={inter.className}>
- {children}
- <Toaster />
- </body>
- </html>
- );
-}
diff --git a/web/app/page.tsx b/web/app/page.tsx
deleted file mode 100644
index ffc128a5..00000000
--- a/web/app/page.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { LoginButton } from "@/components/auth/login";
-import { LogoutButton } from "@/components/auth/logout";
-import Link from "next/link";
-
-export default function Home() {
- return (
- <main className="flex min-h-screen flex-col items-center justify-between p-24">
- <div>
- <LoginButton />
- <br />
- <br />
- <LogoutButton />
- <br />
- <br />
- <Link href="/bookmarks">Bookmarks</Link>
- </div>
- </main>
- );
-}
diff --git a/web/bun.lockb b/web/bun.lockb
deleted file mode 100755
index 7925e942..00000000
--- a/web/bun.lockb
+++ /dev/null
Binary files differ
diff --git a/web/components.json b/web/components.json
deleted file mode 100644
index fa674c93..00000000
--- a/web/components.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "default",
- "rsc": true,
- "tsx": true,
- "tailwind": {
- "config": "tailwind.config.ts",
- "css": "app/globals.css",
- "baseColor": "slate",
- "cssVariables": true,
- "prefix": ""
- },
- "aliases": {
- "components": "@/components",
- "utils": "@/lib/utils"
- }
-}
diff --git a/web/components/auth/login.tsx b/web/components/auth/login.tsx
deleted file mode 100644
index 4cd55546..00000000
--- a/web/components/auth/login.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-"use client";
-import { signIn } from "next-auth/react";
-
-export const LoginButton = () => {
- return (
- <button
- className="btn btn-primary"
- onClick={() =>
- signIn(undefined, {
- callbackUrl: "/",
- })
- }
- >
- Sign in
- </button>
- );
-};
diff --git a/web/components/auth/logout.tsx b/web/components/auth/logout.tsx
deleted file mode 100644
index 8d627f68..00000000
--- a/web/components/auth/logout.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-"use client";
-import { signOut } from "next-auth/react";
-
-export const LogoutButton = () => {
- return (
- <button
- className="btn btn-ghost normal-case"
- onClick={() =>
- signOut({
- callbackUrl: "/",
- })
- }
- >
- Sign Out
- </button>
- );
-};
diff --git a/web/components/ui/badge.tsx b/web/components/ui/badge.tsx
deleted file mode 100644
index d3d5d604..00000000
--- a/web/components/ui/badge.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as React from "react";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "@/lib/utils";
-
-const badgeVariants = cva(
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
- {
- variants: {
- variant: {
- default:
- "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
- secondary:
- "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
- destructive:
- "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
- outline: "text-foreground",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- },
-);
-
-export interface BadgeProps
- extends React.HTMLAttributes<HTMLDivElement>,
- VariantProps<typeof badgeVariants> {}
-
-function Badge({ className, variant, ...props }: BadgeProps) {
- return (
- <div className={cn(badgeVariants({ variant }), className)} {...props} />
- );
-}
-
-export { Badge, badgeVariants };
diff --git a/web/components/ui/button.tsx b/web/components/ui/button.tsx
deleted file mode 100644
index 57c9fe47..00000000
--- a/web/components/ui/button.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "@/lib/utils";
-
-const buttonVariants = cva(
- "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
- {
- variants: {
- variant: {
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
- destructive:
- "bg-destructive text-destructive-foreground hover:bg-destructive/90",
- outline:
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
- secondary:
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
- ghost: "hover:bg-accent hover:text-accent-foreground",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-10 px-4 py-2",
- sm: "h-9 rounded-md px-3",
- lg: "h-11 rounded-md px-8",
- icon: "h-10 w-10",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- },
-);
-
-export interface ButtonProps
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
- VariantProps<typeof buttonVariants> {
- asChild?: boolean;
-}
-
-const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
- ({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button";
- return (
- <Comp
- className={cn(buttonVariants({ variant, size, className }))}
- ref={ref}
- {...props}
- />
- );
- },
-);
-Button.displayName = "Button";
-
-export { Button, buttonVariants };
diff --git a/web/components/ui/card.tsx b/web/components/ui/card.tsx
deleted file mode 100644
index dc3b01de..00000000
--- a/web/components/ui/card.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-const Card = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn(
- "rounded-lg border bg-card text-card-foreground shadow-sm",
- className,
- )}
- {...props}
- />
-));
-Card.displayName = "Card";
-
-const CardHeader = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn("flex flex-col space-y-1.5 p-6", className)}
- {...props}
- />
-));
-CardHeader.displayName = "CardHeader";
-
-const CardTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLHeadingElement>
->(({ className, ...props }, ref) => (
- <h3
- ref={ref}
- className={cn(
- "text-2xl font-semibold leading-none tracking-tight",
- className,
- )}
- {...props}
- />
-));
-CardTitle.displayName = "CardTitle";
-
-const CardDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
->(({ className, ...props }, ref) => (
- <p
- ref={ref}
- className={cn("text-sm text-muted-foreground", className)}
- {...props}
- />
-));
-CardDescription.displayName = "CardDescription";
-
-const CardContent = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
-));
-CardContent.displayName = "CardContent";
-
-const CardFooter = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn("flex items-center p-6 pt-0", className)}
- {...props}
- />
-));
-CardFooter.displayName = "CardFooter";
-
-export {
- Card,
- CardHeader,
- CardFooter,
- CardTitle,
- CardDescription,
- CardContent,
-};
diff --git a/web/components/ui/dropdown-menu.tsx b/web/components/ui/dropdown-menu.tsx
deleted file mode 100644
index 3a0c7fed..00000000
--- a/web/components/ui/dropdown-menu.tsx
+++ /dev/null
@@ -1,200 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
-import { Check, ChevronRight, Circle } from "lucide-react";
-
-import { cn } from "@/lib/utils";
-
-const DropdownMenu = DropdownMenuPrimitive.Root;
-
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
-
-const DropdownMenuGroup = DropdownMenuPrimitive.Group;
-
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
-
-const DropdownMenuSub = DropdownMenuPrimitive.Sub;
-
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
-
-const DropdownMenuSubTrigger = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
- inset?: boolean;
- }
->(({ className, inset, children, ...props }, ref) => (
- <DropdownMenuPrimitive.SubTrigger
- ref={ref}
- className={cn(
- "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
- inset && "pl-8",
- className,
- )}
- {...props}
- >
- {children}
- <ChevronRight className="ml-auto h-4 w-4" />
- </DropdownMenuPrimitive.SubTrigger>
-));
-DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName;
-
-const DropdownMenuSubContent = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
->(({ className, ...props }, ref) => (
- <DropdownMenuPrimitive.SubContent
- ref={ref}
- className={cn(
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
- className,
- )}
- {...props}
- />
-));
-DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName;
-
-const DropdownMenuContent = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
->(({ className, sideOffset = 4, ...props }, ref) => (
- <DropdownMenuPrimitive.Portal>
- <DropdownMenuPrimitive.Content
- ref={ref}
- sideOffset={sideOffset}
- className={cn(
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
- className,
- )}
- {...props}
- />
- </DropdownMenuPrimitive.Portal>
-));
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
-
-const DropdownMenuItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Item>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
- <DropdownMenuPrimitive.Item
- ref={ref}
- className={cn(
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
- inset && "pl-8",
- className,
- )}
- {...props}
- />
-));
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
-
-const DropdownMenuCheckboxItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
->(({ className, children, checked, ...props }, ref) => (
- <DropdownMenuPrimitive.CheckboxItem
- ref={ref}
- className={cn(
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
- className,
- )}
- checked={checked}
- {...props}
- >
- <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
- <DropdownMenuPrimitive.ItemIndicator>
- <Check className="h-4 w-4" />
- </DropdownMenuPrimitive.ItemIndicator>
- </span>
- {children}
- </DropdownMenuPrimitive.CheckboxItem>
-));
-DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName;
-
-const DropdownMenuRadioItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
->(({ className, children, ...props }, ref) => (
- <DropdownMenuPrimitive.RadioItem
- ref={ref}
- className={cn(
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
- className,
- )}
- {...props}
- >
- <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
- <DropdownMenuPrimitive.ItemIndicator>
- <Circle className="h-2 w-2 fill-current" />
- </DropdownMenuPrimitive.ItemIndicator>
- </span>
- {children}
- </DropdownMenuPrimitive.RadioItem>
-));
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
-
-const DropdownMenuLabel = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Label>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
- <DropdownMenuPrimitive.Label
- ref={ref}
- className={cn(
- "px-2 py-1.5 text-sm font-semibold",
- inset && "pl-8",
- className,
- )}
- {...props}
- />
-));
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
-
-const DropdownMenuSeparator = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
->(({ className, ...props }, ref) => (
- <DropdownMenuPrimitive.Separator
- ref={ref}
- className={cn("-mx-1 my-1 h-px bg-muted", className)}
- {...props}
- />
-));
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
-
-const DropdownMenuShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes<HTMLSpanElement>) => {
- return (
- <span
- className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
- {...props}
- />
- );
-};
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
-
-export {
- DropdownMenu,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuCheckboxItem,
- DropdownMenuRadioItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuGroup,
- DropdownMenuPortal,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuRadioGroup,
-};
diff --git a/web/components/ui/form.tsx b/web/components/ui/form.tsx
deleted file mode 100644
index 4603f8b3..00000000
--- a/web/components/ui/form.tsx
+++ /dev/null
@@ -1,176 +0,0 @@
-import * as React from "react"
-import * as LabelPrimitive from "@radix-ui/react-label"
-import { Slot } from "@radix-ui/react-slot"
-import {
- Controller,
- ControllerProps,
- FieldPath,
- FieldValues,
- FormProvider,
- useFormContext,
-} from "react-hook-form"
-
-import { cn } from "@/lib/utils"
-import { Label } from "@/components/ui/label"
-
-const Form = FormProvider
-
-type FormFieldContextValue<
- TFieldValues extends FieldValues = FieldValues,
- TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
-> = {
- name: TName
-}
-
-const FormFieldContext = React.createContext<FormFieldContextValue>(
- {} as FormFieldContextValue
-)
-
-const FormField = <
- TFieldValues extends FieldValues = FieldValues,
- TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
->({
- ...props
-}: ControllerProps<TFieldValues, TName>) => {
- return (
- <FormFieldContext.Provider value={{ name: props.name }}>
- <Controller {...props} />
- </FormFieldContext.Provider>
- )
-}
-
-const useFormField = () => {
- const fieldContext = React.useContext(FormFieldContext)
- const itemContext = React.useContext(FormItemContext)
- const { getFieldState, formState } = useFormContext()
-
- const fieldState = getFieldState(fieldContext.name, formState)
-
- if (!fieldContext) {
- throw new Error("useFormField should be used within <FormField>")
- }
-
- const { id } = itemContext
-
- return {
- id,
- name: fieldContext.name,
- formItemId: `${id}-form-item`,
- formDescriptionId: `${id}-form-item-description`,
- formMessageId: `${id}-form-item-message`,
- ...fieldState,
- }
-}
-
-type FormItemContextValue = {
- id: string
-}
-
-const FormItemContext = React.createContext<FormItemContextValue>(
- {} as FormItemContextValue
-)
-
-const FormItem = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => {
- const id = React.useId()
-
- return (
- <FormItemContext.Provider value={{ id }}>
- <div ref={ref} className={cn("space-y-2", className)} {...props} />
- </FormItemContext.Provider>
- )
-})
-FormItem.displayName = "FormItem"
-
-const FormLabel = React.forwardRef<
- React.ElementRef<typeof LabelPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
->(({ className, ...props }, ref) => {
- const { error, formItemId } = useFormField()
-
- return (
- <Label
- ref={ref}
- className={cn(error && "text-destructive", className)}
- htmlFor={formItemId}
- {...props}
- />
- )
-})
-FormLabel.displayName = "FormLabel"
-
-const FormControl = React.forwardRef<
- React.ElementRef<typeof Slot>,
- React.ComponentPropsWithoutRef<typeof Slot>
->(({ ...props }, ref) => {
- const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
-
- return (
- <Slot
- ref={ref}
- id={formItemId}
- aria-describedby={
- !error
- ? `${formDescriptionId}`
- : `${formDescriptionId} ${formMessageId}`
- }
- aria-invalid={!!error}
- {...props}
- />
- )
-})
-FormControl.displayName = "FormControl"
-
-const FormDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
->(({ className, ...props }, ref) => {
- const { formDescriptionId } = useFormField()
-
- return (
- <p
- ref={ref}
- id={formDescriptionId}
- className={cn("text-sm text-muted-foreground", className)}
- {...props}
- />
- )
-})
-FormDescription.displayName = "FormDescription"
-
-const FormMessage = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
->(({ className, children, ...props }, ref) => {
- const { error, formMessageId } = useFormField()
- const body = error ? String(error?.message) : children
-
- if (!body) {
- return null
- }
-
- return (
- <p
- ref={ref}
- id={formMessageId}
- className={cn("text-sm font-medium text-destructive", className)}
- {...props}
- >
- {body}
- </p>
- )
-})
-FormMessage.displayName = "FormMessage"
-
-export {
- useFormField,
- Form,
- FormItem,
- FormLabel,
- FormControl,
- FormDescription,
- FormMessage,
- FormField,
-}
diff --git a/web/components/ui/imageCard.tsx b/web/components/ui/imageCard.tsx
deleted file mode 100644
index 1394ae08..00000000
--- a/web/components/ui/imageCard.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-export function ImageCard({
- children,
- image,
- className,
- ...props
-}: React.HTMLAttributes<HTMLDivElement> & { image?: string }) {
- return (
- <div
- className={cn("h-96 rounded-lg overflow-hidden shadow-md", className)}
- {...props}
- >
- <div
- className="h-3/5 bg-cover bg-center"
- style={{
- backgroundImage: image ? `url(${image})` : undefined,
- }}
- ></div>
- <div className="flex flex-col h-2/5 p-2">{children}</div>
- </div>
- );
-}
-
-export function ImageCardTitle({
- className,
- ...props
-}: React.HTMLAttributes<HTMLDivElement>) {
- return (
- <div
- className={cn("order-first flex-none font-bold text-lg", className)}
- {...props}
- />
- );
-}
-
-export function ImageCardBody({
- className,
- ...props
-}: React.HTMLAttributes<HTMLDivElement>) {
- return (
- <div
- className={cn("grow order-1 font-bold text-lg", className)}
- {...props}
- />
- );
-}
-
-export function ImageCardFooter({
- className,
- ...props
-}: React.HTMLAttributes<HTMLDivElement>) {
- return <div className={cn("order-last", className)} {...props} />;
-}
diff --git a/web/components/ui/input.tsx b/web/components/ui/input.tsx
deleted file mode 100644
index 9d631e7f..00000000
--- a/web/components/ui/input.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-export interface InputProps
- extends React.InputHTMLAttributes<HTMLInputElement> {}
-
-const Input = React.forwardRef<HTMLInputElement, InputProps>(
- ({ className, type, ...props }, ref) => {
- return (
- <input
- type={type}
- className={cn(
- "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
- className,
- )}
- ref={ref}
- {...props}
- />
- );
- },
-);
-Input.displayName = "Input";
-
-export { Input };
diff --git a/web/components/ui/label.tsx b/web/components/ui/label.tsx
deleted file mode 100644
index 53418217..00000000
--- a/web/components/ui/label.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-"use client"
-
-import * as React from "react"
-import * as LabelPrimitive from "@radix-ui/react-label"
-import { cva, type VariantProps } from "class-variance-authority"
-
-import { cn } from "@/lib/utils"
-
-const labelVariants = cva(
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
-)
-
-const Label = React.forwardRef<
- React.ElementRef<typeof LabelPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
- VariantProps<typeof labelVariants>
->(({ className, ...props }, ref) => (
- <LabelPrimitive.Root
- ref={ref}
- className={cn(labelVariants(), className)}
- {...props}
- />
-))
-Label.displayName = LabelPrimitive.Root.displayName
-
-export { Label }
diff --git a/web/components/ui/toast.tsx b/web/components/ui/toast.tsx
deleted file mode 100644
index 2bc23c1f..00000000
--- a/web/components/ui/toast.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import * as React from "react";
-import * as ToastPrimitives from "@radix-ui/react-toast";
-import { cva, type VariantProps } from "class-variance-authority";
-import { X } from "lucide-react";
-
-import { cn } from "@/lib/utils";
-
-const ToastProvider = ToastPrimitives.Provider;
-
-const ToastViewport = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Viewport>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Viewport
- ref={ref}
- className={cn(
- "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
- className,
- )}
- {...props}
- />
-));
-ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
-
-const toastVariants = cva(
- "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
- {
- variants: {
- variant: {
- default: "border bg-background text-foreground",
- destructive:
- "destructive group border-destructive bg-destructive text-destructive-foreground",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- },
-);
-
-const Toast = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Root>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
- VariantProps<typeof toastVariants>
->(({ className, variant, ...props }, ref) => {
- return (
- <ToastPrimitives.Root
- ref={ref}
- className={cn(toastVariants({ variant }), className)}
- {...props}
- />
- );
-});
-Toast.displayName = ToastPrimitives.Root.displayName;
-
-const ToastAction = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Action>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Action
- ref={ref}
- className={cn(
- "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
- className,
- )}
- {...props}
- />
-));
-ToastAction.displayName = ToastPrimitives.Action.displayName;
-
-const ToastClose = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Close>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Close
- ref={ref}
- className={cn(
- "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
- className,
- )}
- toast-close=""
- {...props}
- >
- <X className="h-4 w-4" />
- </ToastPrimitives.Close>
-));
-ToastClose.displayName = ToastPrimitives.Close.displayName;
-
-const ToastTitle = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Title>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Title
- ref={ref}
- className={cn("text-sm font-semibold", className)}
- {...props}
- />
-));
-ToastTitle.displayName = ToastPrimitives.Title.displayName;
-
-const ToastDescription = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Description>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Description
- ref={ref}
- className={cn("text-sm opacity-90", className)}
- {...props}
- />
-));
-ToastDescription.displayName = ToastPrimitives.Description.displayName;
-
-type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
-
-type ToastActionElement = React.ReactElement<typeof ToastAction>;
-
-export {
- type ToastProps,
- type ToastActionElement,
- ToastProvider,
- ToastViewport,
- Toast,
- ToastTitle,
- ToastDescription,
- ToastClose,
- ToastAction,
-};
diff --git a/web/components/ui/toaster.tsx b/web/components/ui/toaster.tsx
deleted file mode 100644
index 7d82ed55..00000000
--- a/web/components/ui/toaster.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-
-import {
- Toast,
- ToastClose,
- ToastDescription,
- ToastProvider,
- ToastTitle,
- ToastViewport,
-} from "@/components/ui/toast";
-import { useToast } from "@/components/ui/use-toast";
-
-export function Toaster() {
- const { toasts } = useToast();
-
- return (
- <ToastProvider>
- {toasts.map(function ({ id, title, description, action, ...props }) {
- return (
- <Toast key={id} {...props}>
- <div className="grid gap-1">
- {title && <ToastTitle>{title}</ToastTitle>}
- {description && (
- <ToastDescription>{description}</ToastDescription>
- )}
- </div>
- {action}
- <ToastClose />
- </Toast>
- );
- })}
- <ToastViewport />
- </ToastProvider>
- );
-}
diff --git a/web/components/ui/use-toast.ts b/web/components/ui/use-toast.ts
deleted file mode 100644
index 5491e140..00000000
--- a/web/components/ui/use-toast.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-// Inspired by react-hot-toast library
-import * as React from "react";
-
-import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
-
-const TOAST_LIMIT = 1;
-const TOAST_REMOVE_DELAY = 1000000;
-
-type ToasterToast = ToastProps & {
- id: string;
- title?: React.ReactNode;
- description?: React.ReactNode;
- action?: ToastActionElement;
-};
-
-const actionTypes = {
- ADD_TOAST: "ADD_TOAST",
- UPDATE_TOAST: "UPDATE_TOAST",
- DISMISS_TOAST: "DISMISS_TOAST",
- REMOVE_TOAST: "REMOVE_TOAST",
-} as const;
-
-let count = 0;
-
-function genId() {
- count = (count + 1) % Number.MAX_SAFE_INTEGER;
- return count.toString();
-}
-
-type ActionType = typeof actionTypes;
-
-type Action =
- | {
- type: ActionType["ADD_TOAST"];
- toast: ToasterToast;
- }
- | {
- type: ActionType["UPDATE_TOAST"];
- toast: Partial<ToasterToast>;
- }
- | {
- type: ActionType["DISMISS_TOAST"];
- toastId?: ToasterToast["id"];
- }
- | {
- type: ActionType["REMOVE_TOAST"];
- toastId?: ToasterToast["id"];
- };
-
-interface State {
- toasts: ToasterToast[];
-}
-
-const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
-
-const addToRemoveQueue = (toastId: string) => {
- if (toastTimeouts.has(toastId)) {
- return;
- }
-
- const timeout = setTimeout(() => {
- toastTimeouts.delete(toastId);
- dispatch({
- type: "REMOVE_TOAST",
- toastId: toastId,
- });
- }, TOAST_REMOVE_DELAY);
-
- toastTimeouts.set(toastId, timeout);
-};
-
-export const reducer = (state: State, action: Action): State => {
- switch (action.type) {
- case "ADD_TOAST":
- return {
- ...state,
- toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
- };
-
- case "UPDATE_TOAST":
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === action.toast.id ? { ...t, ...action.toast } : t,
- ),
- };
-
- case "DISMISS_TOAST": {
- const { toastId } = action;
-
- // ! Side effects ! - This could be extracted into a dismissToast() action,
- // but I'll keep it here for simplicity
- if (toastId) {
- addToRemoveQueue(toastId);
- } else {
- state.toasts.forEach((toast) => {
- addToRemoveQueue(toast.id);
- });
- }
-
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === toastId || toastId === undefined
- ? {
- ...t,
- open: false,
- }
- : t,
- ),
- };
- }
- case "REMOVE_TOAST":
- if (action.toastId === undefined) {
- return {
- ...state,
- toasts: [],
- };
- }
- return {
- ...state,
- toasts: state.toasts.filter((t) => t.id !== action.toastId),
- };
- }
-};
-
-const listeners: Array<(_state: State) => void> = [];
-
-let memoryState: State = { toasts: [] };
-
-function dispatch(action: Action) {
- memoryState = reducer(memoryState, action);
- listeners.forEach((listener) => {
- listener(memoryState);
- });
-}
-
-type Toast = Omit<ToasterToast, "id">;
-
-function toast({ ...props }: Toast) {
- const id = genId();
-
- const update = (props: ToasterToast) =>
- dispatch({
- type: "UPDATE_TOAST",
- toast: { ...props, id },
- });
- const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
-
- dispatch({
- type: "ADD_TOAST",
- toast: {
- ...props,
- id,
- open: true,
- onOpenChange: (open) => {
- if (!open) dismiss();
- },
- },
- });
-
- return {
- id: id,
- dismiss,
- update,
- };
-}
-
-function useToast() {
- const [state, setState] = React.useState<State>(memoryState);
-
- React.useEffect(() => {
- listeners.push(setState);
- return () => {
- const index = listeners.indexOf(setState);
- if (index > -1) {
- listeners.splice(index, 1);
- }
- };
- }, [state]);
-
- return {
- ...state,
- toast,
- dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
- };
-}
-
-export { useToast, toast };
diff --git a/web/lib/api.ts b/web/lib/api.ts
deleted file mode 100644
index 56686cde..00000000
--- a/web/lib/api.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-"use client";
-
-import { ZodTypeAny, z } from "zod";
-import {
- ZNewBookmarkedLinkRequest,
- zGetLinksResponseSchema,
-} from "./types/api/links";
-
-import serverConfig from "./config";
-
-const BASE_URL = `${serverConfig.api_url}/api/v1`;
-
-export type FetchError = {
- status?: number;
- message?: string;
-};
-
-type InputSchema<T> = T extends ZodTypeAny ? T : undefined;
-
-async function doRequest<T>(
- path: string,
- respSchema?: InputSchema<T>,
- opts?: RequestInit,
-): Promise<
- | (InputSchema<T> extends ZodTypeAny
- ? [z.infer<InputSchema<T>>, undefined]
- : [undefined, undefined])
- | [undefined, FetchError]
-> {
- try {
- const res = await fetch(`${BASE_URL}${path}`, opts);
- if (!res.ok) {
- return [
- undefined,
- { status: res.status, message: await res.text() },
- ] as const;
- }
- if (!respSchema) {
- return [undefined, undefined] as const;
- }
-
- let parsed = respSchema.safeParse(await res.json());
- if (!parsed.success) {
- return [
- undefined,
- { message: `Failed to parse response: ${parsed.error.toString()}` },
- ] as const;
- }
-
- return [parsed.data, undefined] as const;
- } catch (error: any) {
- return [
- undefined,
- { message: `Failed to execute fetch request: ${error}` },
- ] as const;
- }
-}
-
-export default class APIClient {
- static async getLinks() {
- return await doRequest(`/links`, zGetLinksResponseSchema, {
- next: { tags: ["links"] },
- });
- }
-
- static async bookmarkLink(url: string) {
- const body: ZNewBookmarkedLinkRequest = {
- url,
- };
- return await doRequest(`/links`, undefined, {
- method: "POST",
- body: JSON.stringify(body),
- });
- }
-
- static async unbookmarkLink(linkId: string) {
- return await doRequest(`/links/${linkId}`, undefined, {
- method: "DELETE",
- });
- }
-}
diff --git a/web/lib/auth.ts b/web/lib/auth.ts
deleted file mode 100644
index cd6404de..00000000
--- a/web/lib/auth.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import NextAuth, { NextAuthOptions } from "next-auth";
-import { PrismaAdapter } from "@next-auth/prisma-adapter";
-import AuthentikProvider from "next-auth/providers/authentik";
-import serverConfig from "@/lib/config";
-import prisma from "@remember/db";
-
-let providers = [];
-
-if (serverConfig.auth.authentik) {
- providers.push(AuthentikProvider(serverConfig.auth.authentik));
-}
-
-export const authOptions: NextAuthOptions = {
- // Configure one or more authentication providers
- adapter: PrismaAdapter(prisma),
- providers: providers,
- callbacks: {
- session({ session, user }) {
- session.user = { ...user };
- return session;
- },
- },
-};
-
-export const authHandler = NextAuth(authOptions);
diff --git a/web/lib/config.ts b/web/lib/config.ts
deleted file mode 100644
index ec042b54..00000000
--- a/web/lib/config.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-function buildAuthentikConfig() {
- let { AUTHENTIK_ID, AUTHENTIK_SECRET, AUTHENTIK_ISSUER } = process.env;
-
- if (!AUTHENTIK_ID || !AUTHENTIK_SECRET || !AUTHENTIK_ISSUER) {
- return undefined;
- }
-
- return {
- clientId: AUTHENTIK_ID,
- clientSecret: AUTHENTIK_SECRET,
- issuer: AUTHENTIK_ISSUER,
- };
-}
-
-const serverConfig = {
- api_url: process.env.API_URL || "http://localhost:3000",
- auth: {
- authentik: buildAuthentikConfig(),
- },
-};
-
-export default serverConfig;
diff --git a/web/lib/services/links.ts b/web/lib/services/links.ts
deleted file mode 100644
index d273b118..00000000
--- a/web/lib/services/links.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { LinkCrawlerQueue } from "@remember/shared/queues";
-import prisma from "@remember/db";
-import { ZBookmarkedLink } from "@/lib/types/api/links";
-
-const defaultLinkFields = {
- id: true,
- url: true,
- createdAt: true,
- details: {
- select: {
- title: true,
- description: true,
- imageUrl: true,
- favicon: true,
- },
- },
- tags: {
- include: {
- tag: true,
- },
- },
-};
-
-async function dummyPrismaReturnType() {
- return await prisma.bookmarkedLink.findFirstOrThrow({
- select: defaultLinkFields,
- });
-}
-
-function toZodSchema(
- link: Awaited<ReturnType<typeof dummyPrismaReturnType>>,
-): ZBookmarkedLink {
- return {
- id: link.id,
- url: link.url,
- createdAt: link.createdAt,
- details: link.details,
- tags: link.tags.map((t) => t.tag),
- };
-}
-
-export async function unbookmarkLink(linkId: string, userId: string) {
- await prisma.bookmarkedLink.delete({
- where: {
- id: linkId,
- userId,
- },
- });
-}
-
-export async function bookmarkLink(url: string, userId: string) {
- const link = await prisma.bookmarkedLink.create({
- data: {
- url,
- userId,
- },
- select: defaultLinkFields,
- });
-
- // Enqueue crawling request
- await LinkCrawlerQueue.add("crawl", {
- linkId: link.id,
- url: link.url,
- });
-
- return toZodSchema(link);
-}
-
-export async function getLinks(userId: string) {
- return (
- await prisma.bookmarkedLink.findMany({
- where: {
- userId,
- },
- select: defaultLinkFields,
- })
- ).map(toZodSchema);
-}
diff --git a/web/lib/types/api/links.ts b/web/lib/types/api/links.ts
deleted file mode 100644
index f84445f6..00000000
--- a/web/lib/types/api/links.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { z } from "zod";
-import { zBookmarkTagSchema } from "@/lib/types/api/tags";
-
-export const zBookmarkedLinkSchema = z.object({
- id: z.string(),
- url: z.string().url(),
- createdAt: z.coerce.date(),
-
- details: z
- .object({
- title: z.string().nullish(),
- description: z.string().nullish(),
- imageUrl: z.string().url().nullish(),
- favicon: z.string().url().nullish(),
- })
- .nullish(),
- tags: z.array(zBookmarkTagSchema),
-});
-export type ZBookmarkedLink = z.infer<typeof zBookmarkedLinkSchema>;
-
-// POST /v1/links
-export const zNewBookmarkedLinkRequestSchema = zBookmarkedLinkSchema.pick({
- url: true,
-});
-export type ZNewBookmarkedLinkRequest = z.infer<
- typeof zNewBookmarkedLinkRequestSchema
->;
-
-// GET /v1/links
-export const zGetLinksResponseSchema = z.object({
- links: z.array(zBookmarkedLinkSchema),
-});
-export type ZGetLinksResponse = z.infer<typeof zGetLinksResponseSchema>;
diff --git a/web/lib/types/api/tags.ts b/web/lib/types/api/tags.ts
deleted file mode 100644
index f2d2bc18..00000000
--- a/web/lib/types/api/tags.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { z } from "zod";
-
-export const zBookmarkTagSchema = z.object({
- id: z.string(),
- name: z.string(),
-});
diff --git a/web/lib/types/next-auth.d.ts b/web/lib/types/next-auth.d.ts
deleted file mode 100644
index cd47dfce..00000000
--- a/web/lib/types/next-auth.d.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { DefaultSession } from "next-auth";
-
-declare module "next-auth" {
- /**
- * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
- */
- export interface Session {
- user: {
- id: string;
- } & DefaultSession["user"];
- }
-}
diff --git a/web/lib/utils.ts b/web/lib/utils.ts
deleted file mode 100644
index 365058ce..00000000
--- a/web/lib/utils.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { type ClassValue, clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
diff --git a/web/next.config.mjs b/web/next.config.mjs
deleted file mode 100644
index 4678774e..00000000
--- a/web/next.config.mjs
+++ /dev/null
@@ -1,4 +0,0 @@
-/** @type {import('next').NextConfig} */
-const nextConfig = {};
-
-export default nextConfig;
diff --git a/web/package.json b/web/package.json
deleted file mode 100644
index 8a315beb..00000000
--- a/web/package.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/package.json",
- "name": "@remember/web",
- "version": "0.1.0",
- "private": true,
- "scripts": {
- "dev": "next dev",
- "build": "next build",
- "start": "next start",
- "lint": "next lint"
- },
- "dependencies": {
- "@hookform/resolvers": "^3.3.4",
- "@next-auth/prisma-adapter": "^1.0.7",
- "@next/eslint-plugin-next": "^14.1.0",
- "@radix-ui/react-dropdown-menu": "^2.0.6",
- "@radix-ui/react-label": "^2.0.2",
- "@radix-ui/react-slot": "^1.0.2",
- "@radix-ui/react-toast": "^1.1.5",
- "class-variance-authority": "^0.7.0",
- "clsx": "^2.1.0",
- "install": "^0.13.0",
- "lucide-react": "^0.322.0",
- "next": "14.1.0",
- "next-auth": "^4.24.5",
- "prettier": "^3.2.5",
- "react": "^18",
- "react-dom": "^18",
- "react-hook-form": "^7.50.1",
- "tailwind-merge": "^2.2.1",
- "tailwindcss-animate": "^1.0.7",
- "zod": "^3.22.4"
- },
- "devDependencies": {
- "@types/react": "^18",
- "@types/react-dom": "^18",
- "autoprefixer": "^10.0.1",
- "postcss": "^8",
- "tailwindcss": "^3.3.0"
- }
-}
diff --git a/web/postcss.config.js b/web/postcss.config.js
deleted file mode 100644
index 12a703d9..00000000
--- a/web/postcss.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-};
diff --git a/web/public/next.svg b/web/public/next.svg
deleted file mode 100644
index 5174b28c..00000000
--- a/web/public/next.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg> \ No newline at end of file
diff --git a/web/public/vercel.svg b/web/public/vercel.svg
deleted file mode 100644
index d2f84222..00000000
--- a/web/public/vercel.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg> \ No newline at end of file
diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts
deleted file mode 100644
index 41668a3b..00000000
--- a/web/tailwind.config.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import type { Config } from "tailwindcss";
-
-const config = {
- darkMode: ["class"],
- content: [
- "./pages/**/*.{ts,tsx}",
- "./components/**/*.{ts,tsx}",
- "./app/**/*.{ts,tsx}",
- "./src/**/*.{ts,tsx}",
- ],
- prefix: "",
- theme: {
- container: {
- center: true,
- padding: "2rem",
- screens: {
- "2xl": "1400px",
- },
- },
- extend: {
- colors: {
- border: "hsl(var(--border))",
- input: "hsl(var(--input))",
- ring: "hsl(var(--ring))",
- background: "hsl(var(--background))",
- foreground: "hsl(var(--foreground))",
- primary: {
- DEFAULT: "hsl(var(--primary))",
- foreground: "hsl(var(--primary-foreground))",
- },
- secondary: {
- DEFAULT: "hsl(var(--secondary))",
- foreground: "hsl(var(--secondary-foreground))",
- },
- destructive: {
- DEFAULT: "hsl(var(--destructive))",
- foreground: "hsl(var(--destructive-foreground))",
- },
- muted: {
- DEFAULT: "hsl(var(--muted))",
- foreground: "hsl(var(--muted-foreground))",
- },
- accent: {
- DEFAULT: "hsl(var(--accent))",
- foreground: "hsl(var(--accent-foreground))",
- },
- popover: {
- DEFAULT: "hsl(var(--popover))",
- foreground: "hsl(var(--popover-foreground))",
- },
- card: {
- DEFAULT: "hsl(var(--card))",
- foreground: "hsl(var(--card-foreground))",
- },
- },
- borderRadius: {
- lg: "var(--radius)",
- md: "calc(var(--radius) - 2px)",
- sm: "calc(var(--radius) - 4px)",
- },
- keyframes: {
- "accordion-down": {
- from: { height: "0" },
- to: { height: "var(--radix-accordion-content-height)" },
- },
- "accordion-up": {
- from: { height: "var(--radix-accordion-content-height)" },
- to: { height: "0" },
- },
- },
- animation: {
- "accordion-down": "accordion-down 0.2s ease-out",
- "accordion-up": "accordion-up 0.2s ease-out",
- },
- },
- },
- plugins: [require("tailwindcss-animate")],
-} satisfies Config;
-
-export default config;
diff --git a/web/tsconfig.json b/web/tsconfig.json
deleted file mode 100644
index a25dbc14..00000000
--- a/web/tsconfig.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/tsconfig",
- "compilerOptions": {
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ],
- "paths": {
- "@/*": ["./*"]
- }
- },
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
-}