mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
refactor(mod-tools): redesign all related windows with shared visual language
Applies the visual language introduced in ModToolsUserView yesterday to every other ModTools window. The design tokens used consistently: emerald — present in current room / positive state sky — online / informational / current selection zinc — neutral / disabled amber — warn-level (CFH, alerts, cautions) rose — danger (bans, releases, abusive) Files redesigned: ModToolsRoomView Identity header with FaDoorOpen, room name + ID, owner-present pill (emerald/zinc), manual refresh button. Stat strip: user count (sky) + clickable owner name (zinc) opening user info. Quick actions (Visit / Chatlog) in a 2-col grid. Moderate panel collapsed into an amber-tinted card with the 3 toggles + textarea + two CTAs (Send Caution=danger, Send Alert=warning). CTAs disabled until a message is typed AND the room info has loaded. ModToolsUserModActionView Numbered 3-step form (CFH topic → sanction → optional message). Live preview row showing the chosen topic + sanction as tone-coded pills (amber/sky/rose/orange/fuchsia/zinc by action type). Primary CTA = Default Sanction, success CTA = Apply Sanction, both disabled until the required selections are made. ModToolsUserSendMessageView Recipient header with FaEnvelope and the username, autofocused textarea, char counter, single full-width Send button gated on non-empty message. ModToolsUserRoomVisitsView Header strip with entry count badge, three-column grid (time / room name / visit button), monospace timestamps, hover row highlight, empty state with FaDoorOpen icon. ModToolsUserChatlogView / ModToolsChatlogView / CfhChatlogView Loading state with spinner instead of returning null. Cards grow to min-w-[460px] max-w-[520px] max-h-[500px] for usable chatlog area. ChatlogView Replace Bootstrap-ish striped table with a CSS grid (60px / 120px / 1fr). Room-info separator rendered as a sky card with Visit/Tools pill buttons. Per-row hover + even-row tint; highlighted rows (hasHighlighting) get an amber wash. Username is a button opening user info via existing link event. Empty state with FaCommentDots. ModToolsTicketsView Tabs get icons (FaListUl / FaUserCheck / FaCheckSquare) and inline count badges (amber/sky/zinc) so the moderator sees the queue size at a glance. ticket bucket filtering memoized off the tickets array. ModToolsOpenIssuesTabView / MyIssuesTabView / PickedIssuesTabView Same CSS grid table style. Category renders as a tone-coded pill (Open=amber, Mine=sky, All picked=zinc). Action buttons get icons (FaHandPointer Pick, FaTools Handle, FaSignOutAlt Release). Empty state with FaInbox. ModToolsIssueInfoView Card header with category + topic pills. Details rendered as a dl grid instead of a striped table. Caller / Reported names as inline link buttons with external-link icon. Chatlog toggle is full-width secondary. Resolution buttons in a 3-col grid with intent colours (success=Resolved, dark=Useless, danger=Abusive) + a separate Release-to-queue button on its own row so it isn't confused with the resolutions. No behaviour changes — all composers, message events, parent state hookups, and sanction validation paths are unchanged. This is purely a presentation pass. typecheck + vitest 214/214 + lint:hooks all clean.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { GetRoomVisitsMessageComposer, RoomVisitsData, RoomVisitsEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { FaClock, FaDoorOpen, FaSignInAlt } from 'react-icons/fa';
|
||||
import { SendMessageComposer, TryVisitRoom } from '../../../../api';
|
||||
import { Column, DraggableWindowPosition, Grid, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||
import { DraggableWindowPosition, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common';
|
||||
import { useMessageEvent } from '../../../../hooks';
|
||||
|
||||
interface ModToolsUserRoomVisitsViewProps
|
||||
@@ -31,29 +32,51 @@ export const ModToolsUserRoomVisitsView: FC<ModToolsUserRoomVisitsViewProps> = p
|
||||
|
||||
if(!userId) return null;
|
||||
|
||||
const rows = roomVisitData?.rooms ?? [];
|
||||
const isEmpty = rows.length === 0;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-visits" theme="primary-slim" windowPosition={ DraggableWindowPosition.TOP_LEFT }>
|
||||
<NitroCardView className="nitro-mod-tools-user-visits min-w-[400px] max-w-[460px] max-h-[460px]" theme="primary-slim" windowPosition={ DraggableWindowPosition.TOP_LEFT }>
|
||||
<NitroCardHeaderView headerText={ 'User Visits' } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black" gap={ 1 }>
|
||||
<Column fullHeight gap={ 0 } overflow="hidden">
|
||||
<Column gap={ 2 }>
|
||||
<Grid className="text-black font-bold border-bottom pb-1" gap={ 1 }>
|
||||
<div className="col-span-2">Time</div>
|
||||
<div className="col-span-7">Room name</div>
|
||||
<div className="col-span-3">Visit</div>
|
||||
</Grid>
|
||||
</Column>
|
||||
<InfiniteScroll rowRender={ row =>
|
||||
{
|
||||
return (
|
||||
<Grid alignItems="center" className="text-black py-1 border-bottom" fullHeight={ false } gap={ 1 }>
|
||||
<Text className="col-span-2">{ row.enterHour.toString().padStart(2, '0') }: { row.enterMinute.toString().padStart(2, '0') }</Text>
|
||||
<Text className="col-span-7">{ row.roomName }</Text>
|
||||
<Text bold pointer underline className="col-span-3" variant="primary" onClick={ event => TryVisitRoom(row.roomId) }>Visit Room</Text>
|
||||
</Grid>
|
||||
);
|
||||
} } rows={ roomVisitData?.rooms ?? [] } />
|
||||
</Column>
|
||||
{/* Header strip */}
|
||||
<div className="flex items-center gap-2 bg-gradient-to-r from-sky-50 to-transparent rounded p-2 border border-sky-100">
|
||||
<FaDoorOpen className="text-sky-600 shrink-0" size={ 14 } />
|
||||
<div className="text-sm font-semibold leading-tight grow">Recent visited rooms</div>
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium border bg-white border-zinc-200">
|
||||
{ rows.length } { rows.length === 1 ? 'entry' : 'entries' }
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Table head */}
|
||||
<div className="grid grid-cols-[60px_1fr_80px] gap-2 text-[.7rem] uppercase tracking-wide opacity-60 font-semibold border-b border-zinc-200 pb-1 px-1">
|
||||
<div className="flex items-center gap-1"><FaClock size={ 10 } /> Time</div>
|
||||
<div>Room name</div>
|
||||
<div className="text-right">Action</div>
|
||||
</div>
|
||||
|
||||
{/* Rows */}
|
||||
{ isEmpty
|
||||
? <div className="flex flex-col items-center justify-center gap-1 py-6 opacity-50 text-sm">
|
||||
<FaDoorOpen size={ 22 } />
|
||||
<span>No recent visits</span>
|
||||
</div>
|
||||
: <div className="flex flex-col grow min-h-0 overflow-hidden">
|
||||
<InfiniteScroll rowRender={ row => (
|
||||
<div className="grid grid-cols-[60px_1fr_80px] gap-2 items-center px-1 py-1.5 text-sm border-b border-zinc-100 even:bg-black/[0.02] hover:bg-sky-50 transition-colors">
|
||||
<span className="font-mono text-[.75rem] opacity-70 tabular-nums">
|
||||
{ row.enterHour.toString().padStart(2, '0') }:{ row.enterMinute.toString().padStart(2, '0') }
|
||||
</span>
|
||||
<span className="truncate font-medium">{ row.roomName }</span>
|
||||
<button
|
||||
className="inline-flex items-center justify-end gap-1 text-sky-700 hover:text-sky-900 hover:underline text-xs"
|
||||
onClick={ () => TryVisitRoom(row.roomId) }
|
||||
title="Visit room">
|
||||
<FaSignInAlt size={ 10 } /> Visit
|
||||
</button>
|
||||
</div>
|
||||
) } rows={ rows } />
|
||||
</div> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user