const { useState, useEffect, useCallback, useRef } = React;
const ProfileEditModal = ({ profile, onClose, onSaved }) => {
const { showToast } = useApp();
const overlayRef = useRef(null);
const contentRef = useRef(null);
const closingRef = useRef(false);
const [saving, setSaving] = useState(false);
const [form, setForm] = useState({
name: profile?.name || '',
company: profile?.company || '',
tax_id: profile?.tax_id || '',
contact_person: profile?.contact_person || '',
mobile: profile?.mobile || '',
phone: profile?.phone || '',
address: profile?.address || '',
});
const fieldLabels = [
{ key: 'name', label: '姓名' },
{ key: 'company', label: '公司' },
{ key: 'tax_id', label: '統一編號' },
{ key: 'contact_person', label: '聯絡人' },
{ key: 'mobile', label: '手機' },
{ key: 'phone', label: '電話' },
{ key: 'address', label: '地址' },
];
const animateClose = useCallback(() => {
if (closingRef.current) return;
closingRef.current = true;
const content = contentRef.current;
const overlay = overlayRef.current;
if (!content || !overlay) { onClose(); return; }
Motion.animate(content, { opacity: 0, transform: 'translateY(60px)' }, { duration: 0.2, easing: 'ease-in' })
.then(() => Motion.animate(overlay, { opacity: 0 }, { duration: 0.1 }))
.then(() => onClose());
}, [onClose]);
useEffect(() => {
const overlay = overlayRef.current;
const content = contentRef.current;
if (!overlay || !content) return;
Motion.animate(overlay, { opacity: [0, 1] }, { duration: 0.15 });
Motion.animate(content,
{ opacity: [0, 1], transform: ['translateY(60px) scale(0.97)', 'translateY(0px) scale(1)'] },
{ duration: 0.35, easing: [0.22, 1, 0.36, 1] }
);
const handleKey = (e) => { if (e.key === 'Escape') animateClose(); };
document.addEventListener('keydown', handleKey);
document.body.style.overflow = 'hidden';
return () => {
document.removeEventListener('keydown', handleKey);
document.body.style.overflow = '';
};
}, [animateClose]);
const handleBackdropClick = (e) => {
if (e.target === overlayRef.current) animateClose();
};
const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
const handleSave = async () => {
setSaving(true);
try {
const updated = await api.updateProfile(form);
showToast('資料已更新');
onSaved(updated);
onClose();
} catch (error) {
console.error('Profile update error:', error);
showToast('更新失敗,請稍後再試');
setSaving(false);
}
};
return (
{fieldLabels.map(({ key, label }) => (
{key === 'address' ? (
) : (
)}
))}
);
};
const ProfilePage = () => {
const { user, handleLogin, logout, authReady, showToast } = useApp();
const [profile, setProfile] = useState(null);
const [orders, setOrders] = useState([]);
const [loading, setLoading] = useState(true);
const [showEditModal, setShowEditModal] = useState(false);
const [cancellingOrder, setCancellingOrder] = useState(null);
const [confirmingOrder, setConfirmingOrder] = useState(null);
const [orderFilter, setOrderFilter] = useState('all');
useEffect(() => {
const fetchData = async () => {
if (!user?._authenticated) {
setLoading(false);
return;
}
try {
const [profileData, ordersData] = await Promise.all([
api.getProfile(),
api.getOrders()
]);
setProfile(profileData);
setOrders(ordersData);
} catch (error) {
console.error('Profile fetch error:', error);
}
setLoading(false);
};
fetchData();
}, [user]);
const handleCancelOrder = async (orderNumber) => {
if (!confirm('確定要取消此訂單嗎?')) return;
if (!user?._authenticated) return;
setCancellingOrder(orderNumber);
try {
const result = await api.cancelOrder(orderNumber);
setOrders(prev => prev.map(o =>
o.order_number === orderNumber ? result.order : o
));
showToast('訂單已取消');
} catch (error) {
showToast(error.message || '取消失敗');
}
setCancellingOrder(null);
};
const handleConfirmReceived = async (orderNumber) => {
if (!confirm('確認已收到商品嗎?')) return;
if (!user?._authenticated) return;
setConfirmingOrder(orderNumber);
try {
const result = await api.confirmReceived(orderNumber);
setOrders(prev => prev.map(o =>
o.order_number === orderNumber ? result.order : o
));
showToast('已確認收貨');
} catch (error) {
showToast(error.message || '確認收貨失敗');
}
setConfirmingOrder(null);
};
const statusText = {
pending: '待確認', pending_payment: '待付款', paid: '已付款',
confirmed: '已確認', shipped: '已出貨', ready_pickup: '待取貨',
completed: '已完成', cancelled: '已取消', rejected: '已拒絕'
};
const cancellableStatuses = new Set(['pending', 'pending_payment', 'confirmed']);
const receivableStatuses = new Set(['shipped', 'ready_pickup']);
const ORDER_FILTERS = [
{ key: 'all', label: '全部' },
{ key: 'pending', label: '待處理', statuses: ['pending', 'confirmed'] },
{ key: 'payment', label: '待付款', statuses: ['pending_payment'] },
{ key: 'shipping', label: '出貨中', statuses: ['paid', 'shipped', 'ready_pickup'] },
{ key: 'done', label: '已完成', statuses: ['completed', 'cancelled', 'rejected'] },
];
const filteredOrders = orderFilter === 'all'
? orders
: orders.filter(o => ORDER_FILTERS.find(f => f.key === orderFilter)?.statuses?.includes(o.status));
if (loading) return ;
if (!user) {
return (
請先登入
{authReady && (
)}
);
}
return (
{user.pictureUrl ? (

) : (
)}
{profile?.name || user.displayName}
{profile?.company &&
{profile.company}
}
{profile?.price_tier?.name || '一般客戶'}
{orders.length > 0 && (
歷史訂單
{ORDER_FILTERS.map(f => (
))}
{filteredOrders.map((order, index) => (
{order.order_number}
{statusText[order.status] || order.status}
{order.items?.map((item, i) => (
{item.product_name}
x{item.quantity}
NT$ {item.subtotal?.toLocaleString()}
))}
{PAYMENT_LABELS[order.payment_method] || order.payment_method}
{order.payment_method === 'store_pickup' && order.shipping_address && (
{order.shipping_address}
)}
NT$ {order.total_amount?.toLocaleString()}
{new Date(order.created_at).toLocaleString('zh-TW')}
{receivableStatuses.has(order.status) && (
)}
{cancellableStatuses.has(order.status) && (
)}
))}
)}
{showEditModal && (
setShowEditModal(false)}
onSaved={(updated) => setProfile(updated)}
/>
)}
);
};