added background to campain pill

This commit is contained in:
Robert Johnson 2025-05-26 09:52:53 -04:00
parent e09739fc01
commit 69fd462bf5

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import { initializeApp } from 'firebase/app';
import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth';
import { getFirestore, doc, setDoc, addDoc, getDoc, getDocs, collection, onSnapshot, updateDoc, deleteDoc, query, writeBatch } from 'firebase/firestore';
import { PlusCircle, Users, Swords, Shield, Trash2, Eye, Edit3, Save, XCircle, ChevronsUpDown, UserCheck, UserX, HeartCrack, HeartPulse, Zap, Image as ImageIcon, EyeOff, ExternalLink, AlertTriangle } from 'lucide-react'; // Added AlertTriangle
import { PlusCircle, Users, Swords, Shield, Trash2, Eye, Edit3, Save, XCircle, ChevronsUpDown, UserCheck, UserX, HeartCrack, HeartPulse, Zap, /* ImageIcon removed */ EyeOff, ExternalLink, AlertTriangle } from 'lucide-react'; // ImageIcon removed
// --- Firebase Configuration ---
const firebaseConfig = {
@ -126,7 +126,7 @@ function useFirestoreCollection(collectionPath, queryConstraints = []) {
// --- Main App Component ---
function App() {
const [userId, setUserId] = useState(null); // Kept for potential future use, but not displayed
const [userId, setUserId] = useState(null);
const [isAuthReady, setIsAuthReady] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
@ -218,7 +218,6 @@ function App() {
TTRPG Initiative Tracker
</h1>
<div className="flex items-center space-x-4">
{/* UID display removed from header */}
<button
onClick={openPlayerWindow}
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors bg-teal-500 hover:bg-teal-600 text-white flex items-center`}
@ -234,7 +233,7 @@ function App() {
{!isAuthReady && !error && <p>Authenticating...</p>}
</main>
<footer className="bg-slate-900 p-4 text-center text-sm text-slate-400 mt-8">
TTRPG Initiative Tracker v0.1.23
TTRPG Initiative Tracker v0.1.24
</footer>
</div>
);
@ -281,7 +280,7 @@ function AdminView({ userId }) {
const [selectedCampaignId, setSelectedCampaignId] = useState(null);
const [showCreateCampaignModal, setShowCreateCampaignModal] = useState(false);
const [showDeleteCampaignConfirm, setShowDeleteCampaignConfirm] = useState(false);
const [itemToDelete, setItemToDelete] = useState(null); // { id, name, type: 'campaign' }
const [itemToDelete, setItemToDelete] = useState(null);
useEffect(() => {
if (campaignsData) {
@ -360,26 +359,35 @@ function AdminView({ userId }) {
</div>
{campaigns.length === 0 && <p className="text-slate-400">No campaigns yet.</p>}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{campaigns.map(campaign => (
<div
key={campaign.id}
onClick={() => setSelectedCampaignId(campaign.id)}
className={`p-4 rounded-lg shadow-md cursor-pointer transition-all ${selectedCampaignId === campaign.id ? 'bg-sky-700 ring-2 ring-sky-400' : 'bg-slate-700 hover:bg-slate-600'}`}
>
<h3 className="text-xl font-semibold text-white">{campaign.name}</h3>
{/* Campaign ID display removed */}
{campaign.playerDisplayBackgroundUrl && <ImageIcon size={14} className="inline-block mr-1 text-slate-400" title="Has custom background"/>}
<button
onClick={(e) => {
e.stopPropagation();
requestDeleteCampaign(campaign.id, campaign.name);
}}
className="mt-2 text-red-400 hover:text-red-300 text-xs flex items-center"
{campaigns.map(campaign => {
const cardStyle = campaign.playerDisplayBackgroundUrl ? {
backgroundImage: `url(${campaign.playerDisplayBackgroundUrl})`,
} : {};
const cardClasses = `p-4 rounded-lg shadow-md cursor-pointer transition-all relative overflow-hidden bg-cover bg-center ${selectedCampaignId === campaign.id ? 'ring-4 ring-sky-400' : ''} ${!campaign.playerDisplayBackgroundUrl ? 'bg-slate-700 hover:bg-slate-600' : 'hover:shadow-xl'}`;
return (
<div
key={campaign.id}
onClick={() => setSelectedCampaignId(campaign.id)}
className={cardClasses}
style={cardStyle}
>
<Trash2 size={14} className="mr-1" /> Delete
</button>
</div>
))}
{/* Overlay for text readability if background image exists */}
<div className={`relative z-10 ${campaign.playerDisplayBackgroundUrl ? 'bg-black bg-opacity-60 p-3 rounded-md' : ''}`}>
<h3 className="text-xl font-semibold text-white">{campaign.name}</h3>
<button
onClick={(e) => {
e.stopPropagation();
requestDeleteCampaign(campaign.id, campaign.name);
}}
className="mt-2 text-red-300 hover:text-red-100 text-xs flex items-center bg-black bg-opacity-50 hover:bg-opacity-70 px-2 py-1 rounded"
>
<Trash2 size={14} className="mr-1" /> Delete
</button>
</div>
</div>
);
})}
</div>
</div>
{showCreateCampaignModal && <Modal onClose={() => setShowCreateCampaignModal(false)} title="Create New Campaign"><CreateCampaignForm onCreate={handleCreateCampaign} onCancel={() => setShowCreateCampaignModal(false)} /></Modal>}
@ -407,7 +415,13 @@ function AdminView({ userId }) {
);
}
// ... (CreateCampaignForm remains the same)
// ... (CreateCampaignForm, CharacterManager, EncounterManager, CreateEncounterForm, ParticipantManager, EditParticipantModal, InitiativeControls, DisplayView, Modal, Icons remain the same as v0.1.23)
// For brevity, only the changed AdminView is shown in full. The rest of the components are identical to the previous version.
// The DisplayView, CharacterManager, EncounterManager, etc., and their sub-components
// (CreateEncounterForm, ParticipantManager, EditParticipantModal, InitiativeControls)
// are not changed from the previous version provided (v0.1.23).
// The Modal component and Icon components also remain unchanged.
function CreateCampaignForm({ onCreate, onCancel }) {
const [name, setName] = useState('');
const [backgroundUrl, setBackgroundUrl] = useState('');
@ -436,12 +450,11 @@ function CreateCampaignForm({ onCreate, onCancel }) {
}
// --- CharacterManager ---
function CharacterManager({ campaignId, campaignCharacters }) {
const [characterName, setCharacterName] = useState('');
const [editingCharacter, setEditingCharacter] = useState(null);
const [showDeleteCharConfirm, setShowDeleteCharConfirm] = useState(false);
const [itemToDelete, setItemToDelete] = useState(null); // { id, name, type: 'character' }
const [itemToDelete, setItemToDelete] = useState(null);
const handleAddCharacter = async () => {
@ -512,7 +525,6 @@ function CharacterManager({ campaignId, campaignCharacters }) {
);
}
// --- EncounterManager ---
function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharacters }) {
const {data: encountersData, isLoading: isLoadingEncounters } = useFirestoreCollection(campaignId ? getEncountersCollectionPath(campaignId) : null);
const {data: activeDisplayInfo } = useFirestoreDocument(getActiveDisplayDocPath());
@ -521,7 +533,7 @@ function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharac
const [selectedEncounterId, setSelectedEncounterId] = useState(null);
const [showCreateEncounterModal, setShowCreateEncounterModal] = useState(false);
const [showDeleteEncounterConfirm, setShowDeleteEncounterConfirm] = useState(false);
const [itemToDelete, setItemToDelete] = useState(null); // { id, name, type: 'encounter' }
const [itemToDelete, setItemToDelete] = useState(null);
const selectedEncounterIdRef = useRef(selectedEncounterId);
@ -669,10 +681,6 @@ function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharac
);
}
// ... (CreateEncounterForm, ParticipantManager, EditParticipantModal, InitiativeControls, DisplayView, Modal, Icons)
// The rest of the components remain the same as in v0.1.22, with the following changes in ParticipantManager and InitiativeControls
// to use the ConfirmationModal for delete/end actions.
function CreateEncounterForm({ onCreate, onCancel }) {
const [name, setName] = useState('');
return (
@ -699,7 +707,7 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters }) {
const [hpChangeValues, setHpChangeValues] = useState({});
const [draggedItemId, setDraggedItemId] = useState(null);
const [showDeleteParticipantConfirm, setShowDeleteParticipantConfirm] = useState(false);
const [itemToDelete, setItemToDelete] = useState(null); // { id, name, type: 'participant' }
const [itemToDelete, setItemToDelete] = useState(null);
const participants = encounter.participants || [];
@ -1143,7 +1151,7 @@ function DisplayView() {
const { name, participants, round, currentTurnParticipantId, isStarted } = activeEncounterData;
let participantsToRender = []; // Renamed from displayParticipants for clarity
let participantsToRender = [];
if (participants) {
if (isStarted && activeEncounterData.turnOrderIds?.length > 0 ) {
participantsToRender = activeEncounterData.turnOrderIds