What We’re Testing
The bottom half of DashboardPage.tsx renders two side-by-side cards: “Active Machines” and “Team”. Both are populated from the useDashboardData hook’s response.
Active Machines card:
- Source:
recentMachines— derived asonlineMachines.slice(0, 4)whereonlineMachines= machines withstatus === 'online' - The machines list is pre-sorted by
last_seen DESCfrom the SQL query:SELECT id, name, tailnet_ip, status, last_seen FROM machines WHERE org_id = ? ORDER BY last_seen DESC - Each row shows: machine name, tailnet hostname (
name.slug.zt.net), and last_seen formatted viatoLocaleTimeString() - Empty state: “No machines online”
Team card:
- Source:
recentMembers— derived asmembers.slice(0, 4)from the org_members JOIN profiles query - Members are ordered by
joined_at ASCfrom the SQL - Each row shows: avatar initials (derived from
full_name.split(" ").map(n => n[0]).join("").toUpperCase()), full name, and role - Empty state: “No members yet”
The hostname shown for each machine is m.name + "." + currentOrg.slug + ".zt.net" — this is computed client-side in DashboardPage.tsx. It does not query the DNS records table.
The last_seen value is formatted with new Date(m.last_seen).toLocaleTimeString() — this uses the browser’s locale, so the format is locale-dependent (e.g., 10:30:45 AM on en-US).
Your Test Setup
| Machine | Role |
|---|---|
| ⊞ Win-A | Browser for dashboard inspection + API verification |
ST1 — Active Machines List Renders Correctly
What it verifies: The Active Machines card shows up to 4 online machines sorted by most-recent last_seen, with correct name and hostname.
Steps:
- On ⊞ Win-A , set credentials:
TOKEN="YOUR_ADMIN_JWT_TOKEN"
ORG_ID="YOUR_ORG_ID"
- Query online machines sorted by
last_seen DESC:
curl -s "https://login.quickztna.com/api/db/machines?org_id=eq.$ORG_ID&status=eq.online&select=id,name,tailnet_ip,last_seen&order=last_seen.desc" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
-
Note the first 4 machines in the response — these should be the machines shown in the Active Machines card.
-
Open the dashboard at
https://login.quickztna.com/dashboardon ⊞ Win-A . -
Scroll to the Active Machines card. Verify:
- The machine names match the top 4 results from step 2.
- The hostname under each name reads
machinename.orgslug.zt.net.
Expected card content (example, 2 online machines):
Active Machines
● Win-A Win-A.myorg.zt.net 10:30:45 AM
● Linux-C Linux-C.myorg.zt.net 10:28:12 AM
Pass: Up to 4 online machines are shown in last_seen DESC order. Each hostname is name.slug.zt.net where slug is the org slug shown in the page header.
Fail / Common issues:
- Card shows “No machines online” despite machines running — the
statusfield may not beonline(e.g.,pending). Check the machines page for actual status. - Hostname slug is wrong — the slug is derived from
currentOrg.slugin the React context. Verify the org slug viaGET /api/db/organizations?id=eq.ORG_ID.
ST2 — Last Seen Timestamp Formatting
What it verifies: The last_seen timestamp in the Active Machines card is formatted using toLocaleTimeString() and reflects the machine’s most recent heartbeat time.
Steps:
-
On ⊞ Win-A , note the current time in your local timezone (HH:MM:SS).
-
Ensure at least one machine is online. Get the machine’s
last_seenfrom the API:
curl -s "https://login.quickztna.com/api/db/machines?org_id=eq.$ORG_ID&status=eq.online&select=name,last_seen&order=last_seen.desc&limit=1" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Note the ISO timestamp, e.g., "2026-03-17T10:30:45.123Z".
-
Convert that UTC timestamp to local time (UTC+5:30 example:
10:30:45Z=16:00:45 IST). -
On the dashboard Active Machines card, read the timestamp next to the machine’s name.
Expected: The time displayed matches the last_seen value converted to local browser time using toLocaleTimeString(). For en-US locale: 4:00:45 PM. For en-GB: 16:00:45.
Pass: The displayed time is within 1 minute of the last_seen value from the API (accounting for heartbeat interval). It is in local time format, not UTC.
Fail / Common issues:
- Timestamp shows a time far in the past — the machine’s
ztna updaemon may not have sent a recent heartbeat. Checkztna statuson the machine. - Timestamp shows
——last_seenisnullin the database. This can happen on machines registered withauth-keythat have never sent a heartbeat.
ST3 — Team Card Member Display and Initials
What it verifies: The Team card correctly renders member initials, full names, and roles — with initials derived from the first letter of each word in full_name.
Steps:
- On ⊞ Win-A , query org members with their profile names:
curl -s -X POST https://login.quickztna.com/api/db/_rpc \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"fn\":\"get_org_members\",\"_org_id\":\"$ORG_ID\"}" | python3 -m json.tool
-
For each member in the response, manually compute the expected initials:
- Full name
"Vikas Swaminh"→ initials"VS" - Full name
"Jane Doe"→ initials"JD" - Full name
"Unknown"(whenfull_nameis null) → initials"U" - Single word
"Alice"→ initials"A"
- Full name
-
On the dashboard Team card, verify each member row shows:
- A circular avatar with computed initials
- The full name
- The role (e.g.,
owner,admin,member) — displayed capitalized via CSScapitalize
Expected card content:
Team
[VS] Vikas Swaminh owner
[JD] Jane Doe member
Pass: Initials are uppercase, correctly extracted from the full_name field. Role is displayed. For members with full_name = null, the code uses "Unknown" and renders initials "U".
Fail / Common issues:
- Initials show
"?"— the code falls back to"?"whenfull_nameis empty string or missing individual split parts. Verify theprofilestable has afull_namevalue for the user. - “No members yet” appears despite members existing — the
recentMemberslist comes from the RPC; check thatorg_membershas rows for this org.
ST4 — Empty State Rendering
What it verifies: When no machines are online or no members exist, the dashboard renders the correct empty state messages instead of broken UI.
Steps:
- On ⊞ Win-A , temporarily verify an empty state. If you cannot take all machines offline, query for the count first:
curl -s "https://login.quickztna.com/api/db/machines?org_id=eq.$ORG_ID&status=eq.online&select=id" \
-H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json; d=json.load(sys.stdin); print('online:', len(d.get('data',d.get('results',[]))))"
- If zero online machines exist, open the dashboard. The Active Machines card should display:
No machines online
-
For the Team card empty state, this would require an org with no members, which is not a realistic test scenario. Confirm the Team card shows members correctly when the org has members.
-
Verify the loading skeleton state: Open DevTools Network tab, throttle to “Slow 3G”, then navigate to the dashboard. During load, you should see skeleton placeholders (gray animated rectangles) in place of the metric cards and the Active Machines / Team lists.
Expected skeleton count: 4 skeleton cards in the 2-column grid during load, then 2 skeleton rows (one for Active Machines, one for Team) when data is arriving.
Pass: Empty state messages render cleanly (no JavaScript errors in the console). Skeleton loading state appears during data fetch. Cards switch to content once useDashboardData resolves.
Fail / Common issues:
- Page crashes to blank — a JavaScript error in the member initials code. Open DevTools console for the error detail.
- Skeletons never disappear — the
useDashboardDataquery is stuck. Check the Network tab for the_rpcrequest; it may be returning an error.
ST5 — “Active Machines” Slice Limit (4 max)
What it verifies: Even when more than 4 machines are online, the Active Machines card displays at most 4, since recentMachines is onlineMachines.slice(0, 4).
Steps:
- On ⊞ Win-A , count online machines:
curl -s "https://login.quickztna.com/api/db/machines?org_id=eq.$ORG_ID&status=eq.online&select=id,name,last_seen&order=last_seen.desc" \
-H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json; d=json.load(sys.stdin); ms=d.get('data',d.get('results',[])); print(f'{len(ms)} online'); [print(m[\"name\"]) for m in ms[:6]]"
-
If 4 or more machines are online, open the dashboard and count the rows in the Active Machines card.
-
Verify that no more than 4 machines are listed, and the 4 shown are the ones with the most recent
last_seentimestamps from step 1. -
Note: The “Network Status” card still shows the full
onlineCount(e.g., “5/6 Online”) — only the Active Machines list is capped at 4. This is intentional.
Pass: Active Machines card shows exactly min(onlineCount, 4) rows. The rows correspond to the top 4 by last_seen DESC order.
Fail / Common issues:
- More than 4 rows shown — this would be a regression in
DashboardPage.tsx. Check therecentMachinesderivation inuse-dashboard-data.ts(line:onlineMachines.slice(0, 4)). - Fewer than 4 rows despite more being online — the
recentMachinesderivation filters onstatus === 'online'client-side after the RPC returns all machines. Verify thestatusvalues in the API response are actually'online'.
Summary
| Sub-test | What it proves | Pass condition |
|---|---|---|
| ST1 | Active Machines list | Shows up to 4 online machines in last_seen DESC order with correct hostnames |
| ST2 | Last seen timestamp | Time is formatted with toLocaleTimeString() and matches the machine’s last_seen value |
| ST3 | Team card initials | Member initials are uppercase first-letters of full_name words; role is displayed |
| ST4 | Empty states | ”No machines online” and loading skeletons render without errors |
| ST5 | 4-machine slice limit | Active Machines card never shows more than 4 rows even when more are online |