mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-11-14 18:58:49 -05:00
88 lines
3.3 KiB
React
88 lines
3.3 KiB
React
// Vip Private Rover Access Card
|
|
// Purpose: Defines the Vip Private Rover Access Card 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 { useMemo, useState } from 'react';
|
|
import CardFrame from '../CardFrame/index.jsx';
|
|
import { flowWrapClass, innerFlowClass } from './constants.js';
|
|
import RoverLabel from '../RoverLabel/index.jsx';
|
|
|
|
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 (
|
|
<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>
|
|
) : (
|
|
<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">
|
|
<RoverLabel rover={rover} fallback={roverId} className="max-w-full truncate align-middle" />
|
|
</p>
|
|
<p className="truncate text-[0.7rem] text-slate-500">{roverId}</p>
|
|
</div>
|
|
{pending ? (
|
|
<span className="rounded-sm 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>
|
|
</CardFrame>
|
|
);
|
|
}
|