From bf601846db7411a2bfce2097554d31deb34354fc Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 19 Aug 2026 09:33:48 +0200 Subject: [PATCH] new frontend ik ben zo cool --- frontend(experimental)/newchat/README.md | 33 + .../newchat/app/globals.css | 62 + frontend(experimental)/newchat/app/layout.tsx | 27 + .../newchat/app/loading.tsx | 4 + frontend(experimental)/newchat/app/page.tsx | 229 + .../newchat/components.json | 21 + .../newchat/components/auth-screen.tsx | 161 + .../components/create-event-dialog.tsx | 265 ++ .../components/event-detail-dialog.tsx | 205 + .../newchat/components/glass-modal.tsx | 58 + .../newchat/components/room-dialogs.tsx | 224 + .../components/room-settings-dialog.tsx | 271 ++ .../newchat/components/sidebar.tsx | 220 + .../newchat/components/theme-provider.tsx | 11 + .../newchat/components/ui/accordion.tsx | 58 + .../newchat/components/ui/alert-dialog.tsx | 141 + .../newchat/components/ui/alert.tsx | 59 + .../newchat/components/ui/aspect-ratio.tsx | 7 + .../newchat/components/ui/avatar.tsx | 50 + .../newchat/components/ui/badge.tsx | 36 + .../newchat/components/ui/breadcrumb.tsx | 115 + .../newchat/components/ui/button-group.tsx | 83 + .../newchat/components/ui/button.tsx | 56 + .../newchat/components/ui/calendar.tsx | 213 + .../newchat/components/ui/card.tsx | 79 + .../newchat/components/ui/carousel.tsx | 262 + .../newchat/components/ui/chart.tsx | 363 ++ .../newchat/components/ui/checkbox.tsx | 28 + .../newchat/components/ui/collapsible.tsx | 11 + .../newchat/components/ui/command.tsx | 153 + .../newchat/components/ui/context-menu.tsx | 200 + .../newchat/components/ui/dialog.tsx | 122 + .../newchat/components/ui/drawer.tsx | 118 + .../newchat/components/ui/dropdown-menu.tsx | 200 + .../newchat/components/ui/empty.tsx | 104 + .../newchat/components/ui/field.tsx | 244 + .../newchat/components/ui/form.tsx | 178 + .../newchat/components/ui/hover-card.tsx | 29 + .../newchat/components/ui/input-group.tsx | 170 + .../newchat/components/ui/input-otp.tsx | 71 + .../newchat/components/ui/input.tsx | 22 + .../newchat/components/ui/item.tsx | 193 + .../newchat/components/ui/kbd.tsx | 28 + .../newchat/components/ui/label.tsx | 26 + .../newchat/components/ui/menubar.tsx | 236 + .../newchat/components/ui/navigation-menu.tsx | 128 + .../newchat/components/ui/pagination.tsx | 117 + .../newchat/components/ui/popover.tsx | 31 + .../newchat/components/ui/progress.tsx | 28 + .../newchat/components/ui/radio-group.tsx | 44 + .../newchat/components/ui/resizable.tsx | 45 + .../newchat/components/ui/scroll-area.tsx | 48 + .../newchat/components/ui/select.tsx | 160 + .../newchat/components/ui/separator.tsx | 31 + .../newchat/components/ui/sheet.tsx | 140 + .../newchat/components/ui/sidebar.tsx | 771 +++ .../newchat/components/ui/skeleton.tsx | 15 + .../newchat/components/ui/slider.tsx | 28 + .../newchat/components/ui/sonner.tsx | 31 + .../newchat/components/ui/spinner.tsx | 16 + .../newchat/components/ui/switch.tsx | 27 + .../newchat/components/ui/table.tsx | 117 + .../newchat/components/ui/tabs.tsx | 55 + .../newchat/components/ui/textarea.tsx | 22 + .../newchat/components/ui/toast.tsx | 129 + .../newchat/components/ui/toaster.tsx | 35 + .../newchat/components/ui/toggle-group.tsx | 61 + .../newchat/components/ui/toggle.tsx | 45 + .../newchat/components/ui/tooltip.tsx | 30 + .../newchat/components/ui/use-mobile.tsx | 19 + .../newchat/components/ui/use-toast.ts | 191 + .../newchat/components/week-view.tsx | 225 + .../newchat/hooks/use-mobile.ts | 19 + .../newchat/hooks/use-toast.ts | 191 + frontend(experimental)/newchat/lib/api.ts | 274 ++ .../newchat/lib/auth-context.tsx | 81 + .../newchat/lib/date-utils.ts | 69 + frontend(experimental)/newchat/lib/hooks.ts | 36 + .../newchat/lib/room-theme.ts | 32 + frontend(experimental)/newchat/lib/types.ts | 61 + frontend(experimental)/newchat/lib/utils.ts | 6 + .../newchat/next.config.mjs | 11 + .../newchat/package-lock.json | 4212 +++++++++++++++++ frontend(experimental)/newchat/package.json | 72 + frontend(experimental)/newchat/pnpm-lock.yaml | 3404 +++++++++++++ .../newchat/postcss.config.mjs | 8 + .../newchat/public/apple-icon.png | Bin 0 -> 2626 bytes .../newchat/public/backgrounds/all-rooms.png | Bin 0 -> 1581189 bytes .../newchat/public/backgrounds/room-1.png | Bin 0 -> 1873163 bytes .../newchat/public/backgrounds/room-2.png | Bin 0 -> 2343271 bytes .../newchat/public/backgrounds/room-3.png | Bin 0 -> 2349802 bytes .../newchat/public/backgrounds/room-4.png | Bin 0 -> 1614906 bytes .../newchat/public/backgrounds/room-5.png | Bin 0 -> 1558777 bytes .../newchat/public/icon-dark-32x32.png | Bin 0 -> 585 bytes .../newchat/public/icon-light-32x32.png | Bin 0 -> 566 bytes .../newchat/public/icon.svg | 26 + .../newchat/public/placeholder-logo.png | Bin 0 -> 568 bytes .../newchat/public/placeholder-logo.svg | 1 + .../newchat/public/placeholder-user.jpg | Bin 0 -> 1635 bytes .../newchat/public/placeholder.jpg | Bin 0 -> 1064 bytes .../newchat/public/placeholder.svg | 1 + .../newchat/styles/globals.css | 92 + .../newchat/tailwind.config.js | 83 + frontend(experimental)/newchat/tsconfig.json | 27 + 104 files changed, 16741 insertions(+) create mode 100644 frontend(experimental)/newchat/README.md create mode 100644 frontend(experimental)/newchat/app/globals.css create mode 100644 frontend(experimental)/newchat/app/layout.tsx create mode 100644 frontend(experimental)/newchat/app/loading.tsx create mode 100644 frontend(experimental)/newchat/app/page.tsx create mode 100644 frontend(experimental)/newchat/components.json create mode 100644 frontend(experimental)/newchat/components/auth-screen.tsx create mode 100644 frontend(experimental)/newchat/components/create-event-dialog.tsx create mode 100644 frontend(experimental)/newchat/components/event-detail-dialog.tsx create mode 100644 frontend(experimental)/newchat/components/glass-modal.tsx create mode 100644 frontend(experimental)/newchat/components/room-dialogs.tsx create mode 100644 frontend(experimental)/newchat/components/room-settings-dialog.tsx create mode 100644 frontend(experimental)/newchat/components/sidebar.tsx create mode 100644 frontend(experimental)/newchat/components/theme-provider.tsx create mode 100644 frontend(experimental)/newchat/components/ui/accordion.tsx create mode 100644 frontend(experimental)/newchat/components/ui/alert-dialog.tsx create mode 100644 frontend(experimental)/newchat/components/ui/alert.tsx create mode 100644 frontend(experimental)/newchat/components/ui/aspect-ratio.tsx create mode 100644 frontend(experimental)/newchat/components/ui/avatar.tsx create mode 100644 frontend(experimental)/newchat/components/ui/badge.tsx create mode 100644 frontend(experimental)/newchat/components/ui/breadcrumb.tsx create mode 100644 frontend(experimental)/newchat/components/ui/button-group.tsx create mode 100644 frontend(experimental)/newchat/components/ui/button.tsx create mode 100644 frontend(experimental)/newchat/components/ui/calendar.tsx create mode 100644 frontend(experimental)/newchat/components/ui/card.tsx create mode 100644 frontend(experimental)/newchat/components/ui/carousel.tsx create mode 100644 frontend(experimental)/newchat/components/ui/chart.tsx create mode 100644 frontend(experimental)/newchat/components/ui/checkbox.tsx create mode 100644 frontend(experimental)/newchat/components/ui/collapsible.tsx create mode 100644 frontend(experimental)/newchat/components/ui/command.tsx create mode 100644 frontend(experimental)/newchat/components/ui/context-menu.tsx create mode 100644 frontend(experimental)/newchat/components/ui/dialog.tsx create mode 100644 frontend(experimental)/newchat/components/ui/drawer.tsx create mode 100644 frontend(experimental)/newchat/components/ui/dropdown-menu.tsx create mode 100644 frontend(experimental)/newchat/components/ui/empty.tsx create mode 100644 frontend(experimental)/newchat/components/ui/field.tsx create mode 100644 frontend(experimental)/newchat/components/ui/form.tsx create mode 100644 frontend(experimental)/newchat/components/ui/hover-card.tsx create mode 100644 frontend(experimental)/newchat/components/ui/input-group.tsx create mode 100644 frontend(experimental)/newchat/components/ui/input-otp.tsx create mode 100644 frontend(experimental)/newchat/components/ui/input.tsx create mode 100644 frontend(experimental)/newchat/components/ui/item.tsx create mode 100644 frontend(experimental)/newchat/components/ui/kbd.tsx create mode 100644 frontend(experimental)/newchat/components/ui/label.tsx create mode 100644 frontend(experimental)/newchat/components/ui/menubar.tsx create mode 100644 frontend(experimental)/newchat/components/ui/navigation-menu.tsx create mode 100644 frontend(experimental)/newchat/components/ui/pagination.tsx create mode 100644 frontend(experimental)/newchat/components/ui/popover.tsx create mode 100644 frontend(experimental)/newchat/components/ui/progress.tsx create mode 100644 frontend(experimental)/newchat/components/ui/radio-group.tsx create mode 100644 frontend(experimental)/newchat/components/ui/resizable.tsx create mode 100644 frontend(experimental)/newchat/components/ui/scroll-area.tsx create mode 100644 frontend(experimental)/newchat/components/ui/select.tsx create mode 100644 frontend(experimental)/newchat/components/ui/separator.tsx create mode 100644 frontend(experimental)/newchat/components/ui/sheet.tsx create mode 100644 frontend(experimental)/newchat/components/ui/sidebar.tsx create mode 100644 frontend(experimental)/newchat/components/ui/skeleton.tsx create mode 100644 frontend(experimental)/newchat/components/ui/slider.tsx create mode 100644 frontend(experimental)/newchat/components/ui/sonner.tsx create mode 100644 frontend(experimental)/newchat/components/ui/spinner.tsx create mode 100644 frontend(experimental)/newchat/components/ui/switch.tsx create mode 100644 frontend(experimental)/newchat/components/ui/table.tsx create mode 100644 frontend(experimental)/newchat/components/ui/tabs.tsx create mode 100644 frontend(experimental)/newchat/components/ui/textarea.tsx create mode 100644 frontend(experimental)/newchat/components/ui/toast.tsx create mode 100644 frontend(experimental)/newchat/components/ui/toaster.tsx create mode 100644 frontend(experimental)/newchat/components/ui/toggle-group.tsx create mode 100644 frontend(experimental)/newchat/components/ui/toggle.tsx create mode 100644 frontend(experimental)/newchat/components/ui/tooltip.tsx create mode 100644 frontend(experimental)/newchat/components/ui/use-mobile.tsx create mode 100644 frontend(experimental)/newchat/components/ui/use-toast.ts create mode 100644 frontend(experimental)/newchat/components/week-view.tsx create mode 100644 frontend(experimental)/newchat/hooks/use-mobile.ts create mode 100644 frontend(experimental)/newchat/hooks/use-toast.ts create mode 100644 frontend(experimental)/newchat/lib/api.ts create mode 100644 frontend(experimental)/newchat/lib/auth-context.tsx create mode 100644 frontend(experimental)/newchat/lib/date-utils.ts create mode 100644 frontend(experimental)/newchat/lib/hooks.ts create mode 100644 frontend(experimental)/newchat/lib/room-theme.ts create mode 100644 frontend(experimental)/newchat/lib/types.ts create mode 100644 frontend(experimental)/newchat/lib/utils.ts create mode 100644 frontend(experimental)/newchat/next.config.mjs create mode 100644 frontend(experimental)/newchat/package-lock.json create mode 100644 frontend(experimental)/newchat/package.json create mode 100644 frontend(experimental)/newchat/pnpm-lock.yaml create mode 100644 frontend(experimental)/newchat/postcss.config.mjs create mode 100644 frontend(experimental)/newchat/public/apple-icon.png create mode 100644 frontend(experimental)/newchat/public/backgrounds/all-rooms.png create mode 100644 frontend(experimental)/newchat/public/backgrounds/room-1.png create mode 100644 frontend(experimental)/newchat/public/backgrounds/room-2.png create mode 100644 frontend(experimental)/newchat/public/backgrounds/room-3.png create mode 100644 frontend(experimental)/newchat/public/backgrounds/room-4.png create mode 100644 frontend(experimental)/newchat/public/backgrounds/room-5.png create mode 100644 frontend(experimental)/newchat/public/icon-dark-32x32.png create mode 100644 frontend(experimental)/newchat/public/icon-light-32x32.png create mode 100644 frontend(experimental)/newchat/public/icon.svg create mode 100644 frontend(experimental)/newchat/public/placeholder-logo.png create mode 100644 frontend(experimental)/newchat/public/placeholder-logo.svg create mode 100644 frontend(experimental)/newchat/public/placeholder-user.jpg create mode 100644 frontend(experimental)/newchat/public/placeholder.jpg create mode 100644 frontend(experimental)/newchat/public/placeholder.svg create mode 100644 frontend(experimental)/newchat/styles/globals.css create mode 100644 frontend(experimental)/newchat/tailwind.config.js create mode 100644 frontend(experimental)/newchat/tsconfig.json diff --git a/frontend(experimental)/newchat/README.md b/frontend(experimental)/newchat/README.md new file mode 100644 index 0000000..14a4cd3 --- /dev/null +++ b/frontend(experimental)/newchat/README.md @@ -0,0 +1,33 @@ +# calendar-app + +This is a [Next.js](https://nextjs.org) project bootstrapped with [v0](https://v0.app). + +## Built with v0 + +This repository is linked to a [v0](https://v0.app) project. You can continue developing by visiting the link below -- start new chats to make changes, and v0 will push commits directly to this repo. Every merge to `main` will automatically deploy. + +[Continue working on v0 →](https://v0.app/chat/projects/prj_JicVuGp27VUV82GWT4csmGqXBtSV) + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm 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. + +## Learn More + +To learn more, 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. +- [v0 Documentation](https://v0.app/docs) - learn about v0 and how to use it. diff --git a/frontend(experimental)/newchat/app/globals.css b/frontend(experimental)/newchat/app/globals.css new file mode 100644 index 0000000..5791b0a --- /dev/null +++ b/frontend(experimental)/newchat/app/globals.css @@ -0,0 +1,62 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +: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: 221.2 83.2% 53.3%; + --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: 221.2 83.2% 53.3%; + --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: 217.2 91.2% 59.8%; + --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: 224.3 76.3% 48%; +} + +@layer utilities { + .bg-grid-slate-200\/50 { + background-image: linear-gradient(currentColor 1px, transparent 1px), + linear-gradient(to right, currentColor 1px, transparent 1px); + background-size: 20px 20px; + } +} + +.react-draggable { + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); +} + diff --git a/frontend(experimental)/newchat/app/layout.tsx b/frontend(experimental)/newchat/app/layout.tsx new file mode 100644 index 0000000..0778c98 --- /dev/null +++ b/frontend(experimental)/newchat/app/layout.tsx @@ -0,0 +1,27 @@ +import type React from "react" +import type { Metadata } from "next" +import { Inter } from "next/font/google" +import "./globals.css" +import { AuthProvider } from "@/lib/auth-context" + +const inter = Inter({ subsets: ["latin"] }) + +export const metadata: Metadata = { + title: "Calendar | Rooms & Events", + description: "A shared, room-based calendar for planning events together.", + generator: "v0.app", +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + + {children} + + + ) +} diff --git a/frontend(experimental)/newchat/app/loading.tsx b/frontend(experimental)/newchat/app/loading.tsx new file mode 100644 index 0000000..cec067b --- /dev/null +++ b/frontend(experimental)/newchat/app/loading.tsx @@ -0,0 +1,4 @@ +export default function Loading() { + return null +} + diff --git a/frontend(experimental)/newchat/app/page.tsx b/frontend(experimental)/newchat/app/page.tsx new file mode 100644 index 0000000..3114318 --- /dev/null +++ b/frontend(experimental)/newchat/app/page.tsx @@ -0,0 +1,229 @@ +"use client" + +import { useMemo, useState } from "react" +import Image from "next/image" +import { Menu, Plus, Loader2 } from "lucide-react" + +import { useAuth } from "@/lib/auth-context" +import { useRooms, useEvents } from "@/lib/hooks" +import { AuthScreen } from "@/components/auth-screen" +import { Sidebar } from "@/components/sidebar" +import { WeekView } from "@/components/week-view" +import { CreateEventDialog } from "@/components/create-event-dialog" +import { EventDetailDialog } from "@/components/event-detail-dialog" +import { CreateRoomDialog, JoinRoomDialog } from "@/components/room-dialogs" +import { RoomSettingsDialog } from "@/components/room-settings-dialog" +import { getRoomBackground } from "@/lib/room-theme" +import { addDays, startOfWeek, toDateParam, formatMonthYear } from "@/lib/date-utils" +import type { CalendarEvent, User } from "@/lib/types" + +type DialogState = + | { type: "none" } + | { type: "create-event" } + | { type: "event-detail"; event: CalendarEvent } + | { type: "create-room" } + | { type: "join-room" } + | { type: "room-settings" } + +export default function Home() { + const { user, loading, logout } = useAuth() + + if (loading) { + return ( +
+ +
+ ) + } + + if (!user) { + return + } + + return +} + +function CalendarApp({ user, onLogout }: { user: User; onLogout: () => void }) { + const [activeRoomId, setActiveRoomId] = useState(null) + const [selectedDate, setSelectedDate] = useState(() => new Date()) + const [sidebarOpen, setSidebarOpen] = useState(false) + const [dialog, setDialog] = useState({ type: "none" }) + + const { rooms, mutate: mutateRooms } = useRooms() + + const weekStart = useMemo(() => startOfWeek(selectedDate), [selectedDate]) + const weekEnd = useMemo(() => addDays(weekStart, 7), [weekStart]) + const from = useMemo(() => toDateParam(weekStart), [weekStart]) + const to = useMemo(() => toDateParam(weekEnd), [weekEnd]) + + const { + events, + isLoading: eventsLoading, + mutate: mutateEvents, + } = useEvents(activeRoomId, from, to) + + const activeRoom = rooms.find((r) => r.id === activeRoomId) + const background = getRoomBackground(activeRoomId) + + const refreshAll = () => { + mutateEvents() + mutateRooms() + } + + return ( +
+ +
+ +
+ {/* Mobile sidebar backdrop */} + {sidebarOpen && ( +
setSidebarOpen(false)} + /> + )} + +
+ { + setActiveRoomId(id) + setSidebarOpen(false) + }} + selectedDate={selectedDate} + onSelectDate={setSelectedDate} + onCreateEvent={() => setDialog({ type: "create-event" })} + onCreateRoom={() => setDialog({ type: "create-room" })} + onJoinRoom={() => setDialog({ type: "join-room" })} + user={user} + onLogout={onLogout} + /> +
+ +
+
+
+ +
+

+ {activeRoom ? activeRoom.name : "All Rooms"} +

+

{formatMonthYear(weekStart)}

+
+
+ +
+ {activeRoom && ( + + )} + +
+
+ +
+ {eventsLoading ? ( +
+ +
+ ) : ( + setDialog({ type: "event-detail", event })} + /> + )} +
+
+
+ + {dialog.type === "create-event" && ( + setDialog({ type: "none" })} + onCreated={() => { + setDialog({ type: "none" }) + refreshAll() + }} + /> + )} + + {dialog.type === "event-detail" && ( + r.id === dialog.event.room_id)} + currentUserId={user.id} + onClose={() => setDialog({ type: "none" })} + onChanged={refreshAll} + /> + )} + + {dialog.type === "create-room" && ( + setDialog({ type: "none" })} + onCreated={(roomId: number) => { + setDialog({ type: "none" }) + mutateRooms() + setActiveRoomId(roomId) + }} + /> + )} + + {dialog.type === "join-room" && ( + setDialog({ type: "none" })} + onJoined={(roomId: number) => { + setDialog({ type: "none" }) + mutateRooms() + setActiveRoomId(roomId) + }} + /> + )} + + {dialog.type === "room-settings" && activeRoom && ( + setDialog({ type: "none" })} + onChanged={mutateRooms} + onLeftOrDeleted={() => { + setDialog({ type: "none" }) + setActiveRoomId(null) + mutateRooms() + }} + /> + )} +
+ ) +} diff --git a/frontend(experimental)/newchat/components.json b/frontend(experimental)/newchat/components.json new file mode 100644 index 0000000..4ee62ee --- /dev/null +++ b/frontend(experimental)/newchat/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/frontend(experimental)/newchat/components/auth-screen.tsx b/frontend(experimental)/newchat/components/auth-screen.tsx new file mode 100644 index 0000000..3bcc92c --- /dev/null +++ b/frontend(experimental)/newchat/components/auth-screen.tsx @@ -0,0 +1,161 @@ +"use client" + +import type React from "react" +import { useState } from "react" +import Image from "next/image" +import { Calendar, Loader2 } from "lucide-react" +import { useAuth } from "@/lib/auth-context" +import { ApiError } from "@/lib/api" +import { ALL_ROOMS_BACKGROUND } from "@/lib/room-theme" + +type Mode = "login" | "register" + +export function AuthScreen() { + const { login, register } = useAuth() + const [mode, setMode] = useState("login") + const [username, setUsername] = useState("") + const [email, setEmail] = useState("") + const [password, setPassword] = useState("") + const [error, setError] = useState(null) + const [notice, setNotice] = useState(null) + const [submitting, setSubmitting] = useState(false) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setError(null) + setNotice(null) + setSubmitting(true) + try { + if (mode === "login") { + await login(username.trim(), password) + } else { + await register(username.trim(), password, email.trim()) + } + } catch (err) { + const message = + err instanceof ApiError ? err.message : "Something went wrong. Please try again." + setError(message) + } finally { + setSubmitting(false) + } + } + + const switchMode = (next: Mode) => { + setMode(next) + setError(null) + setNotice(null) + } + + return ( +
+ Aurora over a mountain lake +
+ +
+
+
+ +
+

+ {mode === "login" ? "Welcome back" : "Create your account"} +

+

+ {mode === "login" + ? "Sign in to your shared calendar" + : "Join rooms and plan events together"} +

+
+ +
+
+ + setUsername(e.target.value)} + className="rounded-xl border border-white/20 bg-white/10 px-4 py-2.5 text-white placeholder:text-white/50 focus:outline-none focus:ring-2 focus:ring-white/40" + placeholder="your_username" + /> +
+ + {mode === "register" && ( +
+ + setEmail(e.target.value)} + className="rounded-xl border border-white/20 bg-white/10 px-4 py-2.5 text-white placeholder:text-white/50 focus:outline-none focus:ring-2 focus:ring-white/40" + placeholder="you@example.com" + /> +
+ )} + +
+ + setPassword(e.target.value)} + className="rounded-xl border border-white/20 bg-white/10 px-4 py-2.5 text-white placeholder:text-white/50 focus:outline-none focus:ring-2 focus:ring-white/40" + placeholder={mode === "register" ? "At least 8 characters" : "••••••••"} + /> +
+ + {error && ( +
+ {error} +
+ )} + {notice && ( +
+ {notice} +
+ )} + + +
+ +

+ {mode === "login" ? "Don't have an account? " : "Already have an account? "} + +

+
+
+ ) +} diff --git a/frontend(experimental)/newchat/components/create-event-dialog.tsx b/frontend(experimental)/newchat/components/create-event-dialog.tsx new file mode 100644 index 0000000..385f513 --- /dev/null +++ b/frontend(experimental)/newchat/components/create-event-dialog.tsx @@ -0,0 +1,265 @@ +"use client" + +import type React from "react" +import { useState } from "react" +import { Loader2 } from "lucide-react" +import { GlassModal, fieldClass, labelClass } from "./glass-modal" +import * as api from "@/lib/api" +import { ApiError } from "@/lib/api" +import type { EventType, EventVisibility, Room } from "@/lib/types" +import { toBackendDateTime, toInputDateTime } from "@/lib/date-utils" + +interface CreateEventDialogProps { + rooms: Room[] + defaultRoomId: number | null + defaultDate: Date + onClose: () => void + onCreated: () => void +} + +const TYPE_OPTIONS: { value: EventType; label: string }[] = [ + { value: "proposal", label: "Proposal (members vote to confirm)" }, + { value: "busy", label: "Busy (blocked time)" }, + { value: "private", label: "Private" }, +] + +const VISIBILITY_OPTIONS: { value: EventVisibility; label: string }[] = [ + { value: "room", label: "Room — visible to all members" }, + { value: "busy_only", label: "Busy only — hide details" }, + { value: "private", label: "Private — only me" }, +] + +const optionClass = "bg-slate-800 text-white" + +export function CreateEventDialog({ + rooms, + defaultRoomId, + defaultDate, + onClose, + onCreated, +}: CreateEventDialogProps) { + const initialRoom = defaultRoomId ?? rooms[0]?.id ?? 0 + + const defaultStart = new Date(defaultDate) + defaultStart.setHours(9, 0, 0, 0) + const defaultEnd = new Date(defaultDate) + defaultEnd.setHours(10, 0, 0, 0) + + const [roomId, setRoomId] = useState(initialRoom) + const [title, setTitle] = useState("") + const [description, setDescription] = useState("") + const [type, setType] = useState("proposal") + const [visibility, setVisibility] = useState("room") + const [start, setStart] = useState(toInputDateTime(defaultStart)) + const [end, setEnd] = useState(toInputDateTime(defaultEnd)) + const [minPeople, setMinPeople] = useState(1) + const [error, setError] = useState(null) + const [submitting, setSubmitting] = useState(false) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setError(null) + + if (!roomId) { + setError("Please select a room.") + return + } + const startDate = new Date(start) + const endDate = new Date(end) + if (endDate <= startDate) { + setError("End time must be after the start time.") + return + } + + setSubmitting(true) + try { + await api.createEvent({ + room_id: roomId, + type, + title: title.trim(), + description: description.trim(), + start_time: toBackendDateTime(startDate), + end_time: toBackendDateTime(endDate), + visibility, + status: type === "proposal" ? "proposed" : "confirmed", + min_people: type === "proposal" ? minPeople : 0, + }) + onCreated() + onClose() + } catch (err) { + setError(err instanceof ApiError ? err.message : "Could not create the event.") + } finally { + setSubmitting(false) + } + } + + if (rooms.length === 0) { + return ( + +

+ You need to be in a room before creating an event. Create or join a room first. +

+
+ ) + } + + return ( + +
+
+ + +
+ +
+ + setTitle(e.target.value)} + className={fieldClass} + placeholder="e.g. Team dinner" + /> +
+ +
+ +