11<template >
22 <div class =" container py-4" >
3- <div v-if =" !campaign" >
3+ <div v-if =" !userId" >
4+ <p class =" text-muted" >Please login to view this campaign.</p >
5+ </div >
6+
7+ <div v-else-if =" !campaign" >
48 <p class =" text-muted" >Loading campaign...</p >
59 </div >
610
711 <div v-else >
8- <h1 class =" d-print-none d-flex" >
9- Campaign
10- <div class =" ms-3" v-if =" isOwner" >
11- <a href =" /campaigns/" class =" btn btn-secondary mb-2" ><img src =" @/assets/earth-asia-solid.svg" alt =" plus icon" class =" filter-white" /> My Campaigns</a >
12+ <div class =" d-flex justify-content-between align-items-center mb-3" >
13+ <h1 class =" d-print-none" >Campaign</h1 >
14+ <div v-if =" isOwner" class =" d-flex gap-2" >
15+ <a href =" /campaigns/" class =" btn btn-outline-secondary" >
16+ <img src =" @/assets/earth-asia-solid.svg" alt =" planet icon" class =" filter-black" /> My Campaigns
17+ </a >
18+ <button @click =" saveCampaignDetails" class =" btn btn-outline-primary btn-sm" >Save Campaign</button >
1219 </div >
13- </h1 >
20+ </div >
1421
1522 <div class =" mb-4" >
1623 <input v-model =" campaign.name" class =" form-control form-control-lg mb-2" placeholder =" Campaign Name" />
1724 <textarea v-model =" campaign.description" class =" form-control mb-2" placeholder =" Campaign Description" ></textarea >
18- <button v-if =" isOwner" @click =" saveCampaignDetails" class =" btn btn-outline-primary btn-sm" >Save Campaign</button >
1925 </div >
2026
2127 <section class =" mb-4" >
22- <h2 class =" h5 mb-3" >Linked Items</h2 >
28+ <h2 class =" h5 mb-3" >Campaign Items
29+ <button v-if =" isOwner" class =" btn btn-outline-primary btn-sm" data-bs-toggle =" modal" data-bs-target =" #assignModal" >
30+ Assign Items
31+ </button >
32+ </h2 >
2333 <div class =" row" >
2434 <div class =" col-md-6 mb-3" >
25- <strong >Characters:</strong >
26- <ul class =" list-unstyled" >
27- <li v-for =" char in linkedCharacters" :key =" char.id" >
28- <RouterLink :to =" { name: ' character' , params: { id: char .id } } " class="link-primary">
29- {{ char.name }} ({{ char.profession.name }})
30- </RouterLink >
31- </li >
32- </ul >
35+ <div class =" border rounded p-2 overflow-auto" style =" max-height : 200px ;" >
36+ <strong >Characters:</strong >
37+ <ul class =" list-unstyled mb-0" >
38+ <li v-for =" char in linkedCharacters" :key =" char.id" >
39+ <RouterLink :to =" { name: ' character' , params: { id: char .id } } " class="link-primary">
40+ {{ char.name }} ({{ char.profession.name }})
41+ </RouterLink >
42+ </li >
43+ </ul >
44+ </div >
3345 </div >
3446 <div class =" col-md-6 mb-3" >
35- <strong >Fronts:</strong >
36- <ul class =" list-unstyled" >
37- <li v-for =" front in linkedFronts" :key =" front.id" >
38- <span v-if =" front.resolved" class =" me-1" >☑</span >
39- <span v-else class =" me-1" >☐</span >
40- <RouterLink :to =" { name: ' front' , params: { id: front .id } } " class="link-primary">
41- {{ front.name }} ({{ front.type }})
42- </RouterLink >
43- </li >
44- </ul >
47+ <div class =" border rounded p-2 overflow-auto" style =" max-height : 200px ;" >
48+ <strong >Fronts:</strong >
49+ <ul class =" list-unstyled mb-0" >
50+ <li v-for =" front in linkedFronts" :key =" front.id" >
51+ <span v-if =" front.resolved" class =" me-1" >☑</span >
52+ <span v-else class =" me-1" >☐</span >
53+ <RouterLink :to =" { name: ' front' , params: { id: front .id } } " class="link-primary">
54+ {{ front.name }} ({{ front.type }})
55+ </RouterLink >
56+ </li >
57+ </ul >
58+ </div >
4559 </div >
4660 <div class =" col-md-6 mb-3" >
47- <strong >Maps:</strong >
48- <ul class =" list-unstyled" >
49- <li v-for =" map in linkedMaps" :key =" map.id" >
50- <RouterLink :to =" { name: ' map' , params: { id: map .id } } " class="link-primary">
51- {{ map.name }}
52- </RouterLink >
53- </li >
54- </ul >
61+ <div class =" border rounded p-2 overflow-auto" style =" max-height : 200px ;" >
62+ <strong >Maps:</strong >
63+ <ul class =" list-unstyled mb-0" >
64+ <li v-for =" map in linkedMaps" :key =" map.id" >
65+ <RouterLink :to =" { name: ' map' , params: { id: map .id } } " class="link-primary">
66+ {{ map.name }}
67+ </RouterLink >
68+ </li >
69+ </ul >
70+ </div >
5571 </div >
5672 <div class =" col-md-6 mb-3" >
57- <strong >Steadings:</strong >
58- <ul class =" list-unstyled" >
59- <li v-for =" steading in linkedSteadings" :key =" steading.id" >
60- <RouterLink :to =" { name: ' steading' , params: { id: steading .id } } " class="link-primary">
61- {{ steading.name }} ({{ steading.type }})
62- </RouterLink >
63- </li >
64- </ul >
73+ <div class =" border rounded p-2 overflow-auto" style =" max-height : 200px ;" >
74+ <strong >Steadings:</strong >
75+ <ul class =" list-unstyled mb-0" >
76+ <li v-for =" steading in linkedSteadings" :key =" steading.id" >
77+ <RouterLink :to =" { name: ' steading' , params: { id: steading .id } } " class="link-primary">
78+ {{ steading.name }} ({{ steading.type }})
79+ </RouterLink >
80+ </li >
81+ </ul >
82+ </div >
6583 </div >
6684 </div >
6785 </section >
6886
69- <section class =" mb-4" v-if =" isOwner" >
70- <button class =" btn btn-primary" data-bs-toggle =" modal" data-bs-target =" #assignModal" >
71- Assign Items
72- </button >
73- </section >
74-
7587 <section class =" mb-4" >
7688 <h2 class =" h5 mb-2" >Sessions</h2 >
7789 <ul v-if =" campaign.sessions?.length" class =" list-group mb-3" >
91103 <h3 class =" h6 mb-1" >{{ session.title }}</h3 >
92104 <p class =" text-muted small mb-2" >{{ session.date }}</p >
93105 <div v-html =" renderMarkdown(session.notes)" class =" prose" ></div >
94- <div class =" mt-2" >
106+ <div class =" mt-2" v-if = " isOwner " >
95107 <button class =" btn btn-sm btn-outline-secondary" @click =" editSession(session.id)" >Edit</button >
96108 <button class =" btn btn-sm btn-outline-danger ms-2" @click =" confirmDelete(session.id)" >Delete</button >
97109 </div >
179191
180192 </div >
181193</template >
194+
182195<script setup lang="ts">
183196import { computed , onMounted , ref } from ' vue' ;
184197import { useCampaignStore } from ' @/stores/campaignStore' ;
@@ -223,11 +236,11 @@ const sortedMaps = computed(() =>
223236const loadCampaign = async () => {
224237 await campaignStore .fetchCampaignById (route .params .id as string );
225238 campaign .value = campaignStore .selectedCampaign ;
226- await loadLinkedEntities ();
227- await loadAvailableEntities ();
239+ await loadLinkedMapsAndCharacters ();
240+ await loadAvailableMapsAndCharacters ();
228241};
229242
230- const loadLinkedEntities = async () => {
243+ const loadLinkedMapsAndCharacters = async () => {
231244 if (! campaign .value ) return ;
232245 const characterPromises = campaign .value .characterIds .map (async (id : string ) => {
233246 const c = await getCharacter (id );
@@ -240,7 +253,7 @@ const loadLinkedEntities = async () => {
240253 await Promise .all ([... characterPromises , ... mapPromises ]);
241254};
242255
243- const loadAvailableEntities = async () => {
256+ const loadAvailableMapsAndCharacters = async () => {
244257 if (! campaign .value ?.userId ) return ;
245258 allCharacters .value = await getCharactersWithProfessions (campaign .value .userId );
246259 allMaps .value = await getMaps (campaign .value .userId );
@@ -289,7 +302,12 @@ const saveLinkedEntitiesAndClose = async () => {
289302 characterIds: campaign .value .characterIds ,
290303 mapIds: campaign .value .mapIds
291304 });
305+
306+ // Immediately fetch and update local entity objects
307+ await loadLinkedMapsAndCharacters ();
292308 await campaignStore .loadLinkedEntities ();
309+
310+ toast (' Campaign updated.' );
293311};
294312
295313const saveCampaignDetails = async () => {
0 commit comments