Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
155 lines
4.8 KiB
TypeScript
155 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { api } from "@/lib/api/client";
|
|
import { GlassCard } from "@/components/ui/glass-card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { PageHeader } from "@/components/dashboard/page-header";
|
|
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
|
|
|
type Issue = {
|
|
id: string;
|
|
subject: string;
|
|
body: string;
|
|
status: string;
|
|
master_reply: string | null;
|
|
created_at: string;
|
|
leagues: { name: string } | null;
|
|
};
|
|
|
|
export function IssuesPanel({
|
|
leagues,
|
|
asMaster,
|
|
}: {
|
|
leagues: { id: string; name: string }[];
|
|
asMaster: boolean;
|
|
}) {
|
|
const [issues, setIssues] = useState<Issue[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [showConfirm, setShowConfirm] = useState(false);
|
|
const [pending, setPending] = useState<FormData | null>(null);
|
|
|
|
useEffect(() => {
|
|
api.issues.list(asMaster).then((d) => setIssues(d as Issue[]));
|
|
}, [asMaster]);
|
|
|
|
async function submit(fd: FormData) {
|
|
setLoading(true);
|
|
try {
|
|
await api.issues.create({
|
|
leagueId: fd.get("league_id") as string,
|
|
subject: fd.get("subject") as string,
|
|
body: fd.get("body") as string,
|
|
});
|
|
const updated = (await api.issues.list(asMaster)) as Issue[];
|
|
setIssues(updated);
|
|
} finally {
|
|
setLoading(false);
|
|
setShowConfirm(false);
|
|
setPending(null);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<PageHeader
|
|
title="Issues"
|
|
description={
|
|
asMaster
|
|
? "Messages from team managers"
|
|
: "Send problems to your league master"
|
|
}
|
|
/>
|
|
|
|
{!asMaster && (
|
|
<GlassCard title="New issue">
|
|
<form
|
|
className="space-y-3"
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
if (leagues.length === 0) return;
|
|
setPending(new FormData(e.currentTarget));
|
|
setShowConfirm(true);
|
|
}}
|
|
>
|
|
<div>
|
|
<Label>League</Label>
|
|
<select
|
|
name="league_id"
|
|
required
|
|
className="mt-1 flex h-10 w-full rounded-lg border border-white/15 bg-white/5 px-3 text-sm"
|
|
>
|
|
{leagues.map((l) => (
|
|
<option key={l.id} value={l.id}>
|
|
{l.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<Label>Subject</Label>
|
|
<Input name="subject" required className="mt-1" />
|
|
</div>
|
|
<div>
|
|
<Label>Details</Label>
|
|
<textarea
|
|
name="body"
|
|
required
|
|
rows={4}
|
|
className="mt-1 flex w-full rounded-lg border border-white/15 bg-white/5 px-3 py-2 text-sm"
|
|
/>
|
|
</div>
|
|
<Button type="submit" disabled={loading || leagues.length === 0}>
|
|
Submit to league master
|
|
</Button>
|
|
</form>
|
|
</GlassCard>
|
|
)}
|
|
|
|
<GlassCard title={asMaster ? "All issues" : "Your issues"}>
|
|
<ul className="space-y-3">
|
|
{issues.map((issue) => (
|
|
<li
|
|
key={issue.id}
|
|
className="rounded-lg border border-white/10 p-3 text-sm"
|
|
>
|
|
<div className="flex justify-between gap-2">
|
|
<p className="font-medium">{issue.subject}</p>
|
|
<span className="text-xs capitalize text-[var(--color-muted)]">
|
|
{issue.status}
|
|
</span>
|
|
</div>
|
|
<p className="mt-1 text-xs text-cyan-400/70">
|
|
{issue.leagues?.name}
|
|
</p>
|
|
<p className="mt-2 text-[var(--color-muted)]">{issue.body}</p>
|
|
{issue.master_reply && (
|
|
<p className="mt-2 rounded-lg bg-white/5 p-2 text-foreground">
|
|
<span className="text-xs text-[var(--color-muted)]">Reply: </span>
|
|
{issue.master_reply}
|
|
</p>
|
|
)}
|
|
</li>
|
|
))}
|
|
{issues.length === 0 && (
|
|
<p className="text-[var(--color-muted)]">No issues</p>
|
|
)}
|
|
</ul>
|
|
</GlassCard>
|
|
|
|
<ConfirmDialog
|
|
open={showConfirm}
|
|
onOpenChange={setShowConfirm}
|
|
title="Send issue to league master?"
|
|
description="Your message will be visible to league masters for this league."
|
|
confirmLabel="Send"
|
|
variant="primary"
|
|
loading={loading}
|
|
onConfirm={() => pending && submit(pending)}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|