mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
roverquesting
This commit is contained in:
@@ -5,12 +5,14 @@ import { COOKIE_KEY_REGEX, flowWrapClass } from './vip/constants.js';
|
||||
import VipAudioUploadCard from './vip/VipAudioUploadCard.jsx';
|
||||
import VipVerificationCard from './vip/VipVerificationCard.jsx';
|
||||
import VipIdentityCard from './vip/VipIdentityCard.jsx';
|
||||
import VipPrivateRoverAccessCard from './vip/VipPrivateRoverAccessCard.jsx';
|
||||
|
||||
export default function VipPanel() {
|
||||
const {
|
||||
session,
|
||||
identifySession,
|
||||
requestVerification,
|
||||
requestPrivateRoverAccess,
|
||||
playUploadedAudio,
|
||||
stopUploadedAudio,
|
||||
startMicWhip,
|
||||
@@ -24,6 +26,8 @@ export default function VipPanel() {
|
||||
const nickname = (profile?.nickname || '').trim();
|
||||
const isVerified = Boolean(session?.isVerified);
|
||||
const pendingRequestId = session?.verification?.pendingRequestId || null;
|
||||
const requestablePrivateRovers = session?.privateRoverAccess?.requestableRovers || [];
|
||||
const pendingPrivateRoverRequests = session?.privateRoverAccess?.pendingRequests || [];
|
||||
const ownRoverId = String(session?.assignment?.roverId || '').trim();
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
@@ -84,6 +88,16 @@ export default function VipPanel() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-2">
|
||||
<VipPrivateRoverAccessCard
|
||||
requestableRovers={requestablePrivateRovers}
|
||||
pendingRequests={pendingPrivateRoverRequests}
|
||||
requestPrivateRoverAccess={requestPrivateRoverAccess}
|
||||
onMessage={setMessage}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-2">
|
||||
<VipIdentityCard
|
||||
currentStoredKey={currentStoredKey}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { flowWrapClass, innerFlowClass } from './constants.js';
|
||||
|
||||
export default function VipPrivateRoverAccessCard({
|
||||
requestableRovers = [],
|
||||
pendingRequests = [],
|
||||
requestPrivateRoverAccess,
|
||||
onMessage,
|
||||
fullWidth = false,
|
||||
}) {
|
||||
const [pendingByRover, setPendingByRover] = useState({});
|
||||
const pendingMap = useMemo(() => {
|
||||
const next = new Map();
|
||||
(pendingRequests || []).forEach((entry) => {
|
||||
if (!entry?.roverId) return;
|
||||
next.set(String(entry.roverId), entry);
|
||||
});
|
||||
return next;
|
||||
}, [pendingRequests]);
|
||||
|
||||
const wrapClass = fullWidth ? 'w-full' : flowWrapClass;
|
||||
|
||||
const handleRequest = async (roverId) => {
|
||||
if (!roverId) return;
|
||||
setPendingByRover((prev) => ({ ...prev, [roverId]: true }));
|
||||
onMessage?.('');
|
||||
try {
|
||||
const response = await requestPrivateRoverAccess?.(roverId);
|
||||
if (response?.existing) {
|
||||
onMessage?.('You already have a pending request for that rover.');
|
||||
} else {
|
||||
onMessage?.('Private rover access request sent to lockdown admins.');
|
||||
}
|
||||
} catch (err) {
|
||||
onMessage?.(err.message || 'Failed to send request.');
|
||||
} finally {
|
||||
setPendingByRover((prev) => ({ ...prev, [roverId]: false }));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={`surface text-sm text-slate-300 ${wrapClass}`}>
|
||||
<div className={innerFlowClass}>
|
||||
<p className="text-sm text-slate-300">Private rover access requests</p>
|
||||
{requestableRovers.length === 0 ? (
|
||||
<p className="text-xs text-slate-500">No closed private rovers are available to request right now.</p>
|
||||
) : (
|
||||
<div className="w-full space-y-0.5">
|
||||
{requestableRovers.map((rover) => {
|
||||
const roverId = String(rover.id);
|
||||
const inFlight = Boolean(pendingByRover[roverId]);
|
||||
const pending = pendingMap.get(roverId);
|
||||
return (
|
||||
<div key={roverId} className="surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5">
|
||||
<div className="min-w-0 text-left">
|
||||
<p className="truncate text-xs font-semibold text-slate-100">{rover.name || roverId}</p>
|
||||
<p className="truncate text-[0.7rem] text-slate-500">{roverId}</p>
|
||||
</div>
|
||||
{pending ? (
|
||||
<span className="rounded bg-amber-700/30 px-1 py-0.5 text-[0.7rem] text-amber-200">
|
||||
Pending
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRequest(roverId)}
|
||||
disabled={inFlight}
|
||||
className="button-dark text-xs disabled:opacity-50"
|
||||
>
|
||||
{inFlight ? 'Sending...' : 'Request'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user