Yjoz ec0f991a30
Some checks failed
CI/CD / test-and-build (push) Has been cancelled
CI/CD / deploy (push) Has been cancelled
first
2026-04-10 15:31:59 +04:00

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',
};