Smoke-sweep fixes: exact saves, local dates, safer remove, readable markers
- Garden and plant dialogs keep centimeters as the source of truth (LengthField in lib/units.ts): a no-change Save no longer rewrites 900 cm as 899.922 or a 45 cm spacing as 44.958, bumping versions and writing bogus history entries on the way. - The UI stamps every date with the browser's local day (lib/dates.ts). Journal notes already did; plop placement, fill and removal now do too, so a 9 pm placement isn't "planted tomorrow". The fill endpoint gained an optional plantedAt; API and agent callers still default to UTC today. - Removing an object that holds plants asks first and says how many go with it. An empty one still goes straight away (one Undo restores it). - The expanded plant card's action row wraps instead of clipping "Delete". - Monogram lettering switches to a dark ink on pale marker colors (garlic, cabbage, marigold) instead of near-white on near-white. - Copy-as-plan proposes the next free year and warns when the typed name already exists, so two gardens can't both read as "the 2027 plan". - Plan cards show the base name with a "2027 plan" tag, so the year — the point of the name — survives truncation. - A rejected model spec now says which model and why: a wrapped ErrInvalidInput's reason reaches the client as the 400's message, and the Settings field shows it inline instead of toasting "invalid input". Also defuses a clock bomb in TestRemainingReturnsWhenAPlantingIsRemoved, which only passed while the real date was before 2026-08-01. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
cmFromFtIn,
|
||||
convertDimensionField,
|
||||
dimensionField,
|
||||
editDimensionField,
|
||||
editSpacingField,
|
||||
spacingField,
|
||||
cmFromMeters,
|
||||
cmFromSpacing,
|
||||
dimensionInputMode,
|
||||
@@ -242,3 +247,30 @@ describe('formatSize / formatLength', () => {
|
||||
expect(formatLength(1219, 'imperial')).toBe('40′')
|
||||
})
|
||||
})
|
||||
|
||||
describe('LengthField', () => {
|
||||
it('keeps the stored centimeters through a unit switch and back', () => {
|
||||
let f = dimensionField(900, 'imperial')
|
||||
expect(f.text).toBe('29′ 6.3″')
|
||||
f = convertDimensionField(f, 'metric')
|
||||
expect(f.text).toBe('9')
|
||||
f = convertDimensionField(f, 'imperial')
|
||||
expect(f).toEqual({ text: '29′ 6.3″', cm: 900 })
|
||||
})
|
||||
|
||||
it('moves the centimeters only when the text is edited', () => {
|
||||
expect(editDimensionField("15' 6\"", 'imperial').cm).toBe(472.44)
|
||||
const typo = editDimensionField('nope', 'imperial')
|
||||
expect(typo.cm).toBeNull()
|
||||
// A typo survives a unit switch as typed rather than turning into a number.
|
||||
expect(convertDimensionField(typo, 'metric')).toEqual(typo)
|
||||
})
|
||||
|
||||
it('spacing: 45 cm reads 17.7 in and stays 45 cm until typed over', () => {
|
||||
const f = spacingField(45, 'imperial')
|
||||
expect(f).toEqual({ text: '17.7', cm: 45 })
|
||||
expect(editSpacingField('18', 'imperial').cm).toBe(45.72)
|
||||
expect(editSpacingField('', 'imperial').cm).toBeNull()
|
||||
expect(editSpacingField('25', 'metric').cm).toBe(25)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user