first
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
'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',
|
||||
};
|
||||
Reference in New Issue
Block a user