Files
disco-service-tracker-plugin/.claude-context/sessions/2026-09-23-sheet-columns-sidebar-location.md
T

6.5 KiB

Google Sheet Column Remapping, Sidebar Panels & Location Display

Date: 2026-09-23 Project: disco-service-tracker-plugin Status: resolved Tags: google-sheet, column-mapping, sidebar, location, ntt, dashboard, bugfix

Context

The NTT Google Sheet had columns added and reordered, breaking field mapping for AssignedTo, Status, and Notes. Separately, the dashboard sidebar needed new panels (Awaiting Vendor, Awaiting Parts), sidebar items needed to be clickable links, and NTT sheet jobs needed to show their raw location text on the tile. Several bugs were also fixed: closed NTT jobs reappearing, the On Hold panel being missing from the sidebar.

Key Decisions

  • Column indices stored in config, not hardcoded — GoogleSheetConfig holds all col indices as int properties. Set to -1 to disable a column (e.g. ColCompletionDate = -1). This lets the Config UI page update them without a rebuild.
  • Campus column as location fallback — Added ColCampus (col 8). GoogleSheetService reads Location/Classroom first; if empty, falls back to Campus. This means location data is recovered even when the classroom field is blank.
  • RawSheetLocation on DashboardTile — When the sheet's location text can't be fuzzy-matched to a configured Disco location, the raw string is stored on the tile and displayed as a plain italic 📍 pill. Rejected alternative: silently dropping unmatched locations (loses visible info).
  • Sidebar buckets are mutually exclusive, priority-ordered — rfr → onHold → awaitingVendor → awaitingParts → main. A ticket can only be in one sidebar panel. Rejected alternative: showing tickets in both sidebar and main grid.
  • Closed NTT tickets suppressed by StatusOverride — BuildDashboard skips any NTT ticket whose saved StatusOverride contains a closed keyword (resolved, dismissed, complete, closed, done, cancel, finished), even if the sheet row still shows as open. This prevents dismissed jobs from reappearing on every refresh.
  • SidebarPanel() helper — Extracted repeated sidebar panel HTML into one method taking title, accent colour, item list, url, and a showCollected bool. Rejected alternative: copy-paste per panel.
  • Sidebar items are <a> links — Each sidebar item wraps in <a class='rfr-item rfr-link' href='/Detail?...'>. The Collected button uses event.stopPropagation() so clicking it submits the form rather than following the link.

Technical Details

New sheet column layout (0-based):

Col Field Property
0 Timestamp ColTimestamp
1 Email address ColEmail
2 What is the task/request? ColTask
3 Detailed Description ColIssue
4 Location / Classroom ColLocation
5 Requested By ColRequestedBy
6 Priority Level ColPriority
7 Preferred Completion Date ColPreferredDate
8 Campus ColCampus
9 Assigned to ColAssignedTo
10 Status ColStatus
11 Column 1 (junk) ignored
12 Campus (duplicate) ignored
13 Notes ColNotes
14 Category ignored
15 Resolution Confirmed ignored

ColCompletionDate = -1 — SafeGet() returns null for index < 0, so no out-of-bounds risk.

Location fuzzy matching (ServiceTrackerService.MatchSheetLocation):

  1. Exact ID match
  2. Exact name match (case-insensitive)
  3. Substring match either direction (e.g. "Library Block A" matches location named "Library") Falls back to storing raw text in RawSheetLocation if no match.

Sidebar bucket match strings:

  • Ready for Return: status contains "ready for return" or "ready for pickup"
  • On Hold: contains "on hold"
  • Awaiting Vendor: contains "awaiting vendor"
  • Awaiting Parts: contains "awaiting parts"

Closed status keywords (ClosedStatusKeywords array): "resolved", "dismissed", "complete", "closed", "done", "cancel", "finished"

CSS classes added:

  • .rfr-link — makes sidebar <a> items block-level, no text-decoration, colour inherit
  • .rfr-link:hover — light blue bg + blue border
  • .trsl — italic grey for raw sheet location pill on tiles
  • .sp-ct — coloured count badge in sidebar panel headings
  • .btn-xs — 2px/7px padding, 10px font (smaller than btn-sm)

After install: delete sheet_cache.csv from App_Data/Plugins/Disco.Plugins.ServiceTracker/ and save Config once to write new column indices to config.json.

Code Changes

File Change
Models/ServiceTrackerConfig.cs Added ColCampus property; updated GoogleSheetConfig() constructor and CreateDefault() to new column indices (AssignedTo→9, Status→10, ColCompletionDate→-1, Notes→13)
Models/DashboardViewModel.cs Added RawSheetLocation to DashboardTile; added AwaitingVendor and AwaitingParts lists to DashboardViewModel
Services/GoogleSheetService.cs Reads ColCampus as location fallback; SafeGet handles index -1 gracefully; updated to use new column properties
Services/ServiceTrackerService.cs Added ClosedStatusKeywords — skips NTT tickets with closed StatusOverride; added awaitingVendor and awaitingParts bucket queries; added MatchSheetLocation(); sets RawSheetLocation on unmatched tiles; bumped through v1.5.5→v1.5.9
WebHandler/ServiceTrackerWebHandler.cs Added SidebarPanel() helper; sidebar items are <a rfr-link>; Collected button has stopPropagation; Config page col grid updated with Campus field and correct labels/indices; CSS additions (rfr-link, trsl, sp-ct, btn-xs)

Open Questions / Next Steps

  • Category field (col 14) — currently ignored; could be surfaced as a tag or filter on tiles if useful
  • Resolution Confirmed (col 15) — could auto-close NTT tickets when this is populated, rather than requiring manual status override
  • Campus as a proper location dimension — currently only used as a fallback for the Location/Classroom field; could be shown separately on the tile (e.g. as a secondary badge) if campuses are meaningful for filtering
  • external_tickets.json growth — resolved NTT tickets accumulate forever; a periodic cleanup of records older than N days with a closed StatusOverride would help
  • Sheet cache invalidation — currently time-based only; a manual "Refresh Now" button on the dashboard would be useful

Artifacts