mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
set the contractor upload and classification workflow
This commit is contained in:
parent
3a4d102ea4
commit
a5b3cfe85b
1 changed files with 25 additions and 127 deletions
|
|
@ -1,7 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
|
|
@ -256,138 +255,37 @@ function PasGuidancePanel() {
|
|||
);
|
||||
}
|
||||
|
||||
// ── Searchable DocType combobox ────────────────────────────────────────────
|
||||
// ── DocType select ────────────────────────────────────────────────────────
|
||||
|
||||
function DocTypeSelect({ value, onChange, showHint = false }: { value: string; onChange: (v: string) => void; showHint?: boolean }) {
|
||||
const selected = FILE_TYPE_OPTIONS.find((o) => o.value === value);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [query, setQuery] = useState("");
|
||||
const [dropdownStyle, setDropdownStyle] = useState<React.CSSProperties>({});
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
function openDropdown() {
|
||||
if (!triggerRef.current) return;
|
||||
const rect = triggerRef.current.getBoundingClientRect();
|
||||
const spaceBelow = window.innerHeight - rect.bottom;
|
||||
const dropdownHeight = 280;
|
||||
const showAbove = spaceBelow < dropdownHeight && rect.top > dropdownHeight;
|
||||
setDropdownStyle({
|
||||
position: "fixed",
|
||||
left: rect.left,
|
||||
width: rect.width,
|
||||
zIndex: 9999,
|
||||
...(showAbove
|
||||
? { bottom: window.innerHeight - rect.top + 4 }
|
||||
: { top: rect.bottom + 4 }),
|
||||
});
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
function close() {
|
||||
setOpen(false);
|
||||
setQuery("");
|
||||
}
|
||||
|
||||
function select(val: string) {
|
||||
onChange(val);
|
||||
close();
|
||||
}
|
||||
|
||||
// Focus search input when opening
|
||||
useEffect(() => {
|
||||
if (open) setTimeout(() => inputRef.current?.focus(), 0);
|
||||
}, [open]);
|
||||
|
||||
const filtered = query.trim()
|
||||
? FILE_TYPE_OPTIONS.filter((o) =>
|
||||
o.label.toLowerCase().includes(query.toLowerCase()) ||
|
||||
o.group.toLowerCase().includes(query.toLowerCase())
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
{/* Trigger */}
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={() => open ? close() : openDropdown()}
|
||||
className={`flex h-8 w-full items-center justify-between rounded-md border px-3 py-1 text-xs transition-colors
|
||||
${open ? "border-brandblue ring-1 ring-brandblue/30" : "border-input hover:border-gray-300"}
|
||||
bg-background`}
|
||||
>
|
||||
<span className={selected ? "text-gray-800" : "text-gray-400"}>
|
||||
{selected ? selected.label : "Select type…"}
|
||||
</span>
|
||||
<ChevronDown className={`h-3.5 w-3.5 text-gray-400 transition-transform ${open ? "rotate-180" : ""}`} />
|
||||
</button>
|
||||
|
||||
{open && typeof document !== "undefined" && createPortal(
|
||||
<>
|
||||
{/* Transparent overlay — position:fixed escapes overflow:hidden, catches all outside clicks */}
|
||||
<div className="fixed inset-0" style={{ zIndex: 9998 }} onClick={close} />
|
||||
|
||||
{/* Dropdown — above the overlay */}
|
||||
<div style={dropdownStyle} className="rounded-md border border-gray-200 bg-white shadow-xl">
|
||||
<div className="border-b border-gray-100 p-2">
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="Search…"
|
||||
className="w-full rounded border border-gray-200 px-2 py-1 text-xs outline-none focus:border-brandblue"
|
||||
/>
|
||||
</div>
|
||||
<div className="max-h-56 overflow-y-auto py-1">
|
||||
{filtered ? (
|
||||
filtered.length === 0 ? (
|
||||
<p className="px-3 py-2 text-xs text-gray-400">No results</p>
|
||||
) : (
|
||||
filtered.map((o) => (
|
||||
<button
|
||||
key={o.value}
|
||||
type="button"
|
||||
onClick={() => select(o.value)}
|
||||
className={`flex w-full flex-col px-3 py-1.5 text-left text-xs hover:bg-brandlightblue/30 transition-colors
|
||||
${value === o.value ? "bg-brandlightblue/20 font-medium text-brandblue" : "text-gray-700"}`}
|
||||
>
|
||||
<span>{o.label}</span>
|
||||
<span className="text-[10px] text-gray-400">{o.group}</span>
|
||||
</button>
|
||||
))
|
||||
)
|
||||
) : (
|
||||
FILE_TYPE_GROUPS.map((group) => {
|
||||
const items = FILE_TYPE_OPTIONS.filter((o) => o.group === group);
|
||||
if (!items.length) return null;
|
||||
return (
|
||||
<div key={group}>
|
||||
<p className="px-3 pt-2 pb-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-400">
|
||||
{group}
|
||||
</p>
|
||||
{items.map((o) => (
|
||||
<button
|
||||
key={o.value}
|
||||
type="button"
|
||||
onClick={() => select(o.value)}
|
||||
className={`flex w-full px-3 py-1.5 text-left text-xs hover:bg-brandlightblue/30 transition-colors
|
||||
${value === o.value ? "bg-brandlightblue/20 font-medium text-brandblue" : "text-gray-700"}`}
|
||||
>
|
||||
{o.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>,
|
||||
document.body
|
||||
)}
|
||||
|
||||
<Select value={value || "__unset__"} onValueChange={(v) => onChange(v === "__unset__" ? "" : v)}>
|
||||
<SelectTrigger className="h-8 text-xs w-full">
|
||||
<SelectValue placeholder="Select type…" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__unset__" className="text-xs text-gray-400">Select type…</SelectItem>
|
||||
{FILE_TYPE_GROUPS.map((group) => {
|
||||
const items = FILE_TYPE_OPTIONS.filter((o) => o.group === group);
|
||||
if (!items.length) return null;
|
||||
return (
|
||||
<SelectGroup key={group}>
|
||||
<SelectLabel className="text-[10px] font-semibold uppercase tracking-wide text-gray-400 px-2 py-1">
|
||||
{group}
|
||||
</SelectLabel>
|
||||
{items.map((o) => (
|
||||
<SelectItem key={o.value} value={o.value} className="text-xs">
|
||||
{o.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
);
|
||||
})}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{showHint && selected?.hint && (
|
||||
<p className="text-[10px] text-blue-600 leading-snug px-0.5">{selected.hint}</p>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue