Task #96: Show dependency counts in admin app/service/user lists
Goal: make the admin delete dialog show its dependency warning on
the FIRST click (matching the existing Groups UX), instead of
needing a 409 round-trip from the DELETE endpoint to populate the
warning.
Changes:
- lib/api-spec/openapi.yaml: added optional count fields to App
(groupCount, restrictionCount, openCount), Service (orderCount),
and UserProfile (noteCount, orderCount, conversationCount,
messageCount).
- artifacts/api-server/src/routes/apps.ts: GET /admin/apps now
batch-loads groupCount/restrictionCount/openCount via grouped
COUNT queries on group_apps, app_permissions, and app_opens.
- artifacts/api-server/src/routes/services.ts: GET /services now
batch-loads orderCount from service_orders.
- artifacts/api-server/src/routes/users.ts: GET /users now batch-
loads noteCount/orderCount/conversationCount/messageCount from
notes, service_orders, conversations.created_by, and
messages.sender_id.
- artifacts/tx-os/src/pages/admin.tsx: Apps, Services, and Users
delete buttons pre-populate their conflict-state from the row's
count fields so the DeletionWarningDialog shows the warning on
the first click. The lazy 409 fallback still works as a safety
net for any caller without counts.
- replit.md: documented the new admin-panel delete UX.
Tests:
- Added artifacts/api-server/tests/list-dependency-counts.test.mjs
with 6 tests verifying each list endpoint exposes the new count
fields with the expected non-zero values when dependents exist
AND zero values when they do not (apps, services, users).
- Existing artifacts/api-server/tests/delete-force-warnings.test.mjs
(9 tests) still passes — DELETE behavior unchanged.
- Verified end-to-end with a Playwright browser test: clicking the
service delete icon ONCE opens the confirmation modal with the
dependency warning ("orders", count >= 1) immediately.
Notes / drift:
- Count fields were added to the shared App/Service/UserProfile
schemas (not list-only sub-schemas) so the same shape is returned
from list and detail endpoints. Code review flagged this as
acceptable but broader than strictly necessary; left as is to
keep the API consistent.
Out of scope (already pre-existing on main, tracked as follow-ups):
- artifacts/api-server/tests/apps-open.test.mjs has a syntax error.
- artifacts/api-server/tests/app-permissions-unique.test.mjs has
two failing assertions about the composite primary key.
- artifacts/api-server/src/routes/executive-meetings.ts has 3 pre-
existing typecheck errors around the font-settings scope column.
- The `test` workflow exits with ECONNREFUSED when the api-server
isn't already up — needs a readiness check.
Replit-Task-Id: bd14fe73-9961-431b-ab5a-ab70f116e8c7
This commit is contained in:
@@ -151,6 +151,23 @@ export interface UserProfile {
|
||||
roles: string[];
|
||||
groups: GroupSummary[];
|
||||
createdAt: string;
|
||||
/** Dependent record counts. Populated only by the admin list endpoint
|
||||
(GET /users) so the delete dialog can warn before the first click.
|
||||
Omitted from single-user responses (GET /users/:id, PATCH, etc).
|
||||
*/
|
||||
noteCount?: number;
|
||||
/** Number of service orders owned by this user. Populated only by the
|
||||
admin list endpoint (GET /users).
|
||||
*/
|
||||
orderCount?: number;
|
||||
/** Number of conversations created by this user. Populated only by the
|
||||
admin list endpoint (GET /users).
|
||||
*/
|
||||
conversationCount?: number;
|
||||
/** Number of messages authored by this user. Populated only by the
|
||||
admin list endpoint (GET /users).
|
||||
*/
|
||||
messageCount?: number;
|
||||
}
|
||||
|
||||
export interface UpdateUserBody {
|
||||
@@ -314,6 +331,20 @@ export interface App {
|
||||
sortOrder: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
/** Number of groups granting access to this app. Populated only by the
|
||||
admin list endpoint (GET /admin/apps) so the delete dialog can warn
|
||||
before the first click. Omitted from non-admin and single-app
|
||||
responses.
|
||||
*/
|
||||
groupCount?: number;
|
||||
/** Number of legacy permission restrictions for this app. Populated
|
||||
only by the admin list endpoint (GET /admin/apps).
|
||||
*/
|
||||
restrictionCount?: number;
|
||||
/** Number of recorded app-open events for this app. Populated only by
|
||||
the admin list endpoint (GET /admin/apps).
|
||||
*/
|
||||
openCount?: number;
|
||||
}
|
||||
|
||||
export interface CreateAppBody {
|
||||
@@ -364,6 +395,12 @@ export interface Service {
|
||||
sortOrder: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
/** Number of service_orders rows referencing this service. Populated
|
||||
only by the list endpoint (GET /services) so the admin delete
|
||||
dialog can warn before the first click. Omitted from single-service
|
||||
responses.
|
||||
*/
|
||||
orderCount?: number;
|
||||
}
|
||||
|
||||
export interface CreateServiceBody {
|
||||
|
||||
Reference in New Issue
Block a user