Prompt file imported from altersquare/altersquare-manager (
.github/prompts/plan-moveSyncToNavbar.prompt.md). Copyright stays with the author.
Plan: Move Sync Button & Last Synced to Navbar
Status: IMPLEMENTED — Completed February 11, 2026.
The sync button and timestamp move from src/app/page.tsx into src/components/Navbar.tsx via a shared React Context. A tooltip on the button explains what syncing does.
Steps
-
Create
src/components/SyncContext.tsx— new fileSyncProvidercomponent +useSync()hook- State:
cachedAt: string | null,refreshing: boolean handleRefresh()callsPOST /api/team/refresh, updatescachedAt, shows toastregisterSyncCallback/unregisterSyncCallbackso pages can react to a sync (e.g., Team page re-renders its member list, Repos page could refresh its repo list)- On mount, fetch initial
cachedAtfrom a lightweight endpoint (see step 2)
-
Create
GET /api/team/statusroute — new filesrc/app/api/team/status/route.ts- Reads cache via
readCache(), returns{ cachedAt }(ornullif no cache) - Avoids loading full team/repo data just for the timestamp on every page load
- Reads cache via
-
Wrap app in
SyncProvider— in src/app/layout.tsx- Place inside
<ToastProvider>and outside<Navbar />
- Place inside
-
Update src/components/Navbar.tsx — add sync button + timestamp + tooltip
- Import
useSync(), render refresh button and compactcachedAtdisplay to the left of user info - Tooltip: a hover tooltip on the button with text like:
"Sync team & repo data from GitHub. This app caches organization members, their repo access, and repository metadata. Clicking refresh fetches the latest data from GitHub and updates the cache."
- Implementation: use a
group relativewrapper with an absolutely-positioned tooltip div that appears on hover (invisible group-hover:visible). Pure CSS, no library needed — matches the existing Tailwind-only approach. - Show spinning icon when
refreshing, disable button, "Syncing..." text - Timestamp as compact
text-xs text-gray-500absolute format: "Synced: 2/11/2026, 3:45 PM" — hidden on small screens - Visible to all authenticated users (not admin-only)
- Import
-
Update src/app/page.tsx — remove sync UI, consume context
- Remove
refreshingstate,handleRefreshcallback, refresh button JSX, and "Last Synced" stat card - Use
useSync()to register a callback that callssetData(json)when sync completes from the Navbar - Stats grid goes from 3 → 2 columns
- Remove
-
Update src/app/repos/page.tsx — register a sync callback
- After a sync from the Navbar, auto re-fetch repo data from
/api/repos/detailsso the list updates immediately - This makes the sync truly global — both pages refresh when the button is clicked
- After a sync from the Navbar, auto re-fetch repo data from
Tooltip content
The tooltip will say:
Refresh from GitHub Re-syncs team members, collaborator access, and repository data from GitHub. The app caches this data locally to avoid repeated API calls.
This covers what the button does and what's cached, as requested.
Verification
- On Team page (
/): sync button in navbar, click it → team list updates, timestamp updates, toast shown - On Repos page (
/repos): sync button in navbar, click it → repos list refreshes with new data, timestamp updates - Hover over sync button → tooltip appears explaining the action and cache contents
- Test error states (network failure, rate limit) → toast with error message
- Responsive: tooltip and timestamp hidden/adjusted on small screens
Decisions
- Timestamp format: Absolute (e.g. "Synced: 2/11/2026, 3:45 PM"), not relative
- Auto re-fetch: Pages auto re-fetch their data after sync completes (not toast-only)
- Visibility: All authenticated users, not admin-only
- Pure CSS tooltip (Tailwind
group-hover) over a tooltip library — no new dependencies, consistent with existing stack - Dedicated
/api/team/statusendpoint over reusing/api/team— avoids loading full data on every page just for a timestamp - Sync callbacks per-page over global data in context — keeps the context lightweight; pages own their data