70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
import { createContext, useContext, useEffect, useState } from 'react';
|
||
import { api } from '../api.js';
|
||
import { useInitialApiData } from './ApiData.jsx';
|
||
|
||
// Показывается, пока не пришли данные, и если API недоступен — шапка и подвал не должны пустеть.
|
||
const FALLBACK = {
|
||
phone: '+7 928 962-00-94',
|
||
hours: 'Пн–Вс: 08:00–20:00',
|
||
addresses: ['Махачкала, ул. Ляхова, 22', 'Махачкала, ул. Ляхова, 7'],
|
||
branches: [],
|
||
coords: null,
|
||
maxUrl: '',
|
||
email: '',
|
||
legal: null, // реквизиты: org_name, org_short, inn, ogrn, address, license, email
|
||
};
|
||
|
||
const SiteDataContext = createContext(FALLBACK);
|
||
|
||
// «г. Махачкала, ул. Ляхова, 22(3 этаж)» → «Махачкала, ул. Ляхова, 22 (3 этаж)»
|
||
function normalizeAddress(address) {
|
||
return String(address || '')
|
||
.replace(/^г\.\s*/, '')
|
||
.replace(/(\d)\(/g, '$1 (')
|
||
.trim();
|
||
}
|
||
|
||
export function SiteDataProvider({ children }) {
|
||
const initial = useInitialApiData('site');
|
||
const receivedAt = useInitialApiData('__receivedAt');
|
||
const [value, setValue] = useState(initial || FALLBACK);
|
||
|
||
useEffect(() => {
|
||
if (initial && receivedAt && Date.now() - receivedAt < 30_000) return undefined;
|
||
const ctrl = new AbortController();
|
||
const opts = { signal: ctrl.signal };
|
||
|
||
Promise.allSettled([api.branches(opts), api.settings(opts)]).then(([branchesRes, settingsRes]) => {
|
||
if (ctrl.signal.aborted) return;
|
||
const next = { ...FALLBACK };
|
||
|
||
const branches = branchesRes.status === 'fulfilled' ? branchesRes.value.data || [] : [];
|
||
if (branches.length) {
|
||
const first = branches[0];
|
||
next.branches = branches;
|
||
next.phone = first.phones?.[0] || FALLBACK.phone;
|
||
next.hours = first.work_hours?.[0] || FALLBACK.hours;
|
||
next.addresses = branches.map((b) => normalizeAddress(b.address));
|
||
const withCoords = branches.find((b) => b.lat && b.lon);
|
||
next.coords = withCoords ? { lat: withCoords.lat, lon: withCoords.lon } : null;
|
||
}
|
||
|
||
const settings = settingsRes.status === 'fulfilled' ? settingsRes.value.data || {} : {};
|
||
// Ссылки на MAX у клиники пока нет: иконка появится, когда её заведут в настройках.
|
||
if (typeof settings.max_url === 'string') next.maxUrl = settings.max_url;
|
||
if (typeof settings.email === 'string') next.email = settings.email;
|
||
if (settings.legal && typeof settings.legal === 'object') next.legal = settings.legal;
|
||
|
||
setValue(next);
|
||
});
|
||
|
||
return () => ctrl.abort();
|
||
}, []);
|
||
|
||
return <SiteDataContext.Provider value={value}>{children}</SiteDataContext.Provider>;
|
||
}
|
||
|
||
export function useSiteData() {
|
||
return useContext(SiteDataContext);
|
||
}
|