fix: custom conditions apply on add; correct server WS env docs
Custom conditions (per-campaign freeform) previously only persisted to the
campaign palette — Add button did NOT apply them to the targeted participant,
and badges rendered via CONDITIONS (missing custom ids) so applied custom
conditions did not display. Now:
- addCustomCondition(label, participantId) persists to palette AND toggles
onto the participant in one step. Enter + Add button both pass p.id.
- Admin badge lookup uses allConditions (built-ins + custom), not CONDITIONS.
Shared contract: toggleCondition accepts ANY string. Prove it in tests:
- turn.combat.test.js adds CUSTOM_CONDITIONS ('hexed','rager',
'marked_for_death','🛡️blessed') to the seeded queue + random pool, asserts
condition-not-string invariant, and a dedicated add/remove round-trip test.
- replay-combat.js mirrors the same CUSTOM_CONDITIONS through the live backend.
Server-mode docs/env: REACT_APP_BACKEND_WS (stale pre-rename) -> correct
REACT_APP_BACKEND_REALTIME_URL=ws://.../ws in README, env.example,
docker/Dockerfile (STORAGE=ws -> server). DEVELOPMENT.md + scripts/README.md
now point to ./scripts/dev-start.sh as the primary dev entrypoint.
133 shared tests green.
This commit is contained in:
+22
-13
@@ -1100,19 +1100,28 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp
|
||||
}
|
||||
};
|
||||
|
||||
// Persist new freeform condition to campaign doc. Persists across encounters.
|
||||
const addCustomCondition = async (label) => {
|
||||
// Add freeform condition: persist to campaign palette + apply to current participant.
|
||||
const addCustomCondition = async (label, participantId = null) => {
|
||||
const trimmed = (label || '').trim();
|
||||
if (!trimmed || !campaignId) return;
|
||||
// dedupe against built-ins + existing custom (case-insensitive)
|
||||
setCustomConditionInput('');
|
||||
const exists = allConditions.some(c => c.label.toLowerCase() === trimmed.toLowerCase());
|
||||
if (exists) { setCustomConditionInput(''); return; }
|
||||
const next = [...customConditions, trimmed];
|
||||
try {
|
||||
await storage.updateDoc(getPath.campaign(campaignId), { customConditions: next });
|
||||
setCustomConditionInput('');
|
||||
} catch (err) {
|
||||
// fall through silently
|
||||
if (!exists) {
|
||||
try {
|
||||
await storage.updateDoc(getPath.campaign(campaignId), { customConditions: [...customConditions, trimmed] });
|
||||
} catch (err) {}
|
||||
}
|
||||
if (participantId) {
|
||||
const cur = (encounter.participants.find(p => p.id === participantId)?.conditions) || [];
|
||||
if (!cur.includes(trimmed)) {
|
||||
try {
|
||||
const { patch, log } = combatToggleCondition(encounter, participantId, trimmed);
|
||||
await storage.updateDoc(encounterPath, patch);
|
||||
logAction(log.message.replace(trimmed, trimmed), { encounterName: encounter.name }, {
|
||||
encounterPath, updates: log.undo,
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1443,7 +1452,7 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp
|
||||
{(p.conditions || []).length > 0 && (
|
||||
<div className="mt-1 flex flex-wrap gap-1">
|
||||
{(p.conditions || []).map(cId => {
|
||||
const cond = CONDITIONS.find(c => c.id === cId);
|
||||
const cond = allConditions.find(c => c.id === cId);
|
||||
return cond ? (
|
||||
<span
|
||||
key={cId}
|
||||
@@ -1493,7 +1502,7 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp
|
||||
value={customConditionInput}
|
||||
onChange={(e) => setCustomConditionInput(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') { e.preventDefault(); addCustomCondition(customConditionInput); }
|
||||
if (e.key === 'Enter') { e.preventDefault(); addCustomCondition(customConditionInput, p.id); }
|
||||
}}
|
||||
placeholder="Add custom condition..."
|
||||
className="flex-grow px-2 py-1 text-xs rounded bg-stone-800 border border-stone-600 text-stone-200 placeholder-stone-500 focus:outline-none focus:border-amber-500"
|
||||
@@ -1501,7 +1510,7 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => addCustomCondition(customConditionInput)}
|
||||
onClick={() => addCustomCondition(customConditionInput, p.id)}
|
||||
disabled={!customConditionInput.trim()}
|
||||
className="px-2 py-1 rounded text-xs bg-amber-700 border border-amber-500 text-white hover:bg-amber-600 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user