added background to campain pill
This commit is contained in:
parent
e09739fc01
commit
69fd462bf5
78
src/App.js
78
src/App.js
@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
|
|||||||
import { initializeApp } from 'firebase/app';
|
import { initializeApp } from 'firebase/app';
|
||||||
import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth';
|
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 { 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 ---
|
// --- Firebase Configuration ---
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
@ -126,7 +126,7 @@ function useFirestoreCollection(collectionPath, queryConstraints = []) {
|
|||||||
|
|
||||||
// --- Main App Component ---
|
// --- Main App Component ---
|
||||||
function App() {
|
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 [isAuthReady, setIsAuthReady] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
@ -218,7 +218,6 @@ function App() {
|
|||||||
TTRPG Initiative Tracker
|
TTRPG Initiative Tracker
|
||||||
</h1>
|
</h1>
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
{/* UID display removed from header */}
|
|
||||||
<button
|
<button
|
||||||
onClick={openPlayerWindow}
|
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`}
|
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>}
|
{!isAuthReady && !error && <p>Authenticating...</p>}
|
||||||
</main>
|
</main>
|
||||||
<footer className="bg-slate-900 p-4 text-center text-sm text-slate-400 mt-8">
|
<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>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -281,7 +280,7 @@ function AdminView({ userId }) {
|
|||||||
const [selectedCampaignId, setSelectedCampaignId] = useState(null);
|
const [selectedCampaignId, setSelectedCampaignId] = useState(null);
|
||||||
const [showCreateCampaignModal, setShowCreateCampaignModal] = useState(false);
|
const [showCreateCampaignModal, setShowCreateCampaignModal] = useState(false);
|
||||||
const [showDeleteCampaignConfirm, setShowDeleteCampaignConfirm] = useState(false);
|
const [showDeleteCampaignConfirm, setShowDeleteCampaignConfirm] = useState(false);
|
||||||
const [itemToDelete, setItemToDelete] = useState(null); // { id, name, type: 'campaign' }
|
const [itemToDelete, setItemToDelete] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (campaignsData) {
|
if (campaignsData) {
|
||||||
@ -360,26 +359,35 @@ function AdminView({ userId }) {
|
|||||||
</div>
|
</div>
|
||||||
{campaigns.length === 0 && <p className="text-slate-400">No campaigns yet.</p>}
|
{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">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{campaigns.map(campaign => (
|
{campaigns.map(campaign => {
|
||||||
<div
|
const cardStyle = campaign.playerDisplayBackgroundUrl ? {
|
||||||
key={campaign.id}
|
backgroundImage: `url(${campaign.playerDisplayBackgroundUrl})`,
|
||||||
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'}`}
|
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'}`;
|
||||||
>
|
|
||||||
<h3 className="text-xl font-semibold text-white">{campaign.name}</h3>
|
return (
|
||||||
{/* Campaign ID display removed */}
|
<div
|
||||||
{campaign.playerDisplayBackgroundUrl && <ImageIcon size={14} className="inline-block mr-1 text-slate-400" title="Has custom background"/>}
|
key={campaign.id}
|
||||||
<button
|
onClick={() => setSelectedCampaignId(campaign.id)}
|
||||||
onClick={(e) => {
|
className={cardClasses}
|
||||||
e.stopPropagation();
|
style={cardStyle}
|
||||||
requestDeleteCampaign(campaign.id, campaign.name);
|
|
||||||
}}
|
|
||||||
className="mt-2 text-red-400 hover:text-red-300 text-xs flex items-center"
|
|
||||||
>
|
>
|
||||||
<Trash2 size={14} className="mr-1" /> Delete
|
{/* Overlay for text readability if background image exists */}
|
||||||
</button>
|
<div className={`relative z-10 ${campaign.playerDisplayBackgroundUrl ? 'bg-black bg-opacity-60 p-3 rounded-md' : ''}`}>
|
||||||
</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
{showCreateCampaignModal && <Modal onClose={() => setShowCreateCampaignModal(false)} title="Create New Campaign"><CreateCampaignForm onCreate={handleCreateCampaign} onCancel={() => setShowCreateCampaignModal(false)} /></Modal>}
|
{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 }) {
|
function CreateCampaignForm({ onCreate, onCancel }) {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [backgroundUrl, setBackgroundUrl] = useState('');
|
const [backgroundUrl, setBackgroundUrl] = useState('');
|
||||||
@ -436,12 +450,11 @@ function CreateCampaignForm({ onCreate, onCancel }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- CharacterManager ---
|
|
||||||
function CharacterManager({ campaignId, campaignCharacters }) {
|
function CharacterManager({ campaignId, campaignCharacters }) {
|
||||||
const [characterName, setCharacterName] = useState('');
|
const [characterName, setCharacterName] = useState('');
|
||||||
const [editingCharacter, setEditingCharacter] = useState(null);
|
const [editingCharacter, setEditingCharacter] = useState(null);
|
||||||
const [showDeleteCharConfirm, setShowDeleteCharConfirm] = useState(false);
|
const [showDeleteCharConfirm, setShowDeleteCharConfirm] = useState(false);
|
||||||
const [itemToDelete, setItemToDelete] = useState(null); // { id, name, type: 'character' }
|
const [itemToDelete, setItemToDelete] = useState(null);
|
||||||
|
|
||||||
|
|
||||||
const handleAddCharacter = async () => {
|
const handleAddCharacter = async () => {
|
||||||
@ -512,7 +525,6 @@ function CharacterManager({ campaignId, campaignCharacters }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- EncounterManager ---
|
|
||||||
function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharacters }) {
|
function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharacters }) {
|
||||||
const {data: encountersData, isLoading: isLoadingEncounters } = useFirestoreCollection(campaignId ? getEncountersCollectionPath(campaignId) : null);
|
const {data: encountersData, isLoading: isLoadingEncounters } = useFirestoreCollection(campaignId ? getEncountersCollectionPath(campaignId) : null);
|
||||||
const {data: activeDisplayInfo } = useFirestoreDocument(getActiveDisplayDocPath());
|
const {data: activeDisplayInfo } = useFirestoreDocument(getActiveDisplayDocPath());
|
||||||
@ -521,7 +533,7 @@ function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharac
|
|||||||
const [selectedEncounterId, setSelectedEncounterId] = useState(null);
|
const [selectedEncounterId, setSelectedEncounterId] = useState(null);
|
||||||
const [showCreateEncounterModal, setShowCreateEncounterModal] = useState(false);
|
const [showCreateEncounterModal, setShowCreateEncounterModal] = useState(false);
|
||||||
const [showDeleteEncounterConfirm, setShowDeleteEncounterConfirm] = 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);
|
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 }) {
|
function CreateEncounterForm({ onCreate, onCancel }) {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
return (
|
return (
|
||||||
@ -699,7 +707,7 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters }) {
|
|||||||
const [hpChangeValues, setHpChangeValues] = useState({});
|
const [hpChangeValues, setHpChangeValues] = useState({});
|
||||||
const [draggedItemId, setDraggedItemId] = useState(null);
|
const [draggedItemId, setDraggedItemId] = useState(null);
|
||||||
const [showDeleteParticipantConfirm, setShowDeleteParticipantConfirm] = useState(false);
|
const [showDeleteParticipantConfirm, setShowDeleteParticipantConfirm] = useState(false);
|
||||||
const [itemToDelete, setItemToDelete] = useState(null); // { id, name, type: 'participant' }
|
const [itemToDelete, setItemToDelete] = useState(null);
|
||||||
|
|
||||||
|
|
||||||
const participants = encounter.participants || [];
|
const participants = encounter.participants || [];
|
||||||
@ -1143,7 +1151,7 @@ function DisplayView() {
|
|||||||
|
|
||||||
const { name, participants, round, currentTurnParticipantId, isStarted } = activeEncounterData;
|
const { name, participants, round, currentTurnParticipantId, isStarted } = activeEncounterData;
|
||||||
|
|
||||||
let participantsToRender = []; // Renamed from displayParticipants for clarity
|
let participantsToRender = [];
|
||||||
if (participants) {
|
if (participants) {
|
||||||
if (isStarted && activeEncounterData.turnOrderIds?.length > 0 ) {
|
if (isStarted && activeEncounterData.turnOrderIds?.length > 0 ) {
|
||||||
participantsToRender = activeEncounterData.turnOrderIds
|
participantsToRender = activeEncounterData.turnOrderIds
|
||||||
|
Loading…
x
Reference in New Issue
Block a user