rastack 0.0.55 → 0.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/admin/useAdmin.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  FilterKind,
21
21
  FilterOption,
22
22
  PAGE_SIZE,
23
+ adminResources,
23
24
  boolFilterOptions,
24
25
  buildWriteBody,
25
26
  changelistHeaders,
@@ -414,6 +415,96 @@ export function useResourceForm(
414
415
  return { model, loading, loadError, submitError, done };
415
416
  }
416
417
 
418
+ /** Per-model record count, plus the resource it belongs to. */
419
+ export interface ModelStat {
420
+ resource: AdminResource;
421
+ count: number;
422
+ }
423
+
424
+ /** The manifest-derived, engine-counted numbers the Insight dashboard renders. */
425
+ export interface DashboardStats {
426
+ loading: boolean;
427
+ /** Every admin model with its live record count, richest first. */
428
+ models: ModelStat[];
429
+ totalRecords: number;
430
+ modelCount: number;
431
+ appCount: number;
432
+ /** Total foreign-key relations across the schema. */
433
+ relationCount: number;
434
+ /** Record totals grouped by app, richest first. */
435
+ byApp: { app: string; count: number; models: number }[];
436
+ }
437
+
438
+ /**
439
+ * Count every admin model's rows from the engine (one cheap `page_size=1` list
440
+ * per model, read from the DRF `count`) and roll them up into the numbers the
441
+ * Insight dashboard shows. All real, all live — the dashboard only animates how
442
+ * they appear.
443
+ */
444
+ export function useDashboardStats(
445
+ engine: RastackEngine,
446
+ resources: AdminResource[],
447
+ ): DashboardStats {
448
+ const models = useMemo(() => adminResources(resources), [resources]);
449
+ const [counts, setCounts] = useState<Record<string, number> | null>(null);
450
+
451
+ useEffect(() => {
452
+ let cancelled = false;
453
+ setCounts(null);
454
+ (async () => {
455
+ const entries = await Promise.all(
456
+ models.map(async (r): Promise<[string, number]> => {
457
+ try {
458
+ const res: EngineResult = await engine.handle(
459
+ "GET",
460
+ listPath(r.app, r.model),
461
+ { page_size: "1" },
462
+ undefined,
463
+ );
464
+ return [r.key, ok(res) ? readListEnvelope(res.body).count : 0];
465
+ } catch {
466
+ return [r.key, 0];
467
+ }
468
+ }),
469
+ );
470
+ if (!cancelled) setCounts(Object.fromEntries(entries));
471
+ })();
472
+ return () => {
473
+ cancelled = true;
474
+ };
475
+ }, [engine, models]);
476
+
477
+ return useMemo((): DashboardStats => {
478
+ const loading = counts === null;
479
+ const withCounts: ModelStat[] = models
480
+ .map((resource) => ({ resource, count: counts?.[resource.key] ?? 0 }))
481
+ .sort((a, b) => b.count - a.count || a.resource.label.localeCompare(b.resource.label));
482
+
483
+ const appMap = new Map<string, { count: number; models: number }>();
484
+ for (const m of withCounts) {
485
+ const cur = appMap.get(m.resource.app) ?? { count: 0, models: 0 };
486
+ cur.count += m.count;
487
+ cur.models += 1;
488
+ appMap.set(m.resource.app, cur);
489
+ }
490
+
491
+ return {
492
+ loading,
493
+ models: withCounts,
494
+ totalRecords: withCounts.reduce((s, m) => s + m.count, 0),
495
+ modelCount: models.length,
496
+ appCount: appMap.size,
497
+ relationCount: models.reduce(
498
+ (s, r) => s + r.fields.filter((f) => f.type === "fk").length,
499
+ 0,
500
+ ),
501
+ byApp: [...appMap.entries()]
502
+ .map(([app, v]) => ({ app, count: v.count, models: v.models }))
503
+ .sort((a, b) => b.count - a.count || a.app.localeCompare(b.app)),
504
+ };
505
+ }, [counts, models]);
506
+ }
507
+
417
508
  /** Delete a record via the engine; resolves true on success. */
418
509
  export function useDeleteRecord(
419
510
  engine: RastackEngine,