172 lines
7.0 KiB
TypeScript
172 lines
7.0 KiB
TypeScript
import { useEffect, useState } from 'react';
|
||
import { useNavigate } from 'react-router-dom';
|
||
import { useStoreSession } from '../contexts/StoreSessionContext';
|
||
import { request } from '../lib/api';
|
||
|
||
function formatShopHours(store: Record<string, unknown> | null) {
|
||
const parts: string[] = [];
|
||
const openTime = store?.openTime ? String(store.openTime) : '';
|
||
const closeTime = store?.closeTime ? String(store.closeTime) : '';
|
||
const openTime2 = store?.openTime2 ? String(store.openTime2) : '';
|
||
const closeTime2 = store?.closeTime2 ? String(store.closeTime2) : '';
|
||
if (openTime && closeTime) parts.push(`${openTime} - ${closeTime}`);
|
||
if (openTime2 && closeTime2) parts.push(`${openTime2} - ${closeTime2}`);
|
||
return parts.length ? parts.join(',') : '09:30 - 22:00';
|
||
}
|
||
|
||
export default function StatusPage() {
|
||
const navigate = useNavigate();
|
||
const { resetSession } = useStoreSession();
|
||
const [open, setOpen] = useState(true);
|
||
const [permanentlyClosed, setPermanentlyClosed] = useState(false);
|
||
const [store, setStore] = useState<Record<string, unknown> | null>(null);
|
||
const [lastUpdate, setLastUpdate] = useState('');
|
||
const [showModal, setShowModal] = useState(false);
|
||
const [pendingOpen, setPendingOpen] = useState<boolean | null>(null);
|
||
const [error, setError] = useState('');
|
||
|
||
useEffect(() => {
|
||
request('SHOP_H5', '/shop/dashboard').then((d) => {
|
||
const s = d.store as Record<string, unknown>;
|
||
setStore(s);
|
||
const status = String(s?.status || '');
|
||
setPermanentlyClosed(status === 'CLOSED');
|
||
setOpen(status === 'OPEN');
|
||
if (s?.updatedAt) {
|
||
setLastUpdate(new Date(String(s.updatedAt)).toLocaleString('zh-CN'));
|
||
}
|
||
});
|
||
}, []);
|
||
|
||
function requestToggle(next: boolean) {
|
||
if (permanentlyClosed) return;
|
||
if (next === open) return;
|
||
setPendingOpen(next);
|
||
setShowModal(true);
|
||
}
|
||
|
||
async function confirmToggle() {
|
||
if (pendingOpen === null || permanentlyClosed) return;
|
||
const next = pendingOpen ? 'OPEN' : 'PAUSED';
|
||
try {
|
||
setError('');
|
||
await request('SHOP_H5', '/shop/store/status', {
|
||
method: 'PUT',
|
||
body: JSON.stringify({ status: next }),
|
||
});
|
||
setOpen(pendingOpen);
|
||
setLastUpdate(new Date().toLocaleString('zh-CN'));
|
||
} catch (e) {
|
||
setError(e instanceof Error ? e.message : '状态切换失败');
|
||
} finally {
|
||
setShowModal(false);
|
||
setPendingOpen(null);
|
||
}
|
||
}
|
||
|
||
const hoursText = formatShopHours(store);
|
||
const statusLabel = permanentlyClosed ? '永久关闭' : open ? '营业中' : '临时闭店';
|
||
|
||
return (
|
||
<div className="shop-status-page">
|
||
<header className="shop-status-header app-page-header">
|
||
<button type="button" className="app-page-header-action shop-redeem-back" onClick={() => navigate(-1)} aria-label="返回">
|
||
<span className="material-symbols-outlined">arrow_back</span>
|
||
</button>
|
||
<h1 className="app-page-title">门店管理</h1>
|
||
<button
|
||
type="button"
|
||
className="shop-status-logout app-page-header-action app-page-header-action--end"
|
||
onClick={() => { resetSession(); navigate('/login'); }}
|
||
>
|
||
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>logout</span>
|
||
退出登录
|
||
</button>
|
||
</header>
|
||
|
||
<div className="shop-status-content">
|
||
<section className="shop-status-card">
|
||
<div className="shop-status-icon-wrap">
|
||
<div className={`shop-status-icon-outer${open && !permanentlyClosed ? ' open' : ' closed'}`}>
|
||
<div className={`shop-status-icon-inner${open && !permanentlyClosed ? ' open' : ' closed'}`}>
|
||
<span className="material-symbols-outlined">storefront</span>
|
||
</div>
|
||
</div>
|
||
<span className="shop-status-check">
|
||
<span
|
||
className="material-symbols-outlined shop-fill-icon"
|
||
style={{
|
||
fontSize: 14,
|
||
color: open && !permanentlyClosed ? 'var(--color-success-green)' : 'var(--color-subtle-gray)',
|
||
}}
|
||
>
|
||
{open && !permanentlyClosed ? 'check_circle' : 'cancel'}
|
||
</span>
|
||
</span>
|
||
</div>
|
||
|
||
<h2 className={`shop-status-label${open && !permanentlyClosed ? ' open' : ' closed'}`}>
|
||
{statusLabel}
|
||
</h2>
|
||
|
||
{permanentlyClosed ? (
|
||
<p className="shop-status-switch-caption" style={{ marginTop: 12 }}>
|
||
总部已永久关闭本店,门店端无法自行恢复营业
|
||
</p>
|
||
) : (
|
||
<label className={`shop-status-switch${open ? ' open' : ' closed'}`}>
|
||
<input
|
||
type="checkbox"
|
||
checked={open}
|
||
onChange={(e) => requestToggle(e.target.checked)}
|
||
aria-label={open ? '切换为临时闭店' : '切换为营业中'}
|
||
/>
|
||
<span className="shop-status-switch-track" />
|
||
<span className="shop-status-switch-caption">
|
||
{open ? '点击可临时闭店' : '点击恢复营业'}
|
||
</span>
|
||
</label>
|
||
)}
|
||
|
||
<p className="shop-status-hours-label">营业时间</p>
|
||
<p className="shop-status-hours">{hoursText}</p>
|
||
{lastUpdate && <p className="shop-status-updated">最后修改于 {lastUpdate}</p>}
|
||
{error ? <p className="shop-status-updated" style={{ color: 'var(--color-error, #c62828)' }}>{error}</p> : null}
|
||
</section>
|
||
|
||
<div className={`shop-status-hint${open && !permanentlyClosed ? ' open' : ' closed'}`}>
|
||
<span className="material-symbols-outlined">info</span>
|
||
<p>
|
||
{permanentlyClosed
|
||
? '门店已永久关闭。如需重新营业,请联系总部或合伙人处理。'
|
||
: open
|
||
? '当前处于营业状态,用户可在您的门店核销餐券。'
|
||
: '当前处于临时闭店状态,用户将无法看到您的门店或进行核销。'}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{showModal && (
|
||
<div className="shop-status-modal" role="dialog" aria-modal="true">
|
||
<div className="shop-status-modal-card">
|
||
<h4 className="shop-status-modal-title">确认切换状态?</h4>
|
||
<p className="shop-status-modal-desc">
|
||
{pendingOpen
|
||
? '切换至“营业中”后,用户可正常选择本店核销餐券。'
|
||
: '切换至“临时闭店”后,用户将无法选择本店核销餐券。'}
|
||
</p>
|
||
<div className="shop-status-modal-actions">
|
||
<button type="button" className="shop-status-modal-cancel" onClick={() => { setShowModal(false); setPendingOpen(null); }}>
|
||
取消
|
||
</button>
|
||
<button type="button" className="shop-status-modal-confirm" onClick={confirmToggle}>
|
||
确认切换
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|