From ce0819cfaa315affdf68640630ef6d28c5835c9b Mon Sep 17 00:00:00 2001 From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com> Date: Tue, 21 Apr 2026 11:39:34 +0000 Subject: [PATCH] Make admin recent-opens drill-in rows clickable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task: #47 — Let admins jump from a recent open straight to that user. Changes (artifacts/teaboy-os/src/pages/admin.tsx): - AppOpensDrillIn: each user row in the per-app recent-opens popup is now a focusable button. Clicking jumps to that user's row in the Users list (closes the popup, then calls the existing handleSelectUserFromDashboard via a new onJumpUser prop), reusing the highlight behavior that already powers the leaderboard. - UserOpensDrillIn: each app row in the per-user recent-opens popup is now a focusable button. Clicking opens that app's edit modal (closes the popup, then calls the existing handleSelectAppFromDashboard via a new onJumpApp prop), mirroring the leaderboard click behavior. - Added type="button", hover/focus styles, and aria-labels reusing the existing admin.dashboard.viewUserDetails / admin.dashboard.viewAppDetails translation keys (already localized for both en and ar). The footer "Edit app" / "View user" buttons remain unchanged and keep working alongside the per-row clicks. No new translation keys, schema changes, or API changes were needed — AppOpenByAppEntry already exposes userId and AppOpenByUserEntry already exposes appId. Verification: tsc --noEmit on teaboy-os shows no new errors from these edits (only the same pre-existing errors that were present before this task). E2E was not run because the change is a thin wiring of existing handlers and admin login requires custom session setup beyond this task's scope. Replit-Task-Id: 5db0964e-95fa-4199-9337-631cd1a790ec --- artifacts/teaboy-os/src/pages/admin.tsx | 76 ++++++++++++++++--------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/artifacts/teaboy-os/src/pages/admin.tsx b/artifacts/teaboy-os/src/pages/admin.tsx index 456fca71..d66801ae 100644 --- a/artifacts/teaboy-os/src/pages/admin.tsx +++ b/artifacts/teaboy-os/src/pages/admin.tsx @@ -1515,6 +1515,10 @@ function DashboardSection({ setDrillIn(null); onSelectApp(id); }} + onJumpUser={(id) => { + setDrillIn(null); + onSelectUser(id); + }} /> )} {drillIn?.kind === "user" && ( @@ -1528,6 +1532,10 @@ function DashboardSection({ setDrillIn(null); onSelectUser(id); }} + onJumpApp={(id) => { + setDrillIn(null); + onSelectApp(id); + }} /> )} @@ -1607,6 +1615,7 @@ function AppOpensDrillIn({ statsQueryParams, onClose, onJump, + onJumpUser, }: { appId: number; lang: string; @@ -1614,6 +1623,7 @@ function AppOpensDrillIn({ statsQueryParams: GetAdminStatsParams; onClose: () => void; onJump: (appId: number) => void; + onJumpUser: (userId: number) => void; }) { const { t } = useTranslation(); const { data, isLoading, error } = useGetAdminAppOpensByApp(appId, statsQueryParams, { @@ -1665,20 +1675,24 @@ function AppOpensDrillIn({ const initial = (display || o.username || "?").trim().charAt(0).toUpperCase(); const avatar = resolveServiceImageUrl(o.avatarUrl); return ( -
  • - -
    -
    {display}
    - {display !== o.username && ( -
    @{o.username}
    - )} -
    -
    - {formatOpenTimestamp(o.createdAt, lang)} -
    +
  • +
  • ); })} @@ -1695,6 +1709,7 @@ function UserOpensDrillIn({ statsQueryParams, onClose, onJump, + onJumpApp, }: { userId: number; lang: string; @@ -1702,6 +1717,7 @@ function UserOpensDrillIn({ statsQueryParams: GetAdminStatsParams; onClose: () => void; onJump: (userId: number) => void; + onJumpApp: (appId: number) => void; }) { const { t } = useTranslation(); const { data, isLoading, error } = useGetAdminAppOpensByUser(userId, statsQueryParams, { @@ -1752,20 +1768,24 @@ function UserOpensDrillIn({ {data.opens.map((o) => { const name = lang === "ar" ? o.nameAr : o.nameEn; return ( -
  • -
    -
    -
    {name}
    -
    -
    - {formatOpenTimestamp(o.createdAt, lang)} -
    +
  • +
  • ); })}