fix(projects): list cache invalidation, project colours, and form fixes - #65
Merged
Merged
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
furqan-debug
approved these changes
Sep 21, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#B27B00in the database — but the list still showed the old one. Its cache was two module-level variables insideProjects.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.lib/projectsCache.tsso 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.#FFFFFFand#0F172A, measured.var(--color-chart-main), which is not a hex. Anywhere that builds a tint by appending alpha turned it intovar(-- colour-chart-main)10— not a colour, so the tint silently failed.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.viewDatewas set once at mount and moved only by the arrows, so picking 31 August from a September grid left the grid on September.2026-00-28for December 2025, a December grid2026-13-01for January 2027, and the handler wrote them out as-is.min, so the spinner stepped past zero and-4went to the database. Guarded at the input, the change handler and the save. No existing rows affected — all 39 checked.maxWidth="full"on the header withmax-w-6xl mx-autoon the content.rose-50/rose-900.