Skip to content

fix(projects): list cache invalidation, project colours, and form fixes - #65

Merged
furqan-debug merged 14 commits into
mainfrom
fix/projects-colors-and-list-cache
Sep 21, 2026
Merged

furqan-debug merged 14 commits into
mainfrom
fix/projects-colors-and-list-cache

Conversation

@mhuzaifa2904

Copy link
Copy Markdown
Collaborator
  • Editing a project didn't change the list. Saving a colour stored it correctly — ELB Consulting holds #B27B00 in the database — but the list still showed the old one. Its cache was two module-level variables inside Projects.tsx, keyed on the active tab and never cleared; saving navigates back, finds a matching key, and serves the pre-edit rows. Only the refresh button or a reload showed the change.
  • The cache moves to lib/projectsCache.ts so the things that change a project can invalidate it. Three now do: saving the form, bulk archive, and archive/delete from a row menu. The last two had the same fault — both refetched without forcing, re-reading the cache they'd just made stale.
  • A new project colour palette. Generated in OKLCH at fixed lightness (0.62) and chroma (0.16) with hues spaced around the wheel, so they read as one set rather than eight unrelated colours. Mid-lightness because the list prints each project's name in its colour on a 10% tint of itself — all eight clear 3:1 against both #FFFFFF and #0F172A, measured.
  • The old palette held var(--color-chart-main), which is not a hex. Anywhere that builds a tint by appending alpha turned it into var(-- colour-chart-main)10 — not a colour, so the tint silently failed.
  • The Time Distribution donut cycled five shades of gold with index % 5. Around twenty projects meant every fifth slice repeated, and the five were close enough that no slice could be told from its neighbours or matched to the legend. It uses each project's own colour now, falling back to the palette by position; the legend dots take the same value, so a row and its arc agree.
  • The date picker didn't follow the selection. viewDate was set once at mount and moved only by the arrows, so picking 31 August from a September grid left the grid on September.
  • The same click path produced invalid dates at year boundaries — a January grid offered 2026-00-28 for December 2025, a December grid 2026-13-01 for January 2027, and the handler wrote them out as-is.
  • The budget limit accepted negatives. No min, so the spinner stepped past zero and -4 went to the database. Guarded at the input, the change handler and the save. No existing rows affected — all 39 checked.
  • The project form was narrower than its own heading. maxWidth="full" on the header with max-w-6xl mx-auto on the content.
  • The form's error appeared at the bottom of the page, after the members and teams panels. It sits beside the Create Project button now, is sized to its text rather than stretching the full width, distinguishes a validation warning from a save failure, and uses theme tokens instead of rose-50/rose-900.
  • Projects list: refresh button removed, and the green pulsing "Online Now" pill on the Archived tab now reads "Paused" in muted grey.

Muhammad Huzaifa and others added 14 commits September 22, 2026 00:22
…over

Two things, both reached by clicking a day from a neighbouring month.

viewDate was set once at mount and moved only by the arrows, so it never
followed the selection. Picking 31 August from the grey leading row of a
September grid set the value but left the grid on September, with the
selected day sitting outside the month on screen. It now tracks the value,
and re-syncs on open, so paging around without choosing anything does not
leave the grid somewhere else.

Those grey days also carried the current year with month - 1 or month + 1,
so a January grid offered "2026-00-28" for December 2025 and a December
grid "2026-13-01" for January 2027 — neither a real date, and the click
handler wrote them out as-is. Date normalises the rollover now. Checked
across January, December and both sides of a year end: every cell parses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PageLayout was told maxWidth="full", so the heading and the Create Project
button ran the full width — but the content under them was wrapped in
max-w-6xl mx-auto. The card was narrower than its own heading and stopped
well short of the button on the right, which is the misalignment.

Every other full-width page lets its content fill. This one does now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The input had no min, so the spinner stepped straight past zero and a
typed or pasted "-" was accepted. A negative budget has no meaning, and
the value went to the database exactly as entered.

min="0" handles the spinner; the change handler checks the value too,
since min does not stop typing. The save clamps as well, so nothing below
zero is written whichever route it arrived by.

No existing rows are affected — all 39 projects checked, none negative.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eight hues held at a similar mid lightness, so they sit together and with
the brand's navy and gold: gold, orange, rose, purple, indigo, sky, teal,
green.

Mid tones because the colour is not only a swatch — the projects list
prints the project name IN it, on a 10% tint of itself, so each has to
read on white and on the dark surface. The old set was Tailwind 500s
chosen for a light background; #f59e0b and #84cc16 in particular washed
out on white. Every colour here clears 3:1 against both #FFFFFF and
#0F172A, measured rather than eyeballed.

Ten became eight. Two of the old ten were near-duplicates — emerald beside
teal, orange beside amber — and could not be told apart at swatch size.

The list also held var(--color-chart-main), which is not a hex. The places
that build a tint by appending alpha produce "var(--color-chart-main)10"
from it, which is not a colour. All eight are plain hex.

Existing projects are unaffected: all 39 carry #3b82f6, which still
renders. They only change if someone edits one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the muted set from the previous commit. Same eight slots, but
generated in OKLCH at a fixed lightness (0.62) and chroma (0.16), with the
hues spaced around the wheel.

Holding L and C constant is what makes them read as one set: no colour
shouts louder than its neighbours, and none fades beside them. Picking
hexes by eye cannot do that — the earlier gold and orange were visibly
duller than the purple and indigo sitting next to them, because equal
"saturation" in hex is not equal saturation to the eye.

Chroma is up from the last attempt, which is the attractiveness: 0.16
instead of hand-picked values that landed around 0.10-0.13.

Still mid lightness, because the projects list prints each project's name
IN its colour on a 10% tint of itself. All eight clear 3:1 against both
#FFFFFF and #0F172A, worst case 3.13 on white and 4.52 on dark.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It was rendered inline as the last child of the right-hand column, after
the members list and the team panel — so on a form this tall it landed
near the bottom of the page. Pressing Create Project at the top appeared
to do nothing, with the reason off-screen.

It sits above the form now, alongside the button that produced it, and is
a little less tall so it reads as a message rather than another card.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three things.

It stretched the full content width for four words, because it was a
block. inline-flex makes it as wide as its text.

It called a missing project name an error. A missing name is the form
telling you something; a failed save is the system failing. They now
carry a severity: the name check is a warning in amber, the two catch
blocks stay errors in red.

And it was built from rose-50, rose-200, rose-500 and rose-900 — Tailwind
light-mode colours that wash out on the dark surface. It uses the theme's
own --warning and --error now, at a tint for the fill and full strength
for the chip, with the body text on text-main so it reads in both modes.

"Project name is mandatory" becomes "Project name is required", which is
how the rest of the portal words this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It moves into the header's actions row, immediately left of the button,
so the answer appears where the click was rather than in the content
below it.

The bottom margin goes with the move — spacing in that row is the flex
gap now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The icon and the colour already say which it is, and the word sat on its
own line above the message, doubling the height of something four words
long.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The donut cycled five colours with index % 5, and all five were shades of
gold. With around twenty projects every fifth slice repeated, and the five
were close enough that no slice could be told from its neighbours or
matched to the list beneath it. The ring read as decoration.

It uses the eight project colours now — distinct hues, generated at one
lightness and chroma, so they still look like a set. The legend dots below
take the same index, so a row and its arc agree; they were on the same
five-gold cycle and would otherwise have drifted apart.

The palette moves to lib/projectColors.ts rather than being copied. The
project form imports it from there.

Not the same thing as colouring each slice by the project's own colour,
which is where this should end up. It cannot go there yet: all 39 projects
currently store #3b82f6, so every slice would come out identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Whatever colour a project is given now shows up here: the slice and its
legend dot both take proj.color, so the ring matches the swatch on the
project and the dot wherever else it appears.

The colour was already being fetched and carried on projectActivity — the
chart simply ignored it and cycled a palette by position instead.

A project with no colour set falls back to the palette by index, so
slices stay distinguishable rather than collapsing to one. That fallback
was var(--color-chart-main), which is not a hex — anywhere that builds a
tint by appending alpha turned it into "var(--color-chart-main)10".

Worth knowing: all 39 projects currently store #3b82f6, so the ring will
be one colour until they are given different ones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Saving a colour stored it correctly — ELB Consulting holds #B27B00 in the
database — but the list still showed the old one.

The list's cache was two module-level variables inside Projects.tsx, keyed
on the active tab and never cleared. Saving navigates back to the list,
which found a matching key and served the rows it had fetched before the
edit. Only the refresh button or a page reload showed the change.

The cache moves to lib/projectsCache so the things that change a project
can invalidate it, and three places now do: saving the form, archiving in
bulk, and archive or delete from a row's menu. The last two had the same
fault — both refetched without forcing, so they re-read the cache they had
just invalidated the truth of.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The refresh button beside the search box goes. Its `refreshing` state went
with it: nothing rendered it once the button was gone, so it was being set
and cleared for no one.

The green "Online Now" pill stayed green and pulsing on the Archived tab,
where nothing is online and the rows are finished work. It reads
"Completed" there, in muted grey with the pulse off. On Active it is
unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Completed" claimed the work was finished. Archived only means it is not
running, and an archived project can be restored, so "Paused" is what the
tab actually holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
trackowl Ready Ready Preview Sep 21, 2026 9:55pm UTC

@furqan-debug
furqan-debug merged commit 5c585ed into main Sep 21, 2026
4 checks passed

This branch was successfully deployed

2 active deployments
Preview – trackowl — 9a8ed0a4 Deployed Sep 21, 2026 by vercel[bot]
Preview – trackowl-admin — 9a8ed0a4 Deployed Sep 21, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants