feat: the greeting varies, and one TODO closed against its own instruction
Four time bands rather than three — someone revising at 2am is not having an evening — and the wording is keyed on the date, so it differs when you come back and holds steady while you are here. A line that changes under the reader on re-render looks like a glitch, not warmth. Also closed: "References with in-text superscript markers". That was on the list against an explicit instruction — "with refernec, but you dont need in text reference". Leaving it open would have meant building it eventually. Frontend 293/293. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
This commit is contained in:
parent
85ccdadc9b
commit
44af549dbc
3 changed files with 58 additions and 7 deletions
11
docs/TODO.md
11
docs/TODO.md
|
|
@ -115,7 +115,9 @@ Captured so nothing is lost while the article writing runs.
|
|||
bank, answered correctly, average score.
|
||||
- [x] **The old performance card is gone** — a per-quiz graph needing two
|
||||
attempts, superseded by the session analysis.
|
||||
- [ ] **Vary the greeting** rather than one fixed line.
|
||||
- [x] **Vary the greeting** — done 2026-09-11. Four time bands including
|
||||
the small hours, keyed on the date so it holds for a visit and differs
|
||||
between days rather than changing under the reader mid-render.
|
||||
|
||||
### Questions I owe an answer to
|
||||
- [x] **What extracted the PDFs?** PyMuPDF (`fitz`) in `pdf_service.py`, with an
|
||||
|
|
@ -204,9 +206,10 @@ Captured so nothing is lost while the article writing runs.
|
|||
- [x] **Library browsed column by column** — done 2026-09-10. The articles page
|
||||
is now the column browser itself: topics and the articles filed under them
|
||||
share a column, separated by icon. Search still answers with a flat list.
|
||||
- [ ] **References** — a section titled "References" is pinned last and styled,
|
||||
but the numbered list with superscript markers linking down to it is not
|
||||
built.
|
||||
- [x] **References** — a section titled "References" is pinned last and styled.
|
||||
The numbered list with in-text superscript markers is deliberately NOT
|
||||
built: "with refernec, but you dont need in text reference". Closing this
|
||||
rather than leaving it open against an instruction.
|
||||
- [ ] **Per-section notes and feedback** — a learner's own note attached to a
|
||||
section, and a feedback channel to the educator.
|
||||
- [ ] **High-yield / key-exam-info toggles** — mark spans and let the reader show
|
||||
|
|
|
|||
|
|
@ -6,9 +6,30 @@ import LineChart from '../components/LineChart'
|
|||
import MyNote from '../components/MyNote'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
|
||||
function greeting(name) {
|
||||
const h = new Date().getHours()
|
||||
const time = h < 12 ? 'Good morning' : h < 17 ? 'Good afternoon' : 'Good evening'
|
||||
/**
|
||||
* The line at the top of the dashboard.
|
||||
*
|
||||
* One fixed greeting every visit is the kind of thing that stops being read
|
||||
* after a week. These vary by the day rather than by the render, so it is
|
||||
* different when you come back and stable while you are here — a line that
|
||||
* changes under you as the page re-renders reads as a glitch, not as warmth.
|
||||
* Late-night and early-morning get their own, because someone revising at
|
||||
* 2am is not having an evening.
|
||||
*/
|
||||
const GREETINGS = {
|
||||
night: ['Still up', 'Burning the midnight oil', 'Late one'],
|
||||
morning: ['Good morning', 'Morning', 'Bright and early'],
|
||||
afternoon: ['Good afternoon', 'Afternoon', 'Hello again'],
|
||||
evening: ['Good evening', 'Evening', 'Winding down'],
|
||||
}
|
||||
|
||||
export function greeting(name, now = new Date()) {
|
||||
const h = now.getHours()
|
||||
const slot = h < 5 ? 'night' : h < 12 ? 'morning' : h < 17 ? 'afternoon' : h < 22 ? 'evening' : 'night'
|
||||
const options = GREETINGS[slot]
|
||||
// Keyed on the date, so it holds for the whole visit.
|
||||
const day = Math.floor(now.getTime() / 86400000)
|
||||
const time = options[day % options.length]
|
||||
const first = name?.split(' ')[0] || ''
|
||||
return `${time}${first ? `, ${first}` : ''}`
|
||||
}
|
||||
|
|
|
|||
27
frontend/src/pages/greeting.test.js
Normal file
27
frontend/src/pages/greeting.test.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { greeting } from './DashboardPage'
|
||||
|
||||
const at = (hour, day = 15) => new Date(2026, 8, day, hour, 0, 0)
|
||||
|
||||
describe('the dashboard greeting', () => {
|
||||
it('fits the hour, including the small hours', () => {
|
||||
expect(greeting('Ada', at(2))).toMatch(/Still up|midnight|Late one/)
|
||||
expect(greeting('Ada', at(9))).toMatch(/[Mm]orning|Bright/)
|
||||
expect(greeting('Ada', at(14))).toMatch(/[Aa]fternoon|Hello/)
|
||||
expect(greeting('Ada', at(19))).toMatch(/[Ee]vening|Winding/)
|
||||
expect(greeting('Ada', at(23))).toMatch(/Still up|midnight|Late one/)
|
||||
})
|
||||
|
||||
it('uses the first name only, and copes without one', () => {
|
||||
expect(greeting('Ada Lovelace', at(9))).toMatch(/, Ada$/)
|
||||
expect(greeting('', at(9))).not.toContain(',')
|
||||
expect(greeting(undefined, at(9))).not.toContain(',')
|
||||
})
|
||||
|
||||
it('holds steady through a visit but differs between days', () => {
|
||||
// Same day, different minutes: the line must not change under the reader.
|
||||
expect(greeting('Ada', at(9))).toBe(greeting('Ada', new Date(2026, 8, 15, 9, 45)))
|
||||
const week = new Set([15, 16, 17, 18, 19].map(d => greeting('Ada', at(9, d))))
|
||||
expect(week.size).toBeGreaterThan(1)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue