feat : improve UX/UI
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
CopyIcon,
|
||||
GripVerticalIcon,
|
||||
HeartPulseIcon,
|
||||
MousePointerClickIcon,
|
||||
PlusIcon,
|
||||
RefreshCcwIcon,
|
||||
SearchIcon,
|
||||
@@ -130,6 +131,7 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Toggle } from "@/components/ui/toggle";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -918,17 +920,17 @@ function SortableConditionTab({
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
style={{ transform: CSS.Transform.toString(transform), transition }}
|
||||
className={cn("flex shrink-0 items-center rounded-md border", isDragging && "z-10 opacity-60")}
|
||||
className={cn("flex shrink-0 items-center rounded-md", isDragging && "z-10 opacity-60")}
|
||||
>
|
||||
<ToggleGroupItem
|
||||
<TabsTrigger
|
||||
value={item.clientId}
|
||||
className="border-0 aria-pressed:bg-primary aria-pressed:text-primary-foreground"
|
||||
onClick={onSelect}
|
||||
onDoubleClick={onRename}
|
||||
>
|
||||
<MousePointerClickIcon aria-hidden="true" data-icon="inline-start" />
|
||||
{label}
|
||||
{index === 0 ? <Badge variant={active ? "secondary" : "outline"}>Default</Badge> : null}
|
||||
</ToggleGroupItem>
|
||||
</TabsTrigger>
|
||||
<SimpleTooltip text={`ลากเพื่อจัดลำดับ ${label}`}>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -980,26 +982,23 @@ function SortableConditionSwitcher({
|
||||
}}
|
||||
>
|
||||
<SortableContext items={items.map((item) => item.clientId)} strategy={rectSortingStrategy}>
|
||||
<ToggleGroup
|
||||
aria-label={ariaLabel}
|
||||
variant="outline"
|
||||
value={activeId ? [activeId] : []}
|
||||
onValueChange={(values) => {
|
||||
const index = items.findIndex((item) => item.clientId === values[0]);
|
||||
if (index !== -1) onActiveIndexChange(index);
|
||||
}}
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
<SortableConditionTab
|
||||
key={item.clientId}
|
||||
item={item}
|
||||
index={index}
|
||||
active={item.clientId === activeId}
|
||||
onSelect={() => onActiveIndexChange(index)}
|
||||
onRename={() => onRename(index)}
|
||||
/>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
<Tabs value={activeId} onValueChange={(value) => {
|
||||
const index = items.findIndex((item) => item.clientId === value);
|
||||
if (index !== -1) onActiveIndexChange(index);
|
||||
}}>
|
||||
<TabsList aria-label={ariaLabel} className="shrink-0 gap-1">
|
||||
{items.map((item, index) => (
|
||||
<SortableConditionTab
|
||||
key={item.clientId}
|
||||
item={item}
|
||||
index={index}
|
||||
active={item.clientId === activeId}
|
||||
onSelect={() => onActiveIndexChange(index)}
|
||||
onRename={() => onRename(index)}
|
||||
/>
|
||||
))}
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</ScrollableNav>
|
||||
@@ -3812,18 +3811,16 @@ function TeamEditor({
|
||||
</Button>
|
||||
{activeImportGuide.groups.length > 1 ? (
|
||||
<ScrollableNav ariaLabel="เลือกเงื่อนไขทีม" className="w-full min-w-0">
|
||||
<ToggleGroup
|
||||
aria-label="เลือกเงื่อนไขทีม"
|
||||
variant="outline"
|
||||
value={[guideImportCondition]}
|
||||
onValueChange={(values) => { if (values[0]) setGuideImportCondition(values[0]); }}
|
||||
>
|
||||
{activeImportGuide.groups.map((group, index) => (
|
||||
<ToggleGroupItem key={group.id} value={String(index)} className="aria-pressed:bg-primary aria-pressed:text-primary-foreground">
|
||||
{group.name || `Condition ${index + 1}`}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
<Tabs value={guideImportCondition} onValueChange={setGuideImportCondition}>
|
||||
<TabsList aria-label="เลือกเงื่อนไขทีม">
|
||||
{activeImportGuide.groups.map((group, index) => (
|
||||
<TabsTrigger key={group.id} value={String(index)}>
|
||||
<MousePointerClickIcon aria-hidden="true" data-icon="inline-start" />
|
||||
{group.name || `Condition ${index + 1}`}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</ScrollableNav>
|
||||
) : null}
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
|
||||
@@ -49,14 +49,6 @@ import {
|
||||
InputGroupAddon,
|
||||
InputGroupInput,
|
||||
} from "@/components/ui/input-group";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { useAdminPresence } from "@/hooks/use-presence";
|
||||
import type { CatalogSyncJob } from "@/lib/catalog/sync";
|
||||
@@ -86,7 +78,6 @@ export function StructuredDashboard({
|
||||
const router = useRouter();
|
||||
const [pending, startTransition] = useTransition();
|
||||
const [query, setQuery] = useState("");
|
||||
const [sort, setSort] = useState("default");
|
||||
const [syncJob, setSyncJob] = useState<CatalogSyncJob | null>(null);
|
||||
const presence = useAdminPresence(null);
|
||||
const catalogReady = Boolean(catalogVersion);
|
||||
@@ -95,10 +86,7 @@ export function StructuredDashboard({
|
||||
const search = searchItems(guides, query,
|
||||
(guide) => `${guide.name} ${guide.characterKey} ${guide.overview}`,
|
||||
(guide) => [guide.name]);
|
||||
const filteredGuides = sort === "default" ? search.items : [...search.items].sort((left, right) => {
|
||||
const order = left.name.localeCompare(right.name, "th", { numeric: true });
|
||||
return sort === "name-asc" ? order : -order;
|
||||
});
|
||||
const filteredGuides = search.items;
|
||||
|
||||
useEffect(() => {
|
||||
if (!syncJobId || !syncJobStatus || ["completed", "unchanged", "cancelled", "error"].includes(syncJobStatus)) return;
|
||||
@@ -198,31 +186,17 @@ export function StructuredDashboard({
|
||||
</Card>
|
||||
) : null}
|
||||
{guides.length ? (
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
|
||||
<InputGroup className="h-11 max-w-2xl flex-1">
|
||||
<InputGroupAddon>
|
||||
<SearchIcon />
|
||||
</InputGroupAddon>
|
||||
<InputGroupInput
|
||||
value={query}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
placeholder="ค้นหาตัวละคร…"
|
||||
aria-label="ค้นหาตัวละคร"
|
||||
/>
|
||||
</InputGroup>
|
||||
<Select value={sort} onValueChange={(value) => value && setSort(value)}>
|
||||
<SelectTrigger aria-label="เรียง Guide" className="h-11 w-full sm:w-44">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="default">เรียงตามเดิม</SelectItem>
|
||||
<SelectItem value="name-asc">ชื่อ ก-ฮ</SelectItem>
|
||||
<SelectItem value="name-desc">ชื่อ ฮ-ก</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<InputGroup className="h-11 max-w-2xl">
|
||||
<InputGroupAddon>
|
||||
<SearchIcon />
|
||||
</InputGroupAddon>
|
||||
<InputGroupInput
|
||||
value={query}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
placeholder="ค้นหาตัวละคร…"
|
||||
aria-label="ค้นหาตัวละคร"
|
||||
/>
|
||||
</InputGroup>
|
||||
) : null}
|
||||
|
||||
{filteredGuides.length ? (
|
||||
|
||||
Reference in New Issue
Block a user