Replace the UI with the Organic design handoff (docs/design_handoff_pansy_ui)
The frontend is rebuilt screen by screen from the handoff: warm cream ground, terracotta + sage accents, Caprasimo over Figtree, every control a pill. Same React/Vite/TanStack stack and the same lib/ data layer; the presentation is new. - Tokens: web/src/styles/index.css declares the handoff's styles.css variables through Tailwind's @theme under the same names; dark mode is those variables overridden on <html> by the handoff's pansy-theme.js, inlined in index.html so it runs before first paint. Lucide glyphs at stroke 2.75; a small pill kit (Button, Dialog, Field, Seg, Toggle, Tag, toast). - Login / Register: the centered column over soft accent circles; OIDC button and signup footer still follow /auth/providers. - Gardens: cards with a real SVG plot thumbnail (objects + plant-colored dots from /full), a `plan` tag for "<name> — <year>" copies, shares line, Open + share/copy/edit/delete; New garden / Share / Plan-a-season dialogs. - Plants: monogram markers derived from the name (collision-resolved across the catalog — replaces emoji icons), category chips, expandable lot cards, the scan-packet flow as a two-step dialog that never auto-creates. - Settings: Appearance (theme seg), Who gets in (read-only sign-in config), Garden assistant (self-saving toggle + chat/vision model fields), You. - Editor: a new canvas with the prototype's pointer model (wheel-to-cursor, pinch about the centroid, 3″ snap, one PATCH per drop, semantic-zoom monograms/labels), plus corner resize handles; desktop three-card workspace (toolkit | plan | rail with Plot/Journal/History/Assistant) and, below 760px of container width, the phone chrome (header, peek panel, tool strip, mode bar). Seasons as a segmented control over the years with data plus plan copies; Undo re-reads history before reverting the newest step. - Public read-only view and the register page restyled to match. - GET /settings gains a read-only `auth` view (registration mode, local auth, OIDC issuer) so the Settings page can show what's in force. - README / DESIGN.md / CLAUDE.md updated; @use-gesture/react dropped. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { monogramFor, monogramMap, speciesName } from './monogram'
|
||||
|
||||
describe('speciesName', () => {
|
||||
it('drops a variety suffix and a parenthetical', () => {
|
||||
expect(speciesName('Tomato — Cherokee Purple')).toBe('Tomato')
|
||||
expect(speciesName('Melon - Hale’s Best')).toBe('Melon')
|
||||
expect(speciesName('Garlic (Music)')).toBe('Garlic')
|
||||
expect(speciesName('Kale')).toBe('Kale')
|
||||
})
|
||||
})
|
||||
|
||||
describe('monogramMap', () => {
|
||||
it('gives a bare initial where it is unique', () => {
|
||||
const m = monogramMap([
|
||||
{ id: 1, name: 'Garlic' },
|
||||
{ id: 2, name: 'Tomato' },
|
||||
])
|
||||
expect(m.get(1)).toBe('G')
|
||||
expect(m.get(2)).toBe('T')
|
||||
})
|
||||
|
||||
it('resolves a shared initial by catalog order, second letter lowercase', () => {
|
||||
const m = monogramMap([
|
||||
{ id: 6, name: 'Tomato' },
|
||||
{ id: 26, name: 'Thyme' },
|
||||
{ id: 32, name: 'Marigold' },
|
||||
{ id: 29, name: 'Mint' },
|
||||
])
|
||||
expect(m.get(6)).toBe('T') // first into the catalog keeps the initial
|
||||
expect(m.get(26)).toBe('Th')
|
||||
expect(m.get(29)).toBe('M')
|
||||
expect(m.get(32)).toBe('Ma')
|
||||
})
|
||||
|
||||
it('takes the next free second letter, never repeating the initial', () => {
|
||||
const m = monogramMap([
|
||||
{ id: 5, name: 'Pea' },
|
||||
{ id: 7, name: 'Pepper' },
|
||||
{ id: 24, name: 'Parsley' },
|
||||
])
|
||||
expect(m.get(5)).toBe('P')
|
||||
expect(m.get(7)).toBe('Pe')
|
||||
expect(m.get(24)).toBe('Pa')
|
||||
const n = monogramMap([
|
||||
{ id: 1, name: 'Pea' },
|
||||
{ id: 2, name: 'Peach' },
|
||||
{ id: 3, name: 'Pepper' },
|
||||
])
|
||||
expect(n.get(2)).toBe('Pe')
|
||||
expect(n.get(3)).toBe('Pr') // "Pp" would repeat the initial
|
||||
})
|
||||
|
||||
it('uses the species for varieties and keeps the plain one first', () => {
|
||||
const m = monogramMap([
|
||||
{ id: 6, name: 'Tomato' },
|
||||
{ id: 40, name: 'Tomato — Cherokee Purple' },
|
||||
])
|
||||
expect(m.get(6)).toBe('T')
|
||||
expect(m.get(40)).toBe('To')
|
||||
})
|
||||
|
||||
it('marks an unletterable name with a placeholder', () => {
|
||||
expect(monogramMap([{ id: 1, name: '123' }]).get(1)).toBe('?')
|
||||
expect(monogramFor('🌱')).toBe('?')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user