mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
rover based colors!
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
||||
<title>Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-D13hsMAR.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Dva9v7lV.css">
|
||||
<script type="module" crossorigin src="/assets/index-tQBHQF7J.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-C-j0HVkU.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -127,7 +127,7 @@ export default function ButtonBoxPanel() {
|
||||
}, [effectiveAlertVolume, socket]);
|
||||
|
||||
return (
|
||||
<CardFrame title="Button Box" accent="#eab308" bodyClassName="space-y-0.5 text-base">
|
||||
<CardFrame title="Button Box" bodyClassName="space-y-0.5 text-base">
|
||||
<div className="grid grid-cols-4 gap-0.5">
|
||||
{buttons.map((button) => {
|
||||
const id = Number(button.id);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
|
||||
// Utilities
|
||||
function cx(...values) {
|
||||
return values.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
// Color helpers
|
||||
function hexToRgb(hex) {
|
||||
const raw = String(hex || '').trim();
|
||||
const normalized = raw.startsWith('#') ? raw.slice(1) : raw;
|
||||
@@ -24,21 +28,29 @@ function rgba(rgb, alpha) {
|
||||
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
|
||||
}
|
||||
|
||||
// Component
|
||||
export default function CardFrame({
|
||||
title = '',
|
||||
meta = null,
|
||||
actions = null,
|
||||
accent = null,
|
||||
hideHeader = false,
|
||||
className = '',
|
||||
headerClassName = '',
|
||||
bodyClassName = '',
|
||||
fillHeight = false,
|
||||
clipOverflow = true,
|
||||
children,
|
||||
}) {
|
||||
const showHeader = !hideHeader && (title || meta != null || actions);
|
||||
const accentRgb = hexToRgb(accent);
|
||||
const cardStyle = accentRgb ? { borderColor: rgba(accentRgb, 0.65) } : undefined;
|
||||
const ownRoverColor = useSessionSelector((state) => {
|
||||
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
||||
if (!roverId) return null;
|
||||
const roster = Array.isArray(state.session?.roster) ? state.session.roster : [];
|
||||
const rover = roster.find((entry) => String(entry?.id) === roverId);
|
||||
return rover?.color || null;
|
||||
});
|
||||
const accentRgb = hexToRgb(ownRoverColor);
|
||||
const cardStyle = accentRgb ? { borderColor: rgba(accentRgb, 0.35) } : undefined;
|
||||
const headerStyle = accentRgb
|
||||
? {
|
||||
backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${rgba(accentRgb, 0.18)} 100%)`,
|
||||
@@ -48,28 +60,30 @@ export default function CardFrame({
|
||||
return (
|
||||
<section
|
||||
className={cx(
|
||||
'panel-section overflow-hidden border border-neutral-600/55 bg-neutral-900/95 shadow-[0_1px_0_rgba(255,255,255,0.04)_inset,0_10px_24px_rgba(0,0,0,0.3)]',
|
||||
'panel-section border border-neutral-500/60 bg-neutral-900/95 shadow-[0_1px_0_rgba(255,255,255,0.05)_inset,0_10px_24px_rgba(0,0,0,0.28)]',
|
||||
clipOverflow ? 'overflow-hidden' : 'overflow-visible',
|
||||
fillHeight && 'flex h-full min-h-0 flex-col',
|
||||
className,
|
||||
)}
|
||||
style={cardStyle}
|
||||
>
|
||||
{showHeader ? (
|
||||
// Header row
|
||||
<header
|
||||
className={cx(
|
||||
'flex items-center justify-between gap-0.5 border-b border-neutral-600/45 bg-gradient-to-r from-neutral-900 via-neutral-800 to-neutral-700 px-0.5 py-0.5',
|
||||
'flex items-center justify-between gap-0.5 border-b border-neutral-500/50 bg-gradient-to-r from-neutral-800 via-neutral-700 to-neutral-600 px-0.5 py-0.5',
|
||||
headerClassName,
|
||||
)}
|
||||
style={headerStyle}
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-0.5">
|
||||
{title ? <p className="m-0 text-[0.78rem] font-semibold leading-none text-neutral-100">{title}</p> : null}
|
||||
{meta != null ? <span className="text-[0.68rem] font-medium leading-none text-neutral-300">{meta}</span> : null}
|
||||
{title ? <p className="m-0 text-[0.78rem] font-semibold leading-none text-neutral-50">{title}</p> : null}
|
||||
{meta != null ? <span className="text-[0.68rem] font-medium leading-none text-neutral-200">{meta}</span> : null}
|
||||
</div>
|
||||
{actions ? <div className="flex flex-wrap items-center justify-end gap-0.5">{actions}</div> : null}
|
||||
</header>
|
||||
) : null}
|
||||
<div className={cx('', fillHeight && 'flex flex-1 min-h-0 flex-col', bodyClassName)}>{children}</div>
|
||||
<div className={cx(fillHeight && 'flex flex-1 min-h-0 flex-col', bodyClassName)}>{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function ChatPanel({
|
||||
return (
|
||||
<CardFrame
|
||||
title={title}
|
||||
accent="#22c55e"
|
||||
|
||||
hideHeader={!title}
|
||||
fillHeight={fillHeight}
|
||||
bodyClassName="space-y-0.5 text-base"
|
||||
|
||||
@@ -8,11 +8,40 @@ const MOBILE_DISMISS_MS = 10000;
|
||||
const MAX_FONT_PX = 28;
|
||||
const MIN_FONT_PX = 14;
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const raw = String(hex || '').trim();
|
||||
const normalized = raw.startsWith('#') ? raw.slice(1) : raw;
|
||||
if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(normalized)) return null;
|
||||
const expanded =
|
||||
normalized.length === 3
|
||||
? normalized
|
||||
.split('')
|
||||
.map((ch) => ch + ch)
|
||||
.join('')
|
||||
: normalized;
|
||||
return {
|
||||
r: Number.parseInt(expanded.slice(0, 2), 16),
|
||||
g: Number.parseInt(expanded.slice(2, 4), 16),
|
||||
b: Number.parseInt(expanded.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
function rgba(rgb, alpha) {
|
||||
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
|
||||
}
|
||||
|
||||
export default function GlobalObjectiveBanner({ layout = 'desktop', className = '', dismissable = true }) {
|
||||
const goalText = useSessionSelector((state) => {
|
||||
const text = state.session?.globalObjective?.text;
|
||||
return text ? String(text).trim() : '';
|
||||
});
|
||||
const ownRoverColor = useSessionSelector((state) => {
|
||||
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
||||
if (!roverId) return null;
|
||||
const roster = Array.isArray(state.session?.roster) ? state.session.roster : [];
|
||||
const rover = roster.find((entry) => String(entry?.id) === roverId);
|
||||
return rover?.color || null;
|
||||
});
|
||||
const isMobile = layout === 'mobile-portrait' || layout === 'mobile-landscape' || layout === 'mobile';
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [fontSize, setFontSize] = useState(MAX_FONT_PX);
|
||||
@@ -72,7 +101,7 @@ export default function GlobalObjectiveBanner({ layout = 'desktop', className =
|
||||
const containerClass = useMemo(
|
||||
() =>
|
||||
[
|
||||
'panel-section flex w-full items-center justify-center',
|
||||
'panel-section flex w-full items-center justify-center border border-neutral-500/60 shadow-[0_1px_0_rgba(255,255,255,0.05)_inset,0_10px_24px_rgba(0,0,0,0.28)]',
|
||||
isMobile ? 'rounded-none' : 'rounded',
|
||||
'px-1 py-1 text-center font-semibold tracking-tight',
|
||||
className,
|
||||
@@ -81,6 +110,13 @@ export default function GlobalObjectiveBanner({ layout = 'desktop', className =
|
||||
.join(' '),
|
||||
[className, isMobile],
|
||||
);
|
||||
const accentRgb = hexToRgb(ownRoverColor);
|
||||
const frameStyle = accentRgb
|
||||
? {
|
||||
borderColor: rgba(accentRgb, 0.35),
|
||||
backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${rgba(accentRgb, 0.18)} 100%)`,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
if (!goalText || !visible) return null;
|
||||
|
||||
@@ -94,7 +130,7 @@ export default function GlobalObjectiveBanner({ layout = 'desktop', className =
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={containerClass}
|
||||
style={{ fontSize: `${fontSize}px`, lineHeight: 1.1 }}
|
||||
style={{ ...(frameStyle || {}), fontSize: `${fontSize}px`, lineHeight: 1.1 }}
|
||||
{...dismissProps}
|
||||
>
|
||||
<span className="flex w-full items-stretch gap-0.5 whitespace-nowrap rounded-md">
|
||||
|
||||
@@ -256,7 +256,7 @@ export default function HomeAssistantControls() {
|
||||
|
||||
if (!ha?.enabled) {
|
||||
return (
|
||||
<CardFrame title="Room Controls" accent="#14b8a6" bodyClassName="space-y-0.5 text-sm text-slate-400">
|
||||
<CardFrame title="Room Controls" bodyClassName="space-y-0.5 text-sm text-slate-400">
|
||||
<p className="text-slate-500">Not configured on the server.</p>
|
||||
</CardFrame>
|
||||
);
|
||||
@@ -264,7 +264,7 @@ export default function HomeAssistantControls() {
|
||||
|
||||
if (entities.length === 0) {
|
||||
return (
|
||||
<CardFrame title="Room Controls" accent="#14b8a6" bodyClassName="space-y-0.5 text-sm text-slate-400">
|
||||
<CardFrame title="Room Controls" bodyClassName="space-y-0.5 text-sm text-slate-400">
|
||||
<p className="text-slate-500">No lights or switches configured.</p>
|
||||
</CardFrame>
|
||||
);
|
||||
@@ -290,7 +290,7 @@ export default function HomeAssistantControls() {
|
||||
);
|
||||
|
||||
return (
|
||||
<CardFrame title="Room Controls" accent="#14b8a6" actions={actions} bodyClassName="space-y-0.5 text-base">
|
||||
<CardFrame title="Room Controls" actions={actions} bodyClassName="space-y-0.5 text-base">
|
||||
{controlsLocked ? (
|
||||
<p className="rounded border border-amber-600/60 bg-amber-900/40 px-1 py-0.5 text-xs text-amber-100">
|
||||
{lockState === 'off'
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function LogPanel() {
|
||||
const logs = useSessionSelector((state) => state.logs);
|
||||
const rendered = useMemo(() => logs.slice().reverse(), [logs]);
|
||||
return (
|
||||
<CardFrame title="Server logs" accent="#f97316" bodyClassName="space-y-0.5 text-base">
|
||||
<CardFrame title="Server logs" bodyClassName="space-y-0.5 text-base">
|
||||
<div className="surface h-64 overflow-y-auto font-mono text-xs">
|
||||
{logs.length === 0 ? (
|
||||
<p>No logs yet.</p>
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function OverseerPreferencePanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<CardFrame title="Overseer vote" accent="#10b981" bodyClassName="space-y-0.5 text-center text-xs text-slate-200">
|
||||
<CardFrame title="Overseer vote" bodyClassName="space-y-0.5 text-center text-xs text-slate-200">
|
||||
<label
|
||||
className={`flex items-center justify-center gap-1.5 rounded px-1 py-0.5 ${
|
||||
enabled ? 'bg-emerald-600/80 text-emerald-50' : 'bg-slate-600/70 text-slate-100'
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function RawUserPilePanel({
|
||||
return (
|
||||
<CardFrame
|
||||
title={!hideHeader ? 'Users' : ''}
|
||||
accent="#94a3b8"
|
||||
|
||||
hideHeader={hideHeader}
|
||||
fillHeight={fillHeight}
|
||||
className={className}
|
||||
|
||||
@@ -150,7 +150,7 @@ export default function ReplaySourcesPanel({ panelId = 'replay-sources', fillHei
|
||||
const listWrapClass = fillHeight ? 'flex-1 min-h-0 overflow-y-auto' : '';
|
||||
|
||||
return (
|
||||
<CardFrame title="Replay Sources" accent="#06b6d4" fillHeight={fillHeight} bodyClassName="space-y-0.5 text-sm">
|
||||
<CardFrame title="Replay Sources" fillHeight={fillHeight} bodyClassName="space-y-0.5 text-sm">
|
||||
<div className={`grid gap-0.5 md:grid-cols-2 ${listWrapClass}`}>
|
||||
<GroupList title="Rovers" items={grouped.rovers} selected={selected} onToggle={toggleKey} />
|
||||
<GroupList title="Room Cams" items={grouped.rooms} selected={selected} onToggle={toggleKey} />
|
||||
|
||||
@@ -36,7 +36,7 @@ function TopDownMapPanel() {
|
||||
const sensors = frame?.sensors || {};
|
||||
|
||||
return (
|
||||
<CardFrame hideHeader accent="#0067f7">
|
||||
<CardFrame hideHeader>
|
||||
<div className="aspect-square w-full">
|
||||
<TopDownMap sensors={sensors} />
|
||||
</div>
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function RoomCameraPanel({
|
||||
return (
|
||||
<CardFrame
|
||||
title="Room cameras"
|
||||
accent="#38bdf8"
|
||||
|
||||
actions={actions}
|
||||
hideHeader={hideHeader}
|
||||
bodyClassName="space-y-0.5 text-base"
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function RoverQueuesPanel({ title = 'Rovers' }) {
|
||||
users.find((u) => u.socketId === socketId) || { socketId, nickname: null, role: null };
|
||||
|
||||
return (
|
||||
<CardFrame title={title} accent="#f59e0b" bodyClassName="space-y-0.5 text-sm">
|
||||
<CardFrame title={title} bodyClassName="space-y-0.5 text-sm">
|
||||
{rosterItems.length === 0 ? (
|
||||
<p className="text-sm text-slate-500">No rovers registered.</p>
|
||||
) : (
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Purpose: Defines the Tabs module and the local helpers/components used in this file.
|
||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||
import { createContext, useCallback, useContext, useMemo, useState, useEffect } from 'react';
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
|
||||
const TabsContext = createContext(null);
|
||||
|
||||
@@ -33,6 +34,28 @@ function classNames(...parts) {
|
||||
return parts.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const raw = String(hex || '').trim();
|
||||
const normalized = raw.startsWith('#') ? raw.slice(1) : raw;
|
||||
if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(normalized)) return null;
|
||||
const expanded =
|
||||
normalized.length === 3
|
||||
? normalized
|
||||
.split('')
|
||||
.map((ch) => ch + ch)
|
||||
.join('')
|
||||
: normalized;
|
||||
return {
|
||||
r: Number.parseInt(expanded.slice(0, 2), 16),
|
||||
g: Number.parseInt(expanded.slice(2, 4), 16),
|
||||
b: Number.parseInt(expanded.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
function rgba(rgb, alpha) {
|
||||
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
|
||||
}
|
||||
|
||||
export default function Tabs({ children, defaultTab, currentTab, onTabChange, variant = DEFAULT_VARIANT }) {
|
||||
const [internalTab, setInternalTab] = useState(defaultTab ?? null);
|
||||
const [tabOrder, setTabOrder] = useState([]);
|
||||
@@ -95,7 +118,32 @@ export default function Tabs({ children, defaultTab, currentTab, onTabChange, va
|
||||
}
|
||||
|
||||
export function TabList({ children, className = '' }) {
|
||||
return <div className={classNames('flex gap-0.5', className)}>{children}</div>;
|
||||
const ownRoverColor = useSessionSelector((state) => {
|
||||
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
||||
if (!roverId) return null;
|
||||
const roster = Array.isArray(state.session?.roster) ? state.session.roster : [];
|
||||
const rover = roster.find((entry) => String(entry?.id) === roverId);
|
||||
return rover?.color || null;
|
||||
});
|
||||
const accentRgb = hexToRgb(ownRoverColor);
|
||||
const frameStyle = accentRgb ? { borderColor: rgba(accentRgb, 0.35) } : undefined;
|
||||
const headerStyle = accentRgb
|
||||
? {
|
||||
backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${rgba(accentRgb, 0.18)} 100%)`,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'panel-section overflow-hidden border border-neutral-500/60 bg-neutral-900/95 p-0.5 shadow-[0_1px_0_rgba(255,255,255,0.05)_inset,0_10px_24px_rgba(0,0,0,0.28)]',
|
||||
className
|
||||
)}
|
||||
style={frameStyle}
|
||||
>
|
||||
<div className="flex gap-0.5" style={headerStyle}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Tab({ id, children, className = '', disabled = false, highlight = 'none' }) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useMemo } from 'react';
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
||||
import { useDockIr } from '../../hooks/useDockIr.js';
|
||||
import CardFrame from '../CardFrame/index.jsx';
|
||||
|
||||
function formatMetric(value, fallback = '--') {
|
||||
if (value == null || value === '') return fallback;
|
||||
@@ -39,7 +40,7 @@ export default function TelemetryPanel() {
|
||||
}, [activeDriverId, roverId, selfSocketId, users]);
|
||||
|
||||
return (
|
||||
<section className="panel-section space-y-0.5 text-base text-slate-100">
|
||||
<CardFrame hideHeader clipOverflow={false} bodyClassName="space-y-0.5 text-base text-slate-100">
|
||||
{/* <div className="text-sm text-slate-400">
|
||||
<span>{connected ? 'online' : 'offline'}</span>
|
||||
<span> · role {session?.role || 'unknown'}</span>
|
||||
@@ -60,7 +61,7 @@ export default function TelemetryPanel() {
|
||||
{rawSnippet && (
|
||||
<pre className="surface whitespace-pre-wrap break-words text-xs text-lime-300">{rawSnippet}</pre>
|
||||
)}
|
||||
</section>
|
||||
</CardFrame>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export function NicknameEntryPanel({ compact = false }) {
|
||||
|
||||
export function LinkButtonsPanel() {
|
||||
return (
|
||||
<CardFrame title="Socials" accent="#3030f5" fillHeight bodyClassName="flex flex-1 min-h-0 flex-col gap-0.5 text-base">
|
||||
<CardFrame title="Socials" fillHeight bodyClassName="flex flex-1 min-h-0 flex-col gap-0.5 text-base">
|
||||
<SocialButtonsGrid className="flex-1 min-h-0" />
|
||||
</CardFrame>
|
||||
);
|
||||
|
||||
@@ -530,7 +530,7 @@ export default function VipAudioUploadCard({
|
||||
);
|
||||
|
||||
return (
|
||||
<CardFrame title="Audio Controls" accent="#a78bfa">
|
||||
<CardFrame title="Audio Controls">
|
||||
<div className="grid gap-1">
|
||||
<section className="surface">
|
||||
<div className="flex items-center justify-center gap-0.5 py-0.25 text-xs text-slate-300">
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function VipIdentityCard({ currentStoredKey, applyIdentityKey, on
|
||||
const wrapClass = fullWidth ? 'w-full' : flowWrapClass;
|
||||
|
||||
return (
|
||||
<CardFrame title="Identity key" accent="#60a5fa" className={wrapClass}>
|
||||
<CardFrame title="Identity key" className={wrapClass}>
|
||||
<div className={innerFlowClass}>
|
||||
<p className="text-xs text-slate-500">Current: {maskKey(currentStoredKey) || 'not set yet'}</p>
|
||||
<input
|
||||
|
||||
@@ -70,7 +70,7 @@ export default function VipLiftCard({ lift, onUp, onDown, fullWidth = false }) {
|
||||
return (
|
||||
<CardFrame
|
||||
title="Lift Controls"
|
||||
accent="#06b6d4"
|
||||
|
||||
className={`relative ${wrapClass}`}
|
||||
bodyClassName="text-sm text-slate-200"
|
||||
actions={
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function VipNeatoCard({
|
||||
return (
|
||||
<CardFrame
|
||||
title="Neato Controls"
|
||||
accent="#22c55e"
|
||||
|
||||
className={wrapClass}
|
||||
bodyClassName="text-sm text-slate-200"
|
||||
actions={
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function VipPrivateRoverAccessCard({
|
||||
};
|
||||
|
||||
return (
|
||||
<CardFrame title="Private rover access requests" accent="#f43f5e" className={wrapClass} bodyClassName="text-sm text-slate-300">
|
||||
<CardFrame title="Private rover access requests" className={wrapClass} bodyClassName="text-sm text-slate-300">
|
||||
<div className={innerFlowClass}>
|
||||
{requestableRovers.length === 0 ? (
|
||||
<p className="text-xs text-slate-500">No closed private rovers are available to request right now.</p>
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function VipProfileImageCard({ isVerified = false, fullWidth = fa
|
||||
};
|
||||
|
||||
return (
|
||||
<CardFrame title="Chat profile image URL" accent="#f59e0b" className={wrapClass} bodyClassName="text-sm text-slate-300">
|
||||
<CardFrame title="Chat profile image URL" className={wrapClass} bodyClassName="text-sm text-slate-300">
|
||||
<form className={innerFlowClass} onSubmit={handleSave}>
|
||||
<p className="text-xs text-slate-500">
|
||||
Verified users can set a custom avatar for chat and Discord bridge messages.
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function VipVerificationCard({
|
||||
|
||||
if (pendingRequestId) {
|
||||
return (
|
||||
<CardFrame title="Verification" accent="#eab308" className={wrapClass} bodyClassName="text-sm text-slate-300">
|
||||
<CardFrame title="Verification" className={wrapClass} bodyClassName="text-sm text-slate-300">
|
||||
<div className={innerFlowClass}>Verification request pending: {pendingRequestId}</div>
|
||||
</CardFrame>
|
||||
);
|
||||
@@ -70,7 +70,7 @@ export default function VipVerificationCard({
|
||||
|
||||
if (requestFlowStep === 0) {
|
||||
return (
|
||||
<CardFrame title="Verification" accent="#eab308" className={wrapClass}>
|
||||
<CardFrame title="Verification" className={wrapClass}>
|
||||
<div className={innerFlowClass}>
|
||||
<button type="button" className="button-dark text-sm" onClick={beginRequestFlow} disabled={working}>
|
||||
Request Verification
|
||||
@@ -81,7 +81,7 @@ export default function VipVerificationCard({
|
||||
}
|
||||
|
||||
return (
|
||||
<CardFrame title="Request verification" accent="#eab308" className={wrapClass}>
|
||||
<CardFrame title="Request verification" className={wrapClass}>
|
||||
<form onSubmit={handleRequestSubmit}>
|
||||
<div className={innerFlowClass}>
|
||||
<p className="text-xs text-slate-500">
|
||||
|
||||
Reference in New Issue
Block a user