nxtgauge-admin-solid/src/routes/admin/support.tsx
Ashwin Kumar Sivakumar ef8f4410c7
Some checks failed
build-and-release / build (push) Failing after 1m16s
fix: dead Add Note buttons and no-op row click in admin review pages
- verification/[id].tsx: "Add Note" in the Reviewer Notes panel had no
  onClick — wired to the existing POST /api/admin/verifications/:id/notes
  endpoint.
- approval/index.tsx: Decision Notes textarea was uncontrolled and its
  "Add Note" button was a no-op; there's no standalone notes endpoint
  for approval_requests, so the textarea is now bound to state and its
  value is sent as the rejection reason when the reviewer rejects
  (the only channel the backend currently supports).
- support.tsx: removed a no-op onClick={() => {}} on the case row —
  the row isn't actually clickable, only the adjacent "View" link is
  (though that link's target route doesn't exist yet — flagged
  separately, out of scope here).
2026-07-12 17:59:03 +05:30

745 lines
31 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createResource, createSignal, createMemo, Show, For } from "solid-js";
import { A } from "@solidjs/router";
const API = "";
function getToken(): string {
return typeof sessionStorage !== "undefined"
? sessionStorage.getItem("nxtgauge_admin_access_token") || ""
: "";
}
function authHeaders(contentType = false): Record<string, string> {
const token = getToken();
return {
Accept: "application/json",
...(contentType ? { "Content-Type": "application/json" } : {}),
...(token ? { Authorization: `Bearer ${token}` } : {}),
};
}
type SupportCase = {
id: string;
title: string;
description: string;
type:
| "platform_issue"
| "customer_query"
| "professional_query"
| "billing_issue"
| "lead_dispute";
priority: "low" | "medium" | "high" | "critical";
status: "new" | "in_progress" | "waiting_for_user" | "resolved" | "closed";
requesterName?: string;
requesterEmail?: string;
updatedAt: string;
createdAt: string;
};
type AssigneeOption = {
id: string;
name: string;
email?: string;
};
const STATUS_OPTIONS: SupportCase["status"][] = [
"new",
"in_progress",
"waiting_for_user",
"resolved",
"closed",
];
const TYPE_OPTIONS: SupportCase["type"][] = [
"platform_issue",
"customer_query",
"professional_query",
"billing_issue",
"lead_dispute",
];
const PRIORITY_OPTIONS: SupportCase["priority"][] = ["low", "medium", "high", "critical"];
function formatValue(input: string): string {
return input.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
}
function typeBadgeStyle(type: string): string {
const map: Record<string, string> = {
platform_issue: "background:#dbeafe;color:#1d4ed8",
customer_query: "background:#dcfce7;color:#15803d",
billing_issue: "background:#ffedd5;color:#c2410c",
lead_dispute: "background:#fee2e2;color:#b91c1c",
professional_query: "background:#f3e8ff;color:#7e22ce",
};
return map[type] || "background:#f1f5f9;color:#475569";
}
function priorityBadgeStyle(priority: string): string {
const map: Record<string, string> = {
low: "background:#f1f5f9;color:#475569",
medium: "background:#dbeafe;color:#1d4ed8",
high: "background:#ffedd5;color:#c2410c",
critical: "background:#fee2e2;color:#b91c1c",
};
return map[priority] || "background:#f1f5f9;color:#475569";
}
function statusBadgeStyle(status: string): string {
const map: Record<string, string> = {
new: "background:#dbeafe;color:#1d4ed8",
in_progress: "background:#ffedd5;color:#c2410c",
waiting_for_user: "background:#fef9c3;color:#a16207",
resolved: "background:#dcfce7;color:#15803d",
closed: "background:#f1f5f9;color:#475569",
};
return map[status] || "background:#f1f5f9;color:#475569";
}
const BADGE_STYLE =
"display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600";
async function loadAllCases(): Promise<SupportCase[]> {
try {
const res = await fetch(`${API}/api/admin/support-cases`, {
headers: authHeaders(),
credentials: "include",
});
if (!res.ok) throw new Error("Failed");
const data = await res.json();
return Array.isArray(data.cases) ? data.cases : Array.isArray(data) ? data : [];
} catch {
return [];
}
}
async function loadAssignees(): Promise<AssigneeOption[]> {
try {
const params = new URLSearchParams({ page: "1", per_page: "200", sort: "joined_desc" });
const res = await fetch(`${API}/api/admin/employees?${params.toString()}`, {
headers: authHeaders(),
credentials: "include",
});
if (!res.ok) throw new Error("Failed");
const data = await res.json();
const raw = Array.isArray(data?.items) ? data.items : Array.isArray(data) ? data : [];
return raw
.map((item: any) => ({
id: String(item.id ?? ""),
name: String(item.name ?? item.full_name ?? item.email ?? "Unknown"),
email: item.email ? String(item.email) : undefined,
}))
.filter((item: AssigneeOption) => Boolean(item.id));
} catch {
return [];
}
}
export default function SupportPage() {
const [activeTab, setActiveTab] = createSignal<"queue" | "create">("queue");
const [statusFilter, setStatusFilter] = createSignal<"all" | SupportCase["status"]>("all");
const [search, setSearch] = createSignal("");
const [sortBy, setSortBy] = createSignal<"newest" | "oldest" | "priority">("newest");
const [sortMenuOpen, setSortMenuOpen] = createSignal(false);
const [filterMenuOpen, setFilterMenuOpen] = createSignal(false);
const [refetchKey, setRefetchKey] = createSignal(0);
const [cases] = createResource(refetchKey, loadAllCases);
const [assignees] = createResource(loadAssignees);
const refetch = () => setRefetchKey((k) => k + 1);
const filteredCases = createMemo(() => {
let all = cases() ?? [];
const q = search().toLowerCase().trim();
if (q) {
all = all.filter(
(c) =>
String(c.title || "")
.toLowerCase()
.includes(q) ||
String(c.description || "")
.toLowerCase()
.includes(q) ||
String(c.requesterName || "")
.toLowerCase()
.includes(q) ||
String(c.requesterEmail || "")
.toLowerCase()
.includes(q) ||
String(c.type || "")
.toLowerCase()
.includes(q)
);
}
const sf = statusFilter();
if (sf !== "all") all = all.filter((c) => c.status === sf);
const priorityRank: Record<SupportCase["priority"], number> = {
critical: 4,
high: 3,
medium: 2,
low: 1,
};
const sorted = [...all];
sorted.sort((a, b) => {
if (sortBy() === "oldest")
return new Date(a.createdAt || 0).getTime() - new Date(b.createdAt || 0).getTime();
if (sortBy() === "priority")
return (priorityRank[b.priority] || 0) - (priorityRank[a.priority] || 0);
return new Date(b.createdAt || 0).getTime() - new Date(a.createdAt || 0).getTime();
});
return sorted;
});
const stats = createMemo(() => {
const all = cases() ?? [];
return {
newCount: all.filter((c) => c.status === "new").length,
inProgressCount: all.filter((c) => c.status === "in_progress").length,
waitingCount: all.filter((c) => c.status === "waiting_for_user").length,
total: all.length,
};
});
// Create Case form state
const [fTitle, setFTitle] = createSignal("");
const [fDesc, setFDesc] = createSignal("");
const [fType, setFType] = createSignal<SupportCase["type"]>("customer_query");
const [fPriority, setFPriority] = createSignal<SupportCase["priority"]>("medium");
const [fRequesterName, setFRequesterName] = createSignal("");
const [fRequesterEmail, setFRequesterEmail] = createSignal("");
const [fAssignedTo, setFAssignedTo] = createSignal("");
const [createLoading, setCreateLoading] = createSignal(false);
const [createSuccess, setCreateSuccess] = createSignal("");
const [createError, setCreateError] = createSignal("");
const resetForm = () => {
setFTitle("");
setFDesc("");
setFType("customer_query");
setFPriority("medium");
setFRequesterName("");
setFRequesterEmail("");
setFAssignedTo("");
};
const handleCreate = async (e: Event) => {
e.preventDefault();
setCreateLoading(true);
setCreateSuccess("");
setCreateError("");
try {
const res = await fetch(`${API}/api/admin/support-cases`, {
method: "POST",
headers: authHeaders(true),
credentials: "include",
body: JSON.stringify({
title: fTitle(),
description: fDesc(),
category: fType(),
priority: fPriority(),
requesterName: fRequesterName(),
requesterEmail: fRequesterEmail(),
}),
});
if (!res.ok) {
const d = await res.json().catch(() => ({}));
throw new Error((d as any).message || "Failed to create case");
}
const created = await res.json().catch(() => ({}));
const createdId = String((created as any)?.id || "");
if (createdId && fAssignedTo()) {
await fetch(`${API}/api/admin/support-cases/${createdId}`, {
method: "PATCH",
headers: authHeaders(true),
credentials: "include",
body: JSON.stringify({ assigned_to: fAssignedTo() }),
});
}
setCreateSuccess("Case created!");
resetForm();
refetch();
setActiveTab("queue");
} catch (err: any) {
setCreateError(err.message || "Failed to create case");
} finally {
setCreateLoading(false);
}
};
const statCards = [
{ label: "New", getValue: () => stats().newCount },
{ label: "In Progress", getValue: () => stats().inProgressCount },
{ label: "Waiting", getValue: () => stats().waitingCount },
{ label: "Total", getValue: () => stats().total },
];
const exportCsv = () => {
const headers = ["Issue", "Type", "Priority", "Status", "Requester", "Updated"];
const rows = filteredCases().map((item) => [
item.title,
item.type,
item.priority,
item.status,
item.requesterEmail || item.requesterName || "—",
item.updatedAt ? new Date(item.updatedAt).toLocaleString() : "—",
]);
const csv = [headers, ...rows]
.map((line) => line.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(","))
.join("\n");
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "support-management.csv";
link.click();
URL.revokeObjectURL(url);
};
return (
<div class="w-full space-y-6 pb-8">
<div style="margin-bottom:1.5rem">
<h1 class="text-[28px] font-bold leading-tight text-[#111827]">Support Management</h1>
<p class="mt-1 text-[14px] text-[#6B7280]">Handle platform issues and customer queries</p>
</div>
{/* Tabs */}
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<button
type="button"
class={
activeTab() === "queue"
? "py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium"
: "py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors"
}
onClick={() => setActiveTab("queue")}
>
Support Queue
</button>
<button
type="button"
class={
activeTab() === "create"
? "py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium"
: "py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors"
}
onClick={() => setActiveTab("create")}
>
Create Case
</button>
</div>
<div>
{/* Stats bar */}
<div style="display:flex;gap:12px;margin-bottom:16px">
<For each={statCards}>
{(card) => (
<div style="background:#f8f9fa;border:1px solid #e5e7eb;border-radius:8px;padding:12px 20px;text-align:center">
<div style="font-size:24px;font-weight:700">{card.getValue()}</div>
<div style="font-size:12px;color:#6b7280">{card.label}</div>
</div>
)}
</For>
</div>
{/* Support Queue Tab */}
<Show when={activeTab() === "queue"}>
<div style="position:relative;margin-top:1.5rem;margin-left:-24px;margin-right:-24px;border-radius:0;border-left:none;border-right:none;overflow:visible;border-top:1px solid #E5E7EB;border-bottom:1px solid #E5E7EB;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.06)">
<div style="display:flex;align-items:center;gap:8px;padding:14px 20px;border-bottom:1px solid #F3F4F6;position:relative;z-index:20;margin-bottom:0">
<input
type="text"
placeholder="Search issues, requester, type..."
value={search()}
onInput={(e) => setSearch(e.currentTarget.value)}
style="height:34px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:13px;color:#111827;outline:none"
/>
<div style="position:relative;">
<button
type="button"
onClick={() => {
setSortMenuOpen((v) => !v);
setFilterMenuOpen(false);
}}
style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151;cursor:pointer"
>
<svg
width="13"
height="13"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M7 4v13" />
<path d="m3 13 4 4 4-4" />
<path d="M17 20V7" />
<path d="m21 11-4-4-4 4" />
</svg>
Sort
</button>
<Show when={sortMenuOpen()}>
<div style="position:absolute;right:0;top:38px;z-index:40;min-width:180px;border:1px solid #E5E7EB;border-radius:12px;background:#fff;box-shadow:0 12px 24px rgba(2,6,23,0.12);padding:8px">
<For
each={
[
{ key: "newest", label: "Newest First" },
{ key: "oldest", label: "Oldest First" },
{ key: "priority", label: "Priority High-Low" },
] as { key: "newest" | "oldest" | "priority"; label: string }[]
}
>
{(item) => (
<button
type="button"
onClick={() => {
setSortBy(item.key);
setSortMenuOpen(false);
}}
style={`display:block;width:100%;border-radius:8px;padding:8px 12px;text-align:left;font-size:13px;background:none;border:none;cursor:pointer;color:${sortBy() === item.key ? "#FF5E13" : "#374151"};background:${sortBy() === item.key ? "#FFF1EB" : "transparent"}`}
>
{item.label}
</button>
)}
</For>
</div>
</Show>
</div>
<div style="position:relative;">
<button
type="button"
onClick={() => {
setFilterMenuOpen((v) => !v);
setSortMenuOpen(false);
}}
style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151;cursor:pointer"
>
<svg
width="13"
height="13"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M3 5h18M6 12h12M10 19h4" />
</svg>
Filters
</button>
<Show when={filterMenuOpen()}>
<div style="position:absolute;right:0;top:38px;z-index:40;min-width:220px;border:1px solid #E5E7EB;border-radius:12px;background:#fff;box-shadow:0 12px 24px rgba(2,6,23,0.12);padding:8px">
<button
type="button"
onClick={() => {
setStatusFilter("all");
setFilterMenuOpen(false);
}}
style={`display:block;width:100%;border-radius:8px;padding:8px 12px;text-align:left;font-size:13px;background:none;border:none;cursor:pointer;color:${statusFilter() === "all" ? "#FF5E13" : "#374151"};background:${statusFilter() === "all" ? "#FFF1EB" : "transparent"}`}
>
All statuses
</button>
<For each={STATUS_OPTIONS}>
{(s) => (
<button
type="button"
onClick={() => {
setStatusFilter(s);
setFilterMenuOpen(false);
}}
style={`display:block;width:100%;border-radius:8px;padding:8px 12px;text-align:left;font-size:13px;background:none;border:none;cursor:pointer;color:${statusFilter() === s ? "#FF5E13" : "#374151"};background:${statusFilter() === s ? "#FFF1EB" : "transparent"}`}
>
{formatValue(s)}
</button>
)}
</For>
</div>
</Show>
</div>
<button
type="button"
onClick={exportCsv}
style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;background:#0D0D2A;padding:0 12px;font-size:12px;font-weight:600;color:white;border:none;cursor:pointer"
>
<svg
width="13"
height="13"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" />
<line x1="12" y1="15" x2="12" y2="3" />
</svg>
Export
</button>
</div>
<div class="table-card">
<div class="overflow-x-auto">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Issue</th>
<th>Type</th>
<th>Priority</th>
<th>Status</th>
<th>Requester</th>
<th>Updated At</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
<Show when={cases.loading}>
<tr>
<td colspan="7" style="text-align:center;padding:32px;color:#64748b">
Loading...
</td>
</tr>
</Show>
<Show when={!cases.loading && cases.error}>
<tr>
<td colspan="7" style="text-align:center;padding:32px;color:#b91c1c">
Failed to load cases.
</td>
</tr>
</Show>
<Show when={!cases.loading && !cases.error && filteredCases().length === 0}>
<tr>
<td colspan="7" style="text-align:center;padding:32px;color:#94a3b8">
No support cases found.
</td>
</tr>
</Show>
<Show when={!cases.loading && !cases.error && filteredCases().length > 0}>
<For each={filteredCases()}>
{(item) => (
<tr class="hover:bg-slate-50">
<td>
<div class="font-semibold text-slate-900">{item.title}</div>
<div style="font-size:12px;color:#64748b;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
{item.description}
</div>
</td>
<td>
<span style={`${BADGE_STYLE};${typeBadgeStyle(item.type)}`}>
{formatValue(item.type)}
</span>
</td>
<td>
<span style={`${BADGE_STYLE};${priorityBadgeStyle(item.priority)}`}>
{formatValue(item.priority)}
</span>
</td>
<td>
<span style={`${BADGE_STYLE};${statusBadgeStyle(item.status)}`}>
{formatValue(item.status)}
</span>
</td>
<td>
<div style="font-size:13px">{item.requesterName || "—"}</div>
<div style="font-size:11px;color:#64748b">
{item.requesterEmail || ""}
</div>
</td>
<td class="text-slate-500" style="font-size:12px">
{item.updatedAt ? new Date(item.updatedAt).toLocaleString() : "—"}
</td>
<td>
<div class="flex items-center justify-end gap-1">
<A
class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors"
href={`/admin/support/${item.id}`}
>
View
</A>
</div>
</td>
</tr>
)}
</For>
</Show>
</tbody>
</table>
</div>
<Show when={!cases.loading && !cases.error && filteredCases().length > 0}>
<div style="display:flex;align-items:center;justify-content:space-between;border-top:1px solid #F3F4F6;padding:12px 20px">
<p style="font-size:13px;color:#6B7280">
Showing{" "}
<strong style="font-weight:600;color:#111827">
1{filteredCases().length}
</strong>{" "}
of{" "}
<strong style="font-weight:600;color:#111827">{filteredCases().length}</strong>{" "}
cases
</p>
<div style="display:flex;align-items:center;gap:4px">
<button
type="button"
style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;border:1px solid #E5E7EB;background:white;color:#6B7280;cursor:pointer;font-size:15px"
>
</button>
<button
type="button"
style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;background:#FF5E13;color:white;font-size:13px;font-weight:600;border:none;cursor:pointer"
>
1
</button>
<button
type="button"
style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;border:1px solid #E5E7EB;background:white;color:#374151;font-size:13px;font-weight:500;cursor:pointer"
>
2
</button>
<button
type="button"
style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;border:1px solid #E5E7EB;background:white;color:#374151;font-size:13px;font-weight:500;cursor:pointer"
>
3
</button>
<button
type="button"
style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;border:1px solid #E5E7EB;background:white;color:#6B7280;cursor:pointer;font-size:15px"
>
</button>
</div>
</div>
</Show>
</div>
</div>
</Show>
{/* Create Case Tab */}
<Show when={activeTab() === "create"}>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm p-6">
<h2 style="margin:0 0 6px;font-size:16px;font-weight:700;color:#1e293b">
Create Support Case
</h2>
<p style="margin:0 0 20px;font-size:13px;color:#64748b">
Create an internal support record for platform issues, customer concerns, or
compensation-related reviews.
</p>
<Show when={createSuccess()}>
<div style="background:#dcfce7;border:1px solid #86efac;border-radius:6px;padding:10px 14px;margin-bottom:14px;font-size:14px;color:#15803d;font-weight:600">
{createSuccess()}
</div>
</Show>
<Show when={createError()}>
<div
class="mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700"
style="margin-bottom:14px"
>
{createError()}
</div>
</Show>
<form onSubmit={handleCreate} style="display:flex;flex-direction:column;gap:16px">
<div class="field">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">
Title
</label>
<input
type="text"
required
value={fTitle()}
onInput={(e) => setFTitle(e.currentTarget.value)}
style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;box-sizing:border-box"
/>
</div>
<div class="field">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">
Description
</label>
<textarea
required
rows="4"
value={fDesc()}
onInput={(e) => setFDesc(e.currentTarget.value)}
style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;resize:vertical;box-sizing:border-box"
/>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="field">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">
Type
</label>
<select
value={fType()}
onChange={(e) => setFType(e.currentTarget.value as SupportCase["type"])}
style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;box-sizing:border-box"
>
<For each={TYPE_OPTIONS}>
{(t) => <option value={t}>{formatValue(t)}</option>}
</For>
</select>
</div>
<div class="field">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">
Priority
</label>
<select
value={fPriority()}
onChange={(e) => setFPriority(e.currentTarget.value as SupportCase["priority"])}
style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;box-sizing:border-box"
>
<For each={PRIORITY_OPTIONS}>
{(p) => <option value={p}>{formatValue(p)}</option>}
</For>
</select>
</div>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="field">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">
Requester Name
</label>
<input
type="text"
value={fRequesterName()}
onInput={(e) => setFRequesterName(e.currentTarget.value)}
style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;box-sizing:border-box"
/>
</div>
<div class="field">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">
Requester Email
</label>
<input
type="email"
value={fRequesterEmail()}
onInput={(e) => setFRequesterEmail(e.currentTarget.value)}
style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;box-sizing:border-box"
/>
</div>
</div>
<div class="field">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">
Assign To (optional)
</label>
<select
value={fAssignedTo()}
onChange={(e) => setFAssignedTo(e.currentTarget.value)}
style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;box-sizing:border-box"
>
<option value="">Unassigned</option>
<For each={assignees()}>
{(assignee) => (
<option value={assignee.id}>
{assignee.name}
{assignee.email ? ` (${assignee.email})` : ""}
</option>
)}
</For>
</select>
</div>
<div>
<button class="btn-primary" type="submit" disabled={createLoading()}>
{createLoading() ? "Creating..." : "Create Support Case"}
</button>
</div>
</form>
</section>
</Show>
</div>
</div>
);
}