Quality gates / quality (pull_request) Successful in 1m54s
Logging must never depend on AI availability, and an unsaved entry must not disappear when the user navigates away mid-log. - add src/vision-readiness.js so the photo flow consumes one readiness contract - when the worker is offline/disabled/unknown, offer no AI action and no analysis-consent prompt, and keep manual Bristol entry one action away - add src/draft.js and make the draft own the photo so a chosen photo and entered context survive a recoverable close or navigation, with a visible kept-draft summary and an explicit start over - drafts stay in session memory only: a photo awaiting consent is never persisted, and reload starts clean - service worker now refuses to cache image bodies in addition to analysis requests and no-store responses - tests: 2 new unit files, 3 service-worker privacy assertions (1 hold-out), 1 browser acceptance file, release-demo and CI/package assertions - release demo now visibly replays the vision-offline pass and the retained draft with synthetic data and an in-script assertion
40 lines
1.2 KiB
JavaScript
40 lines
1.2 KiB
JavaScript
// A draft is what the user has entered but not yet saved.
|
|
// It is kept in this browser only, so a recoverable close or navigation never loses it.
|
|
export function createDraft() {
|
|
return {
|
|
bristolType: 4,
|
|
typeChosen: false,
|
|
color: 'brown',
|
|
urgency: 0,
|
|
discomfort: 0,
|
|
note: '',
|
|
symptoms: {},
|
|
photoDataUrl: '',
|
|
};
|
|
}
|
|
|
|
export function isDraftDirty(draft = {}) {
|
|
return Boolean(
|
|
draft.typeChosen
|
|
|| draft.photoDataUrl
|
|
|| String(draft.note || '').trim()
|
|
|| Number(draft.urgency) > 0
|
|
|| Number(draft.discomfort) > 0
|
|
|| (draft.color && draft.color !== 'brown')
|
|
|| Object.values(draft.symptoms || {}).some(Boolean),
|
|
);
|
|
}
|
|
|
|
export function draftNotice(draft = {}) {
|
|
if (!isDraftDirty(draft)) return '';
|
|
const parts = [];
|
|
if (draft.typeChosen) parts.push(`Type ${draft.bristolType}`);
|
|
if (draft.color && draft.color !== 'brown') parts.push(`${draft.color} color`);
|
|
if (Number(draft.urgency) > 0) parts.push('urgency');
|
|
if (Number(draft.discomfort) > 0) parts.push('discomfort');
|
|
if (String(draft.note || '').trim()) parts.push('note');
|
|
if (Object.values(draft.symptoms || {}).some(Boolean)) parts.push('symptoms');
|
|
if (draft.photoDataUrl) parts.push('photo');
|
|
return parts.join(' · ');
|
|
}
|