feat: pharmacy hours status and open now filter
Run Tests on Branches / Detect Changes (push) Successful in 17s
Run Tests on Branches / Backend Tests (push) Successful in 2m44s
Run Tests on Branches / Frontend Tests (push) Successful in 1m59s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Detect Changes (push) Successful in 17s
Run Tests on Branches / Backend Tests (push) Successful in 2m44s
Run Tests on Branches / Frontend Tests (push) Successful in 1m59s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
- Add is_open_now and is_always_open helpers in backend - Add backend endpoint enrichment with is_open and is_24h fields - Add client-side getOpenStatus with 24h, opens-at, opens-tomorrow support - Add open-now filter toggle in PublicView and SearchView - Add pharmacy no-hours fallback display - Add sticky pharmacy controls on scroll - Add admin hours editor with 24h toggle - Add translations (es/ca) for all hour-related strings - Add backend tests for hours logic and pharmacy endpoint - Remove unused backup test files
This commit is contained in:
@@ -27,14 +27,12 @@ function parseHours(raw) {
|
||||
}
|
||||
}
|
||||
|
||||
function findNextOpen(hours, now) {
|
||||
function findNextOpenInfo(hours, now) {
|
||||
for (let offset = 1; offset <= 7; offset++) {
|
||||
const day = DAYS[(now.getDay() + offset) % 7];
|
||||
const range = hours[day];
|
||||
if (Array.isArray(range) && range.length === 2) {
|
||||
const openStr = range[0];
|
||||
if (offset === 1) return `mañana a las ${openStr}`;
|
||||
return `${DAY_LABELS[day]} a las ${openStr}`;
|
||||
return { day, time: range[0], offset };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -42,14 +40,24 @@ function findNextOpen(hours, now) {
|
||||
|
||||
export function getOpenStatus(rawHours, now = new Date()) {
|
||||
const hours = parseHours(rawHours);
|
||||
if (!hours) return null;
|
||||
if (!hours) return { status: 'unknown', label: 'Sin horario', labelKey: 'pharmacy.noHours', labelParams: {} };
|
||||
|
||||
if (isAlwaysOpen(hours)) {
|
||||
return { status: 'open', label: 'Abierto 24h', labelKey: 'pharmacy.alwaysOpen', labelParams: {} };
|
||||
}
|
||||
|
||||
const day = DAYS[now.getDay()];
|
||||
const range = hours[day];
|
||||
|
||||
if (!Array.isArray(range) || range.length !== 2) {
|
||||
const next = findNextOpen(hours, now);
|
||||
return { status: 'closed', label: next ? `Cerrado · Abre ${next}` : 'Cerrado' };
|
||||
const next = findNextOpenInfo(hours, now);
|
||||
if (!next) {
|
||||
return { status: 'closed', label: 'Cerrado', labelKey: 'pharmacy.closedAllDay', labelParams: {} };
|
||||
}
|
||||
if (next.offset === 1) {
|
||||
return { status: 'closed', label: `Cerrado · Abre mañana a las ${next.time}`, labelKey: 'pharmacy.opensTomorrow', labelParams: { time: next.time } };
|
||||
}
|
||||
return { status: 'closed', label: `Cerrado · Abre el ${DAY_LABELS[next.day]} a las ${next.time}`, labelKey: 'pharmacy.opensDay', labelParams: { day: DAY_LABELS[next.day], time: next.time } };
|
||||
}
|
||||
|
||||
const openMins = parseHM(range[0]);
|
||||
@@ -59,15 +67,81 @@ export function getOpenStatus(rawHours, now = new Date()) {
|
||||
const nowMins = now.getHours() * 60 + now.getMinutes();
|
||||
|
||||
if (nowMins < openMins) {
|
||||
return { status: 'closed', label: `Cerrado · Abre a las ${range[0]}` };
|
||||
return { status: 'closed', label: `Cerrado · Abre a las ${range[0]}`, labelKey: 'pharmacy.opensAt', labelParams: { time: range[0] } };
|
||||
}
|
||||
if (nowMins >= closeMins) {
|
||||
const next = findNextOpen(hours, now);
|
||||
return { status: 'closed', label: next ? `Cerrado · Abre ${next}` : 'Cerrado' };
|
||||
const next = findNextOpenInfo(hours, now);
|
||||
if (!next) {
|
||||
return { status: 'closed', label: 'Cerrado', labelKey: 'pharmacy.closedAllDay', labelParams: {} };
|
||||
}
|
||||
if (next.offset === 1) {
|
||||
return { status: 'closed', label: `Cerrado · Abre mañana a las ${next.time}`, labelKey: 'pharmacy.opensTomorrow', labelParams: { time: next.time } };
|
||||
}
|
||||
return { status: 'closed', label: `Cerrado · Abre el ${DAY_LABELS[next.day]} a las ${next.time}`, labelKey: 'pharmacy.opensDay', labelParams: { day: DAY_LABELS[next.day], time: next.time } };
|
||||
}
|
||||
return { status: 'open', label: `Abierto · Cierra a las ${range[1]}` };
|
||||
return { status: 'open', label: `Abierto · Cierra a las ${range[1]}`, labelKey: 'pharmacy.openNow', labelParams: { time: range[1] } };
|
||||
}
|
||||
|
||||
export function emptyHours() {
|
||||
return { sun: null, mon: null, tue: null, wed: null, thu: null, fri: null, sat: null };
|
||||
}
|
||||
|
||||
export function emptyHoursDraft() {
|
||||
const draft = {};
|
||||
for (const day of DAYS) {
|
||||
draft[day] = { open: '09:00', close: '21:00', closed: true };
|
||||
}
|
||||
return draft;
|
||||
}
|
||||
|
||||
export function hoursToDraft(raw) {
|
||||
let parsed = null;
|
||||
if (raw) {
|
||||
try { parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; }
|
||||
catch { parsed = null; }
|
||||
}
|
||||
const draft = {};
|
||||
for (const day of DAYS) {
|
||||
const v = parsed && parsed[day];
|
||||
if (Array.isArray(v) && v.length === 2) {
|
||||
draft[day] = { open: v[0], close: v[1], closed: false };
|
||||
} else {
|
||||
draft[day] = { open: '09:00', close: '21:00', closed: true };
|
||||
}
|
||||
}
|
||||
return draft;
|
||||
}
|
||||
|
||||
export function draftToHours(draft) {
|
||||
const out = {};
|
||||
let hasAny = false;
|
||||
for (const day of DAYS) {
|
||||
const d = draft[day];
|
||||
if (d && !d.closed && d.open && d.close) {
|
||||
out[day] = [d.open, d.close];
|
||||
hasAny = true;
|
||||
} else {
|
||||
out[day] = null;
|
||||
}
|
||||
}
|
||||
return hasAny ? out : null;
|
||||
}
|
||||
|
||||
export function isAlwaysOpen(rawHours) {
|
||||
const h = parseHours(rawHours);
|
||||
if (!h) return false;
|
||||
for (const d of DAYS) {
|
||||
const r = h[d];
|
||||
if (!Array.isArray(r) || r.length !== 2) return false;
|
||||
if (r[0] !== '00:00' || r[1] !== '24:00') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function makeAlwaysOpenDraft() {
|
||||
const draft = {};
|
||||
for (const day of DAYS) {
|
||||
draft[day] = { open: '00:00', close: '24:00', closed: false };
|
||||
}
|
||||
return draft;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user