140 lines
5.4 KiB
TypeScript
140 lines
5.4 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useStoreSession } from '../contexts/StoreSessionContext';
|
|
import { request } from '../lib/api';
|
|
|
|
export default function StatusPage() {
|
|
const navigate = useNavigate();
|
|
const { resetSession } = useStoreSession();
|
|
const [open, setOpen] = useState(true);
|
|
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);
|
|
|
|
useEffect(() => {
|
|
request('SHOP_H5', '/shop/dashboard').then((d) => {
|
|
const s = d.store as Record<string, unknown>;
|
|
setStore(s);
|
|
setOpen(String(s?.status) === 'OPEN');
|
|
if (s?.updatedAt) {
|
|
setLastUpdate(new Date(String(s.updatedAt)).toLocaleString('zh-CN'));
|
|
}
|
|
});
|
|
}, []);
|
|
|
|
function requestToggle(next: boolean) {
|
|
if (next === open) return;
|
|
setPendingOpen(next);
|
|
setShowModal(true);
|
|
}
|
|
|
|
async function confirmToggle() {
|
|
if (pendingOpen === null) return;
|
|
const next = pendingOpen ? 'OPEN' : 'PAUSED';
|
|
try {
|
|
await request('SHOP_H5', '/shop/store/status', {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ status: next }),
|
|
});
|
|
setOpen(pendingOpen);
|
|
setLastUpdate(new Date().toLocaleString('zh-CN'));
|
|
} catch {
|
|
/* keep current state */
|
|
} finally {
|
|
setShowModal(false);
|
|
setPendingOpen(null);
|
|
}
|
|
}
|
|
|
|
const openTime = String(store?.openTime || '09:30');
|
|
const closeTime = String(store?.closeTime || '22:00');
|
|
|
|
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 ? ' open' : ' closed'}`}>
|
|
<div className={`shop-status-icon-inner${open ? ' open' : ' closed'}`}>
|
|
<span className="material-symbols-outlined">storefront</span>
|
|
</div>
|
|
</div>
|
|
<span className="shop-status-check">
|
|
<span className={`material-symbols-outlined shop-fill-icon${open ? '' : ''}`} style={{ fontSize: 14, color: open ? 'var(--color-success-green)' : 'var(--color-subtle-gray)' }}>
|
|
{open ? 'check_circle' : 'cancel'}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
|
|
<h2 className={`shop-status-label${open ? ' open' : ' closed'}`}>
|
|
{open ? '营业中' : '临时闭店'}
|
|
</h2>
|
|
|
|
<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">{openTime} - {closeTime}</p>
|
|
{lastUpdate && <p className="shop-status-updated">最后修改于 {lastUpdate}</p>}
|
|
</section>
|
|
|
|
<div className={`shop-status-hint${open ? ' open' : ' closed'}`}>
|
|
<span className="material-symbols-outlined">info</span>
|
|
<p>
|
|
{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>
|
|
);
|
|
}
|