Files
calendar/frontend(experimental)/newchat/components/create-event-dialog.tsx
T
ben bf601846db new frontend
ik ben zo cool
2026-08-19 09:33:48 +02:00

266 lines
7.9 KiB
TypeScript

"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<number>(initialRoom)
const [title, setTitle] = useState("")
const [description, setDescription] = useState("")
const [type, setType] = useState<EventType>("proposal")
const [visibility, setVisibility] = useState<EventVisibility>("room")
const [start, setStart] = useState(toInputDateTime(defaultStart))
const [end, setEnd] = useState(toInputDateTime(defaultEnd))
const [minPeople, setMinPeople] = useState(1)
const [error, setError] = useState<string | null>(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 (
<GlassModal title="Create event" onClose={onClose}>
<p className="text-sm text-white/80">
You need to be in a room before creating an event. Create or join a room first.
</p>
</GlassModal>
)
}
return (
<GlassModal title="Create event" onClose={onClose}>
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<div>
<label className={labelClass} htmlFor="event-room">
Room
</label>
<select
id="event-room"
value={roomId}
onChange={(e) => setRoomId(Number(e.target.value))}
className={fieldClass}
>
{rooms.map((r) => (
<option key={r.id} value={r.id} className={optionClass}>
{r.name}
</option>
))}
</select>
</div>
<div>
<label className={labelClass} htmlFor="event-title">
Title
</label>
<input
id="event-title"
required
value={title}
onChange={(e) => setTitle(e.target.value)}
className={fieldClass}
placeholder="e.g. Team dinner"
/>
</div>
<div>
<label className={labelClass} htmlFor="event-desc">
Description
</label>
<textarea
id="event-desc"
value={description}
onChange={(e) => setDescription(e.target.value)}
rows={2}
className={fieldClass}
placeholder="Optional details"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className={labelClass} htmlFor="event-start">
Start
</label>
<input
id="event-start"
type="datetime-local"
required
value={start}
onChange={(e) => setStart(e.target.value)}
className={`${fieldClass} [color-scheme:dark]`}
/>
</div>
<div>
<label className={labelClass} htmlFor="event-end">
End
</label>
<input
id="event-end"
type="datetime-local"
required
value={end}
onChange={(e) => setEnd(e.target.value)}
className={`${fieldClass} [color-scheme:dark]`}
/>
</div>
</div>
<div>
<label className={labelClass} htmlFor="event-type">
Type
</label>
<select
id="event-type"
value={type}
onChange={(e) => setType(e.target.value as EventType)}
className={fieldClass}
>
{TYPE_OPTIONS.map((o) => (
<option key={o.value} value={o.value} className={optionClass}>
{o.label}
</option>
))}
</select>
</div>
{type === "proposal" && (
<div>
<label className={labelClass} htmlFor="event-min">
Minimum people to confirm
</label>
<input
id="event-min"
type="number"
min={0}
value={minPeople}
onChange={(e) => setMinPeople(Number(e.target.value))}
className={fieldClass}
/>
<p className="mt-1 text-xs text-white/60">
Auto-confirms once this many members respond &quot;going&quot;.
</p>
</div>
)}
<div>
<label className={labelClass} htmlFor="event-visibility">
Visibility
</label>
<select
id="event-visibility"
value={visibility}
onChange={(e) => setVisibility(e.target.value as EventVisibility)}
className={fieldClass}
>
{VISIBILITY_OPTIONS.map((o) => (
<option key={o.value} value={o.value} className={optionClass}>
{o.label}
</option>
))}
</select>
</div>
{error && (
<div className="rounded-xl border border-red-400/40 bg-red-500/20 px-4 py-2.5 text-sm">
{error}
</div>
)}
<div className="mt-1 flex justify-end gap-3">
<button
type="button"
onClick={onClose}
className="rounded-xl border border-white/20 bg-white/10 px-4 py-2.5 text-sm font-medium transition-colors hover:bg-white/20"
>
Cancel
</button>
<button
type="submit"
disabled={submitting}
className="flex items-center gap-2 rounded-xl bg-blue-500 px-5 py-2.5 text-sm font-medium transition-colors hover:bg-blue-600 disabled:opacity-70"
>
{submitting && <Loader2 className="h-4 w-4 animate-spin" />}
Create
</button>
</div>
</form>
</GlassModal>
)
}