215 lines
7.1 KiB
TypeScript
215 lines
7.1 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { pricingStore, usePricingStore } from '@/store/usePricingStore';
|
|
import Link from 'next/link';
|
|
|
|
export default function AdminPage() {
|
|
const items = usePricingStore((s) => s.items);
|
|
const isHydrated = usePricingStore((s) => s.isHydrated);
|
|
const [editedPrices, setEditedPrices] = useState<Record<string, number>>({});
|
|
const [saved, setSaved] = useState(false);
|
|
|
|
useEffect(() => {
|
|
pricingStore.getState().hydrate();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const map: Record<string, number> = {};
|
|
items.forEach((item) => {
|
|
map[item.id] = item.price;
|
|
});
|
|
setEditedPrices(map);
|
|
}, [items]);
|
|
|
|
const handlePriceChange = (id: string, value: string) => {
|
|
const num = parseInt(value.replace(/[^0-9]/g, ''), 10);
|
|
if (!isNaN(num)) {
|
|
setEditedPrices((prev) => ({ ...prev, [id]: num }));
|
|
} else if (value === '') {
|
|
setEditedPrices((prev) => ({ ...prev, [id]: 0 }));
|
|
}
|
|
};
|
|
|
|
const handleSave = () => {
|
|
Object.entries(editedPrices).forEach(([id, price]) => {
|
|
pricingStore.getState().updatePrice(id, price);
|
|
});
|
|
setSaved(true);
|
|
setTimeout(() => setSaved(false), 2000);
|
|
};
|
|
|
|
const handleReset = () => {
|
|
pricingStore.getState().resetPrices();
|
|
setSaved(false);
|
|
};
|
|
|
|
const formatPrice = (price: number) => {
|
|
return new Intl.NumberFormat('en-AE', { style: 'decimal' }).format(price);
|
|
};
|
|
|
|
if (!isHydrated) {
|
|
return (
|
|
<div style={pageStyle}>
|
|
<p style={{ color: '#64748b' }}>Loading...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={pageStyle}>
|
|
<div style={containerStyle}>
|
|
{/* Header */}
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '2rem' }}>
|
|
<div>
|
|
<h1 style={{ fontSize: '1.5rem', fontWeight: 700, color: '#1a1a2e', margin: 0, marginBottom: '0.25rem' }}>
|
|
Pricing Dashboard
|
|
</h1>
|
|
<p style={{ fontSize: '0.8rem', color: '#94a3b8', margin: 0 }}>
|
|
Edit prices for the G1 Robot Configurator
|
|
</p>
|
|
</div>
|
|
<Link
|
|
href="/"
|
|
style={{
|
|
padding: '0.5rem 1rem',
|
|
borderRadius: '0.375rem',
|
|
border: '1px solid rgba(59, 130, 246, 0.2)',
|
|
background: 'rgba(59, 130, 246, 0.06)',
|
|
color: '#2563eb',
|
|
fontSize: '0.8rem',
|
|
textDecoration: 'none',
|
|
transition: 'all 0.2s ease',
|
|
}}
|
|
>
|
|
Back to Configurator
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Pricing Table */}
|
|
<div style={{
|
|
background: 'rgba(255, 255, 255, 0.95)',
|
|
backdropFilter: 'blur(20px)',
|
|
border: '1px solid rgba(0, 0, 0, 0.06)',
|
|
borderRadius: '0.75rem',
|
|
overflow: 'hidden',
|
|
}}>
|
|
{/* Table Header */}
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: '1fr 200px',
|
|
padding: '0.75rem 1.25rem',
|
|
borderBottom: '1px solid rgba(0, 0, 0, 0.04)',
|
|
background: 'rgba(248, 248, 246, 0.5)',
|
|
}}>
|
|
<span style={{ fontSize: '0.7rem', fontWeight: 600, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
|
Item
|
|
</span>
|
|
<span style={{ fontSize: '0.7rem', fontWeight: 600, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em', textAlign: 'right' }}>
|
|
Price (AED)
|
|
</span>
|
|
</div>
|
|
|
|
{/* Rows */}
|
|
{items.map((item, index) => (
|
|
<div
|
|
key={item.id}
|
|
style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: '1fr 200px',
|
|
padding: '1rem 1.25rem',
|
|
alignItems: 'center',
|
|
borderBottom: index < items.length - 1 ? '1px solid rgba(0, 0, 0, 0.04)' : 'none',
|
|
transition: 'background 0.15s ease',
|
|
}}
|
|
>
|
|
<div>
|
|
<div style={{ fontSize: '0.875rem', color: '#374151', fontWeight: 500 }}>
|
|
{item.label}
|
|
</div>
|
|
<div style={{ fontSize: '0.7rem', color: '#94a3b8', fontFamily: 'monospace' }}>
|
|
{item.id}
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '0.5rem' }}>
|
|
<span style={{ fontSize: '0.75rem', color: '#94a3b8' }}>AED</span>
|
|
<input
|
|
type="text"
|
|
value={formatPrice(editedPrices[item.id] ?? item.price)}
|
|
onChange={(e) => handlePriceChange(item.id, e.target.value)}
|
|
style={{
|
|
width: '130px',
|
|
padding: '0.5rem 0.75rem',
|
|
borderRadius: '0.375rem',
|
|
border: '1px solid rgba(0, 0, 0, 0.1)',
|
|
background: 'rgba(255, 255, 255, 1)',
|
|
color: '#1a1a2e',
|
|
fontSize: '0.875rem',
|
|
fontFamily: 'monospace',
|
|
textAlign: 'right',
|
|
outline: 'none',
|
|
transition: 'border-color 0.2s ease',
|
|
}}
|
|
onFocus={(e) => { e.currentTarget.style.borderColor = 'rgba(59, 130, 246, 0.5)'; }}
|
|
onBlur={(e) => { e.currentTarget.style.borderColor = 'rgba(0, 0, 0, 0.1)'; }}
|
|
aria-label={`Price for ${item.label}`}
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div style={{ display: 'flex', gap: '0.75rem', marginTop: '1.5rem', justifyContent: 'flex-end' }}>
|
|
<button
|
|
onClick={handleReset}
|
|
style={{
|
|
padding: '0.6rem 1.25rem',
|
|
borderRadius: '0.375rem',
|
|
border: '1px solid rgba(239, 68, 68, 0.2)',
|
|
background: 'rgba(239, 68, 68, 0.05)',
|
|
color: '#ef4444',
|
|
cursor: 'pointer',
|
|
fontSize: '0.8rem',
|
|
transition: 'all 0.2s ease',
|
|
}}
|
|
>
|
|
Reset to Defaults
|
|
</button>
|
|
<button
|
|
onClick={handleSave}
|
|
style={{
|
|
padding: '0.6rem 1.5rem',
|
|
borderRadius: '0.375rem',
|
|
border: '1px solid rgba(59, 130, 246, 0.3)',
|
|
background: saved ? 'rgba(34, 197, 94, 0.08)' : 'rgba(59, 130, 246, 0.08)',
|
|
color: saved ? '#16a34a' : '#2563eb',
|
|
cursor: 'pointer',
|
|
fontSize: '0.8rem',
|
|
fontWeight: 600,
|
|
transition: 'all 0.2s ease',
|
|
}}
|
|
>
|
|
{saved ? 'Saved!' : 'Save Prices'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const pageStyle: React.CSSProperties = {
|
|
minHeight: '100vh',
|
|
background: '#ffffff',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
padding: '2rem',
|
|
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
};
|
|
|
|
const containerStyle: React.CSSProperties = {
|
|
width: '100%',
|
|
maxWidth: '640px',
|
|
};
|