Generic (non-5e) ruleset mode + UI polish + combat switch fix #7

Merged
robert merged 25 commits from feat/generic-ruleset into main 2026-07-08 22:54:04 -04:00
Showing only changes of commit 1078062b53 - Show all commits
+27 -1
View File
@@ -2330,6 +2330,7 @@ function AdminView({ userId }) {
const { data: initialActiveInfo } = useFirestoreDocument(getPath.activeDisplay()); const { data: initialActiveInfo } = useFirestoreDocument(getPath.activeDisplay());
const [campaignsWithDetails, setCampaignsWithDetails] = useState([]); const [campaignsWithDetails, setCampaignsWithDetails] = useState([]);
const [draggedCampaignId, setDraggedCampaignId] = useState(null);
const [selectedCampaignId, setSelectedCampaignId] = useState(null); const [selectedCampaignId, setSelectedCampaignId] = useState(null);
const encounterStartedRef = useRef(false); const encounterStartedRef = useRef(false);
const encounterActiveRef = useRef(false); const encounterActiveRef = useRef(false);
@@ -2390,6 +2391,10 @@ function AdminView({ userId }) {
fetchDetails(); fetchDetails();
} else if (campaignsData) { } else if (campaignsData) {
const sorted = [...campaignsData].sort((a, b) => { const sorted = [...campaignsData].sort((a, b) => {
const ao = a.order ?? null, bo = b.order ?? null;
if (ao !== null && bo !== null) return ao - bo;
if (ao !== null) return -1;
if (bo !== null) return 1;
const at = a.createdAt || 0; const at = a.createdAt || 0;
const bt = b.createdAt || 0; const bt = b.createdAt || 0;
if (at === bt) return (a.name || '').localeCompare(b.name || ''); if (at === bt) return (a.name || '').localeCompare(b.name || '');
@@ -2433,6 +2438,7 @@ function AdminView({ userId }) {
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
players: [], players: [],
ruleset: ruleset || '5e', ruleset: ruleset || '5e',
order: (campaignsWithDetails || []).reduce((max, c) => Math.max(max, c.order ?? -1), -1) + 1,
}); });
setShowCreateModal(false); setShowCreateModal(false);
@@ -2579,11 +2585,28 @@ function AdminView({ userId }) {
? { backgroundImage: `url(${campaign.playerDisplayBackgroundUrl})` } ? { backgroundImage: `url(${campaign.playerDisplayBackgroundUrl})` }
: {}; : {};
const cardClasses = `h-40 flex flex-col justify-between rounded-lg shadow-md cursor-pointer transition-all relative overflow-hidden bg-cover bg-center ${selectedCampaignId === campaign.id ? 'ring-4 ring-amber-500' : ''} ${!campaign.playerDisplayBackgroundUrl ? 'bg-stone-800 hover:bg-stone-700' : 'hover:shadow-xl'}`; const cardClasses = `h-40 flex flex-col justify-between rounded-lg shadow-md cursor-grab transition-all relative overflow-hidden bg-cover bg-center ${selectedCampaignId === campaign.id ? 'ring-4 ring-amber-500' : ''} ${!campaign.playerDisplayBackgroundUrl ? 'bg-stone-800 hover:bg-stone-700' : 'hover:shadow-xl'} ${draggedCampaignId === campaign.id ? 'opacity-50 ring-2 ring-yellow-400' : ''}`;
return ( return (
<div <div
key={campaign.id} key={campaign.id}
draggable
onDragStart={(e) => { setDraggedCampaignId(campaign.id); e.dataTransfer.effectAllowed = 'move'; }}
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }}
onDrop={async (e) => {
e.preventDefault();
if (!db || !draggedCampaignId || draggedCampaignId === campaign.id) { setDraggedCampaignId(null); return; }
const reordered = [...campaignsWithDetails];
const fromIdx = reordered.findIndex(c => c.id === draggedCampaignId);
const toIdx = reordered.findIndex(c => c.id === campaign.id);
if (fromIdx === -1 || toIdx === -1) { setDraggedCampaignId(null); return; }
const [moved] = reordered.splice(fromIdx, 1);
reordered.splice(toIdx, 0, moved);
const ops = reordered.map((c, i) => ({ type: 'update', path: getPath.campaign(c.id), data: { order: i } }));
try { await storage.batchWrite(ops); } catch (err) {}
setDraggedCampaignId(null);
}}
onDragEnd={() => setDraggedCampaignId(null)}
onClick={() => { onClick={() => {
if (encounterStartedRef.current) { if (encounterStartedRef.current) {
showToast('End or pause active encounter before switching campaigns.'); showToast('End or pause active encounter before switching campaigns.');
@@ -2599,7 +2622,10 @@ function AdminView({ userId }) {
className={`relative z-10 flex flex-col justify-between h-full ${campaign.playerDisplayBackgroundUrl ? 'bg-black bg-opacity-60 p-3' : 'p-4'}`} className={`relative z-10 flex flex-col justify-between h-full ${campaign.playerDisplayBackgroundUrl ? 'bg-black bg-opacity-60 p-3' : 'p-4'}`}
> >
<div> <div>
<div className="flex items-center gap-1">
<ChevronsUpDown size={14} className="text-white/60 flex-shrink-0" title="Drag to reorder" />
<h3 className="text-xl font-semibold text-white">{campaign.name}</h3> <h3 className="text-xl font-semibold text-white">{campaign.name}</h3>
</div>
<div className="text-xs text-stone-100 mt-1 space-x-3"> <div className="text-xs text-stone-100 mt-1 space-x-3">
<span className="inline-flex items-center"> <span className="inline-flex items-center">
<Users size={12} className="mr-1" /> {campaign.characters?.length || 0} Characters <Users size={12} className="mr-1" /> {campaign.characters?.length || 0} Characters