11import { createProjectJourneyMockRepository } from "../toolbox/project-journey/project-journey-mock-repository.js" ;
2+ import { createAssetToolMockRepository } from "../toolbox/assets/assets-mock-repository.js" ;
3+ import { createProjectWorkspacePaletteRepository } from "../toolbox/colors/palette-workspace-repository.js" ;
4+ import {
5+ getAllPersistedMockDbTables ,
6+ getStandaloneMockDbTables ,
7+ } from "../src/shared/mock-db/mock-db-store.js" ;
28
39const AUDIT_FIELDS = [ "createdAt" , "updatedAt" , "createdByType" , "updatedByType" ] ;
10+ const TOOL_GROUP_ORDER = [ "project-journey" , "palette" , "asset" ] ;
11+ const TOOL_GROUP_LABELS = Object . freeze ( {
12+ asset : "Asset" ,
13+ palette : "Palette" ,
14+ "project-journey" : "Project Journey" ,
15+ } ) ;
16+ const STANDALONE_TABLE_LABELS = Object . freeze ( {
17+ users : "Users" ,
18+ actors : "Actors" ,
19+ } ) ;
20+ const AUDIT_REQUIRED_TABLES = new Set ( [ "users" , "actors" ] ) ;
421
522class AdminDbViewer {
623 constructor ( documentRef = document ) {
724 this . document = documentRef ;
8- this . repository = createProjectJourneyMockRepository ( ) ;
25+ this . projectJourneyRepository = createProjectJourneyMockRepository ( ) ;
26+ this . paletteRepository = createProjectWorkspacePaletteRepository ( ) ;
27+ this . assetRepository = createAssetToolMockRepository ( ) ;
28+ this . activeFilter = "all" ;
29+ this . filterRoot = documentRef . querySelector ( "[data-admin-db-filters]" ) ;
930 this . status = documentRef . querySelector ( "[data-admin-db-status]" ) ;
1031 this . diagnostics = documentRef . querySelector ( "[data-admin-db-diagnostics]" ) ;
1132 this . relationships = documentRef . querySelector ( "[data-admin-db-relationships]" ) ;
1233 this . tablesRoot = documentRef . querySelector ( "[data-admin-db-tables]" ) ;
1334 }
1435
1536 start ( ) {
16- this . repository . openProject ( "demo-project" ) ;
17- const tables = this . repository . getTables ( ) ;
18- this . renderDiagnostics ( tables ) ;
19- this . renderRelationships ( tables ) ;
20- this . renderTables ( tables ) ;
21- if ( this . status ) {
22- this . status . textContent = "Read-only mock DB dump loaded for Demo Project." ;
23- }
37+ this . projectJourneyRepository . openProject ( "demo-project" ) ;
38+ this . render ( ) ;
39+ this . filterRoot ?. addEventListener ( "click" , ( event ) => {
40+ const button = event . target . closest ( "[data-admin-db-filter]" ) ;
41+ if ( ! button ) {
42+ return ;
43+ }
44+ this . activeFilter = button . dataset . adminDbFilter || "all" ;
45+ this . render ( ) ;
46+ } ) ;
2447 }
2548
2649 createElement ( tagName , options = { } ) {
@@ -72,6 +95,55 @@ class AdminDbViewer {
7295 return fields ;
7396 }
7497
98+ collectSnapshot ( ) {
99+ const projectJourneyTables = this . projectJourneyRepository . getTables ( ) ;
100+ const paletteTables = this . paletteRepository . getTables ( ) ;
101+ const assetTables = this . assetRepository . getTables ( ) ;
102+ const standaloneTables = getStandaloneMockDbTables ( ) ;
103+ const persistedTables = getAllPersistedMockDbTables ( ) ;
104+ const tables = {
105+ ...persistedTables ,
106+ ...projectJourneyTables ,
107+ ...paletteTables ,
108+ ...assetTables ,
109+ ...standaloneTables ,
110+ } ;
111+ const groups = [
112+ {
113+ id : "all" ,
114+ label : "All" ,
115+ tableNames : Object . keys ( tables ) . sort ( ) ,
116+ type : "all" ,
117+ } ,
118+ ...TOOL_GROUP_ORDER . map ( ( id ) => ( {
119+ id,
120+ label : TOOL_GROUP_LABELS [ id ] ,
121+ tableNames : Object . keys (
122+ id === "project-journey"
123+ ? projectJourneyTables
124+ : id === "palette"
125+ ? paletteTables
126+ : assetTables ,
127+ ) . sort ( ) ,
128+ type : "tool" ,
129+ } ) ) ,
130+ ...Object . entries ( STANDALONE_TABLE_LABELS ) . map ( ( [ tableName , label ] ) => ( {
131+ id : tableName ,
132+ label,
133+ tableNames : [ tableName ] ,
134+ type : "table" ,
135+ } ) ) ,
136+ ] ;
137+ return {
138+ groups,
139+ tables,
140+ } ;
141+ }
142+
143+ activeGroup ( groups ) {
144+ return groups . find ( ( group ) => group . id === this . activeFilter ) || groups [ 0 ] ;
145+ }
146+
75147 renderTable ( tableName , records ) {
76148 const details = this . createElement ( "details" , {
77149 className : "vertical-accordion" ,
@@ -134,9 +206,29 @@ class AdminDbViewer {
134206 } ) ;
135207 }
136208
209+ renderFilters ( groups ) {
210+ if ( ! this . filterRoot ) {
211+ return ;
212+ }
213+ this . filterRoot . replaceChildren ( ) ;
214+ groups . forEach ( ( group ) => {
215+ const button = this . createElement ( "button" , {
216+ className : group . id === this . activeFilter ? "btn btn--compact primary" : "btn btn--compact" ,
217+ text : group . label ,
218+ } ) ;
219+ button . type = "button" ;
220+ button . dataset . adminDbFilter = group . id ;
221+ button . setAttribute ( "aria-pressed" , String ( group . id === this . activeFilter ) ) ;
222+ this . filterRoot . append ( button ) ;
223+ } ) ;
224+ }
225+
137226 auditFindings ( tables ) {
138227 const findings = [ ] ;
139228 Object . entries ( tables ) . forEach ( ( [ tableName , records ] ) => {
229+ if ( ! tableName . startsWith ( "project_journey_" ) && ! AUDIT_REQUIRED_TABLES . has ( tableName ) ) {
230+ return ;
231+ }
140232 records . forEach ( ( record ) => {
141233 AUDIT_FIELDS . forEach ( ( field ) => {
142234 if ( ! Object . hasOwn ( record , field ) ) {
@@ -149,49 +241,95 @@ class AdminDbViewer {
149241 }
150242
151243 relationshipsForTables ( tables ) {
152- const noteTypeKeys = new Set ( tables . project_journey_note_types . map ( ( type ) => type . key ) ) ;
153- const noteKeys = new Set ( tables . project_journey_notes . map ( ( note ) => note . key ) ) ;
244+ const noteTypeKeys = new Set ( ( tables . project_journey_note_types || [ ] ) . map ( ( type ) => type . key ) ) ;
245+ const noteKeys = new Set ( ( tables . project_journey_notes || [ ] ) . map ( ( note ) => note . key ) ) ;
246+ const userKeys = new Set ( ( tables . users || [ ] ) . map ( ( user ) => user . key ) ) ;
247+ const actorUserKeys = new Set ( ( tables . actors || [ ] ) . map ( ( actor ) => actor . userKey ) ) ;
154248 const activeTemplateKeys = new Set (
155- tables . project_journey_templates
249+ ( tables . project_journey_templates || [ ] )
156250 . filter ( ( template ) => template . isActive )
157251 . map ( ( template ) => template . key ) ,
158252 ) ;
253+ const paletteGlobals = new Set ( ( tables . project_workspace_palette_globals || [ ] ) . map ( ( row ) => row . projectId ) ) ;
254+ const paletteColorKeys = new Set ( ( tables . palette_colors || [ ] ) . map ( ( row ) => `${ row . projectId } :${ row . symbol } ` ) ) ;
255+ const assetIds = new Set ( ( tables . asset_library_items || [ ] ) . map ( ( asset ) => asset . id ) ) ;
256+ const assetStorageIds = new Set ( ( tables . asset_storage_objects || [ ] ) . map ( ( object ) => object . id ) ) ;
159257 return [
160258 {
161259 name : "project_journey_notes.typeKey -> project_journey_note_types.key" ,
162- checked : tables . project_journey_notes . length ,
163- missing : tables . project_journey_notes . filter ( ( note ) => ! noteTypeKeys . has ( note . typeKey ) ) ,
260+ checked : ( tables . project_journey_notes || [ ] ) . length ,
261+ missing : ( tables . project_journey_notes || [ ] ) . filter ( ( note ) => ! noteTypeKeys . has ( note . typeKey ) ) ,
262+ } ,
263+ {
264+ name : "project_journey_notes.ownerKey -> users.key" ,
265+ checked : ( tables . project_journey_notes || [ ] ) . length ,
266+ missing : ( tables . project_journey_notes || [ ] ) . filter ( ( note ) => ! userKeys . has ( note . ownerKey ) ) ,
164267 } ,
165268 {
166269 name : "project_journey_items.noteKey -> project_journey_notes.key" ,
167- checked : tables . project_journey_items . length ,
168- missing : tables . project_journey_items . filter ( ( item ) => ! noteKeys . has ( item . noteKey ) ) ,
270+ checked : ( tables . project_journey_items || [ ] ) . length ,
271+ missing : ( tables . project_journey_items || [ ] ) . filter ( ( item ) => ! noteKeys . has ( item . noteKey ) ) ,
169272 } ,
170273 {
171274 name : "system project_journey_items.templateKey -> active project_journey_templates.key" ,
172- checked : tables . project_journey_items . filter ( ( item ) => item . createdByType === "system" ) . length ,
173- missing : tables . project_journey_items . filter (
275+ checked : ( tables . project_journey_items || [ ] ) . filter ( ( item ) => item . createdByType === "system" ) . length ,
276+ missing : ( tables . project_journey_items || [ ] ) . filter (
174277 ( item ) => item . createdByType === "system" && ! activeTemplateKeys . has ( item . templateKey ) ,
175278 ) ,
176279 } ,
177280 {
178281 name : "project_journey_activity.noteKey -> project_journey_notes.key" ,
179- checked : tables . project_journey_activity . length ,
180- missing : tables . project_journey_activity . filter ( ( activity ) => ! noteKeys . has ( activity . noteKey ) ) ,
282+ checked : ( tables . project_journey_activity || [ ] ) . length ,
283+ missing : ( tables . project_journey_activity || [ ] ) . filter ( ( activity ) => ! noteKeys . has ( activity . noteKey ) ) ,
284+ } ,
285+ {
286+ name : "actors.userKey -> users.key" ,
287+ checked : ( tables . actors || [ ] ) . length ,
288+ missing : ( tables . actors || [ ] ) . filter ( ( actor ) => ! userKeys . has ( actor . userKey ) ) ,
289+ } ,
290+ {
291+ name : "users.key -> actors.userKey" ,
292+ checked : ( tables . users || [ ] ) . length ,
293+ missing : ( tables . users || [ ] ) . filter ( ( user ) => ! actorUserKeys . has ( user . key ) ) ,
294+ } ,
295+ {
296+ name : "palette_colors.projectId -> project_workspace_palette_globals.projectId" ,
297+ checked : ( tables . palette_colors || [ ] ) . length ,
298+ missing : ( tables . palette_colors || [ ] ) . filter ( ( row ) => ! paletteGlobals . has ( row . projectId ) ) ,
299+ } ,
300+ {
301+ name : "palette_swatch_usages.swatchSymbol -> palette_colors.symbol" ,
302+ checked : ( tables . palette_swatch_usages || [ ] ) . length ,
303+ missing : ( tables . palette_swatch_usages || [ ] ) . filter ( ( row ) => ! paletteColorKeys . has ( `${ row . projectId } :${ row . swatchSymbol } ` ) ) ,
304+ } ,
305+ {
306+ name : "palette_swatch_usages.assetId -> asset_library_items.id" ,
307+ checked : ( tables . palette_swatch_usages || [ ] ) . length ,
308+ missing : ( tables . palette_swatch_usages || [ ] ) . filter ( ( row ) => ! assetIds . has ( row . assetId ) ) ,
309+ } ,
310+ {
311+ name : "asset_library_items.storageObjectId -> asset_storage_objects.id" ,
312+ checked : ( tables . asset_library_items || [ ] ) . length ,
313+ missing : ( tables . asset_library_items || [ ] ) . filter ( ( asset ) => ! assetStorageIds . has ( asset . storageObjectId ) ) ,
314+ } ,
315+ {
316+ name : "asset_import_events.assetId -> asset_library_items.id" ,
317+ checked : ( tables . asset_import_events || [ ] ) . length ,
318+ missing : ( tables . asset_import_events || [ ] ) . filter ( ( event ) => ! assetIds . has ( event . assetId ) ) ,
181319 } ,
182320 ] ;
183321 }
184322
185323 tableBleedFindings ( tables ) {
186- const notesByKey = new Map ( tables . project_journey_notes . map ( ( note ) => [ note . key , note ] ) ) ;
324+ const notesByKey = new Map ( ( tables . project_journey_notes || [ ] ) . map ( ( note ) => [ note . key , note ] ) ) ;
187325 const findings = [ ] ;
188- tables . project_journey_items . forEach ( ( item ) => {
326+ ( tables . project_journey_items || [ ] ) . forEach ( ( item ) => {
189327 const note = notesByKey . get ( item . noteKey ) ;
190328 if ( note && note . projectKey !== item . projectKey ) {
191329 findings . push ( `${ item . key } projectKey ${ item . projectKey } does not match note ${ note . key } projectKey ${ note . projectKey } .` ) ;
192330 }
193331 } ) ;
194- tables . project_journey_activity . forEach ( ( activity ) => {
332+ ( tables . project_journey_activity || [ ] ) . forEach ( ( activity ) => {
195333 const note = notesByKey . get ( activity . noteKey ) ;
196334 if ( note && note . projectKey !== activity . projectKey ) {
197335 findings . push ( `${ activity . key } projectKey ${ activity . projectKey } does not match note ${ note . key } projectKey ${ note . projectKey } .` ) ;
@@ -200,6 +338,15 @@ class AdminDbViewer {
200338 return findings ;
201339 }
202340
341+ staleDisplayFindings ( tables , groups ) {
342+ const groupTableNames = new Set ( groups . flatMap ( ( group ) => group . tableNames ) ) ;
343+ const unownedTables = Object . keys ( tables ) . filter ( ( tableName ) => ! groupTableNames . has ( tableName ) ) ;
344+ if ( unownedTables . length ) {
345+ return unownedTables . map ( ( tableName ) => `${ tableName } has live data but no Mock DB filter owner.` ) ;
346+ }
347+ return [ "No stale display data detected; tables are rendered from current mock DB snapshots." ] ;
348+ }
349+
203350 renderList ( messages , dataName ) {
204351 const list = this . createElement ( "ul" ) ;
205352 list . dataset [ dataName ] = "" ;
@@ -209,17 +356,19 @@ class AdminDbViewer {
209356 return list ;
210357 }
211358
212- renderDiagnostics ( tables ) {
359+ renderDiagnostics ( tables , groups ) {
213360 this . diagnostics . replaceChildren ( ) ;
214361 const auditFindings = this . auditFindings ( tables ) ;
215362 const bleedFindings = this . tableBleedFindings ( tables ) ;
363+ const staleFindings = this . staleDisplayFindings ( tables , groups ) ;
216364 const auditSummary = auditFindings . length
217365 ? auditFindings
218- : [ "All Project Journey mock DB tables include createdAt, updatedAt, createdByType, and updatedByType." ] ;
366+ : [ "All current mock DB tables include createdAt, updatedAt, createdByType, and updatedByType where required by the owning mock model ." ] ;
219367 const bleedSummary = bleedFindings . length ? bleedFindings : [ "No table bleed detected." ] ;
220368 this . diagnostics . append (
221369 this . renderList ( auditSummary , "adminDbAuditFindings" ) ,
222370 this . renderList ( bleedSummary , "adminDbBleedFindings" ) ,
371+ this . renderList ( staleFindings , "adminDbStaleDisplayFindings" ) ,
223372 ) ;
224373 }
225374
@@ -244,6 +393,24 @@ class AdminDbViewer {
244393 this . renderList ( missingLinks . length ? missingLinks : [ "No missing links detected." ] , "adminDbMissingLinks" ) ,
245394 ) ;
246395 }
396+
397+ render ( ) {
398+ const snapshot = this . collectSnapshot ( ) ;
399+ const group = this . activeGroup ( snapshot . groups ) ;
400+ const visibleTables = Object . fromEntries (
401+ group . tableNames
402+ . filter ( ( tableName ) => Object . hasOwn ( snapshot . tables , tableName ) )
403+ . map ( ( tableName ) => [ tableName , snapshot . tables [ tableName ] ] ) ,
404+ ) ;
405+ this . renderFilters ( snapshot . groups ) ;
406+ this . renderDiagnostics ( snapshot . tables , snapshot . groups ) ;
407+ this . renderRelationships ( snapshot . tables ) ;
408+ this . renderTables ( visibleTables ) ;
409+ if ( this . status ) {
410+ const recordCount = Object . values ( visibleTables ) . reduce ( ( total , rows ) => total + rows . length , 0 ) ;
411+ this . status . textContent = `Mock DB loaded ${ Object . keys ( visibleTables ) . length } table${ Object . keys ( visibleTables ) . length === 1 ? "" : "s" } and ${ recordCount } record${ recordCount === 1 ? "" : "s" } for ${ group . label } .` ;
412+ }
413+ }
247414}
248415
249416new AdminDbViewer ( ) . start ( ) ;
0 commit comments