TICKET-005: Frontend daily view — milestone M1 complete
- Dashboard: progress bar vs target, entries grouped by meal slot, edit/delete inline, date navigation (UTC-safe shiftDate helper) - Add Food manual creation form, search-and-log flow with live preview - Minimal target form; loading/error/empty states throughout - Stores (current date, log, summary) with summary refresh on mutation - All HTTP via lib/api.js; formatting via lib/format.js; runes only - Full suites green (backend 104 passed, frontend vitest + build)
This commit is contained in:
@@ -4,11 +4,87 @@
|
||||
* Svelte 5 runes style only (spec §8.2 rule 8).
|
||||
*/
|
||||
|
||||
// Current date as a YYYY-MM-DD string — the server never decides "today" (spec §8.1 rule 8)
|
||||
export const currentDate = $state({
|
||||
value: new Date().toISOString().slice(0, 10),
|
||||
import { api } from './api.js'
|
||||
import { shiftDate } from './format.js'
|
||||
|
||||
/** Return today as a YYYY-MM-DD string — the server never decides "today" (spec §8.1 rule 8). */
|
||||
function todayString() {
|
||||
return new Date().toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
// ── Current date ────────────────────────────────────────────────────────────
|
||||
|
||||
export const currentDate = $state({ value: todayString() })
|
||||
|
||||
export function setDate(dateStr) {
|
||||
currentDate.value = dateStr
|
||||
}
|
||||
|
||||
export function goPrevDay() {
|
||||
currentDate.value = shiftDate(currentDate.value, -1)
|
||||
}
|
||||
|
||||
export function goNextDay() {
|
||||
currentDate.value = shiftDate(currentDate.value, 1)
|
||||
}
|
||||
|
||||
// ── App view ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export const appView = $state({ current: 'dashboard' })
|
||||
|
||||
/** Simple navigation requests from child components (e.g., "Set a target"). */
|
||||
export const navigateTo = (view) => { appView.current = view }
|
||||
|
||||
// ── Day data (log + summary) ─────────────────────────────────────────────────
|
||||
|
||||
export const dayData = $state({
|
||||
log: [],
|
||||
summary: null, // { date, totals: { calories, protein_g, ... }, target: {...} | null }
|
||||
loading: false,
|
||||
error: null,
|
||||
})
|
||||
|
||||
// Today's log entries and current target, refreshed from the API after any mutation
|
||||
export const todayLog = $state({ entries: [], loading: false, error: null })
|
||||
export const currentTarget = $state({ target: null, loading: false, error: null })
|
||||
export async function refreshDayData() {
|
||||
const date = currentDate.value
|
||||
dayData.loading = true
|
||||
dayData.error = null
|
||||
try {
|
||||
const [log, summary] = await Promise.all([
|
||||
api.getLog(date),
|
||||
api.getSummary(date),
|
||||
])
|
||||
dayData.log = log
|
||||
dayData.summary = summary
|
||||
dayData.loading = false
|
||||
} catch (e) {
|
||||
dayData.error = e.message
|
||||
dayData.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
/** Re-fetch the summary for the current date without touching the log array. */
|
||||
async function refreshSummary() {
|
||||
try {
|
||||
dayData.summary = await api.getSummary(currentDate.value)
|
||||
} catch {
|
||||
// Keep the previous summary on failure; the user can retry.
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a newly-created log entry to the store then refresh the summary. */
|
||||
export async function addLogEntryToStore(entry) {
|
||||
dayData.log = [...dayData.log, entry]
|
||||
await refreshSummary()
|
||||
}
|
||||
|
||||
/** Update a log entry in the store after a successful API PUT, then refresh the summary. */
|
||||
export async function updateLogEntryInStore(updated) {
|
||||
dayData.log = dayData.log.map(e => (e.id === updated.id ? updated : e))
|
||||
await refreshSummary()
|
||||
}
|
||||
|
||||
/** Remove a log entry from the store after a successful API DELETE, then refresh the summary. */
|
||||
export async function removeLogEntryFromStore(id) {
|
||||
dayData.log = dayData.log.filter(e => e.id !== id)
|
||||
await refreshSummary()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user