/* * Copyright (C) 2026 Fluxer Contributors * * This file is part of Fluxer. * * Fluxer is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fluxer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Fluxer. If not, see . */ /** @jsxRuntime automatic */ /** @jsxImportSource hono/jsx */ import {hasPermission} from '@fluxer/admin/src/AccessControlList'; import {Layout} from '@fluxer/admin/src/components/Layout'; import {FormFieldGroup} from '@fluxer/admin/src/components/ui/Form/FormFieldGroup'; import {Input} from '@fluxer/admin/src/components/ui/Input'; import {PageLayout} from '@fluxer/admin/src/components/ui/Layout/PageLayout'; import {VStack} from '@fluxer/admin/src/components/ui/Layout/VStack'; import {Table} from '@fluxer/admin/src/components/ui/Table'; import {TableBody} from '@fluxer/admin/src/components/ui/TableBody'; import {TableCell} from '@fluxer/admin/src/components/ui/TableCell'; import {TableContainer} from '@fluxer/admin/src/components/ui/TableContainer'; import {TableHeader} from '@fluxer/admin/src/components/ui/TableHeader'; import {TableHeaderCell} from '@fluxer/admin/src/components/ui/TableHeaderCell'; import {TableRow} from '@fluxer/admin/src/components/ui/TableRow'; import {Heading, Text} from '@fluxer/admin/src/components/ui/Typography'; import type {Session} from '@fluxer/admin/src/types/App'; import type {AdminConfig as Config} from '@fluxer/admin/src/types/Config'; import {AdminACLs} from '@fluxer/constants/src/AdminACLs'; import type {Flash} from '@fluxer/hono/src/Flash'; import type {UserAdminResponse} from '@fluxer/schema/src/domains/admin/AdminUserSchemas'; import {Button} from '@fluxer/ui/src/components/Button'; import {Card} from '@fluxer/ui/src/components/Card'; import {CsrfInput} from '@fluxer/ui/src/components/CsrfInput'; import {SliderInput} from '@fluxer/ui/src/components/SliderInput'; import type {FC} from 'hono/jsx'; const MAX_EXPAND_COUNT = 1000; const DEFAULT_EXPAND_COUNT = 10; export interface VisionarySlot { slot_index: number; user_id: string | null; } export interface VisionarySlotsPageProps { config: Config; session: Session; currentAdmin: UserAdminResponse | undefined; flash: Flash | undefined; adminAcls: Array; assetVersion: string; csrfToken: string; slots: Array; totalCount: number; reservedCount: number; } export const VisionarySlotsPage: FC = ({ config, session, currentAdmin, flash, adminAcls, assetVersion, csrfToken, slots, totalCount, reservedCount, }) => { const hasViewPermission = hasPermission(adminAcls, AdminACLs.VISIONARY_SLOT_VIEW); const hasExpandPermission = hasPermission(adminAcls, AdminACLs.VISIONARY_SLOT_EXPAND); const hasShrinkPermission = hasPermission(adminAcls, AdminACLs.VISIONARY_SLOT_SHRINK); const hasReservePermission = hasPermission(adminAcls, AdminACLs.VISIONARY_SLOT_RESERVE); const hasSwapPermission = hasPermission(adminAcls, AdminACLs.VISIONARY_SLOT_SWAP); const reservedSlots = slots.filter((s) => s.user_id !== null); const minAllowedTotalCount = reservedSlots.length > 0 ? Math.max(...reservedSlots.map((s) => s.slot_index)) : 0; return ( {hasViewPermission ? ( Visionary Slots Management Total slots: {totalCount} | Reserved: {reservedCount} | Available:{' '} {totalCount - reservedCount} {hasExpandPermission && ( Expand Slots
)} {hasShrinkPermission && ( Shrink Slots {totalCount === 0 ? ( No slots to shrink. ) : minAllowedTotalCount >= totalCount ? ( Cannot shrink further. The highest reserved slot is index {minAllowedTotalCount}. ) : (
0 ? '(cannot shrink below highest reserved slot)' : '(can shrink to 0)' }`} /> )}
)} {hasReservePermission && ( Reserve Slot {totalCount === 0 ? ( No slots exist yet. Expand slots first before reserving. ) : (
)}
)} {hasSwapPermission && ( Swap Slots {totalCount < 2 ? ( Need at least two slots to swap. ) : (
)}
)} All Slots {slots.length === 0 ? ( No slots exist yet. ) : ( Slot Index User ID Status {slots.map((slot) => ( {slot.slot_index} {slot.user_id ?? null} {slot.user_id ? 'Reserved' : 'Available'} ))}
)}
) : ( Visionary Slots You do not have permission to view visionary slots. )}
); };