@@ -109,6 +109,28 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
109109 // Data
110110 // ---------------------------------------------------------------------------
111111
112+ /**
113+ * Normalize an API response to an array.
114+ * Chronicle endpoints may return a plain array or an object wrapping
115+ * the array under common keys like `data`, `entity_types`, `entities`,
116+ * `maps`, etc. This helper unwraps whichever shape we get.
117+ * @param {* } raw - The raw parsed JSON from the API.
118+ * @param {...string } keys - Object keys to try (in order) if raw is not an array.
119+ * @returns {Array }
120+ * @private
121+ */
122+ _normalizeArray ( raw , ...keys ) {
123+ if ( Array . isArray ( raw ) ) return raw ;
124+ if ( raw && typeof raw === 'object' ) {
125+ for ( const key of keys ) {
126+ if ( Array . isArray ( raw [ key ] ) ) return raw [ key ] ;
127+ }
128+ // Last resort: try the generic `data` wrapper.
129+ if ( Array . isArray ( raw . data ) ) return raw . data ;
130+ }
131+ return [ ] ;
132+ }
133+
112134 /** @override */
113135 async _prepareContext ( options = { } ) {
114136 if ( ! this . _syncManager || ! this . api ) {
@@ -117,6 +139,7 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
117139
118140 this . _loading = true ;
119141 const exclusions = this . _getExclusions ( ) ;
142+ const loadErrors = [ ] ;
120143
121144 // Build entity tab data.
122145 let entityGroups = [ ] ;
@@ -126,6 +149,7 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
126149 foundryOnlyJournals = this . _getFoundryOnlyJournals ( ) ;
127150 } catch ( err ) {
128151 console . error ( 'Chronicle Dashboard: Failed to load entities' , err ) ;
152+ loadErrors . push ( { tab : 'entities' , message : err . message || 'Failed to load entities' } ) ;
129153 }
130154
131155 // Build map tab data.
@@ -134,6 +158,7 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
134158 mapData = await this . _buildMapData ( ) ;
135159 } catch ( err ) {
136160 console . error ( 'Chronicle Dashboard: Failed to load maps' , err ) ;
161+ loadErrors . push ( { tab : 'maps' , message : err . message || 'Failed to load maps' } ) ;
137162 }
138163
139164 // Build shops tab data.
@@ -142,6 +167,7 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
142167 shopData = await this . _buildShopData ( ) ;
143168 } catch ( err ) {
144169 console . error ( 'Chronicle Dashboard: Failed to load shops' , err ) ;
170+ loadErrors . push ( { tab : 'shops' , message : err . message || 'Failed to load shops' } ) ;
145171 }
146172
147173 // Build calendar tab data.
@@ -150,6 +176,7 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
150176 calendarData = await this . _buildCalendarData ( ) ;
151177 } catch ( err ) {
152178 console . error ( 'Chronicle Dashboard: Failed to load calendar' , err ) ;
179+ loadErrors . push ( { tab : 'calendar' , message : err . message || 'Failed to load calendar' } ) ;
153180 }
154181
155182 // Build status tab data.
@@ -168,6 +195,8 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
168195 loading : false ,
169196 searchFilter : this . _searchFilter ,
170197 activeTab : this . _activeTab ,
198+ loadErrors,
199+ hasLoadErrors : loadErrors . length > 0 ,
171200
172201 // Config tab.
173202 config : configData ,
@@ -208,9 +237,10 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
208237 async _buildEntityGroups ( exclusions ) {
209238 // Fetch entity types from Chronicle.
210239 if ( ! this . _cache . entityTypes ) {
211- this . _cache . entityTypes = await this . api . get ( '/entity-types' ) ;
240+ const raw = await this . api . get ( '/entity-types' ) ;
241+ this . _cache . entityTypes = this . _normalizeArray ( raw , 'entity_types' ) ;
212242 }
213- const types = this . _cache . entityTypes || [ ] ;
243+ const types = this . _cache . entityTypes ;
214244
215245 // Fetch all entities (paginated, up to 500 for now).
216246 if ( ! this . _cache . entities ) {
@@ -219,8 +249,8 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
219249 let hasMore = true ;
220250 while ( hasMore && page <= 5 ) {
221251 const result = await this . api . get ( `/entities?per_page=100&page=${ page } ` ) ;
222- const entities = result ?. entities || result || [ ] ;
223- if ( Array . isArray ( entities ) && entities . length > 0 ) {
252+ const entities = this . _normalizeArray ( result , 'entities' ) ;
253+ if ( entities . length > 0 ) {
224254 allEntities . push ( ...entities ) ;
225255 hasMore = entities . length === 100 ;
226256 page ++ ;
@@ -345,9 +375,10 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
345375 async _buildMapData ( ) {
346376 // Fetch Chronicle maps.
347377 if ( ! this . _cache . maps ) {
348- this . _cache . maps = await this . api . get ( '/maps' ) . catch ( ( ) => [ ] ) ;
378+ const raw = await this . api . get ( '/maps' ) . catch ( ( ) => [ ] ) ;
379+ this . _cache . maps = this . _normalizeArray ( raw , 'maps' ) ;
349380 }
350- const chronicles = this . _cache . maps || [ ] ;
381+ const chronicles = this . _cache . maps ;
351382
352383 // Index Foundry scenes by linked mapId.
353384 const scenesByMapId = new Map ( ) ;
@@ -395,9 +426,10 @@ export class SyncDashboard extends HandlebarsApplicationMixin(ApplicationV2) {
395426 async _buildShopData ( ) {
396427 // Ensure entity types are cached.
397428 if ( ! this . _cache . entityTypes ) {
398- this . _cache . entityTypes = await this . api . get ( '/entity-types' ) ;
429+ const raw = await this . api . get ( '/entity-types' ) ;
430+ this . _cache . entityTypes = this . _normalizeArray ( raw , 'entity_types' ) ;
399431 }
400- const types = this . _cache . entityTypes || [ ] ;
432+ const types = this . _cache . entityTypes ;
401433
402434 // Find the shop entity type by slug or name.
403435 const shopType = types . find ( t =>
0 commit comments