first
CI/CD / test-and-build (push) Has been cancelled
CI/CD / deploy (push) Has been cancelled

This commit is contained in:
Yjoz
2026-04-10 15:31:59 +04:00
parent 87d2688eeb
commit ec0f991a30
55 changed files with 10218 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
'use client';
import { useCallback, useEffect } from 'react';
import { useOrderStore, orderStore, type CheckoutStep } from '@/store/useOrderStore';
import { ShippingStep } from './checkout/ShippingStep';
import { PaymentStep } from './checkout/PaymentStep';
import { ReviewStep } from './checkout/ReviewStep';
import { ConfirmationStep } from './checkout/ConfirmationStep';
const STEPS: { id: CheckoutStep; label: string }[] = [
{ id: 'shipping', label: 'Shipping' },
{ id: 'payment', label: 'Payment' },
{ id: 'review', label: 'Review' },
];
function getStepIndex(step: CheckoutStep): number {
return STEPS.findIndex((s) => s.id === step);
}
export function CheckoutOverlay() {
const step = useOrderStore((s) => s.step);
const handleClose = useCallback(() => {
orderStore.getState().resetOrder();
}, []);
const handleBack = useCallback(() => {
const currentIndex = getStepIndex(step);
if (currentIndex > 0) {
orderStore.getState().setStep(STEPS[currentIndex - 1].id);
} else {
orderStore.getState().setStep('config');
}
}, [step]);
// Close on Escape key
useEffect(() => {
if (step === 'config') return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
handleClose();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [step, handleClose]);
if (step === 'config') return null;
return (
<div
role="dialog"
aria-modal="true"
aria-label="Checkout"
style={{
position: 'fixed',
inset: 0,
zIndex: 100,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'rgba(255, 255, 255, 0.92)',
backdropFilter: 'blur(12px)',
}}
>
<div style={{
width: '100%',
maxWidth: '560px',
maxHeight: '90vh',
overflowY: 'auto',
margin: '1rem',
background: 'rgba(255, 255, 255, 0.95)',
border: '1px solid rgba(59, 130, 246, 0.08)',
borderRadius: '1rem',
boxShadow: '0 24px 80px rgba(0, 0, 0, 0.5), 0 0 40px rgba(59, 130, 246, 0.1)',
}}>
{/* Header with progress */}
{step !== 'confirmed' && (
<div style={{ padding: '1.25rem 1.5rem', borderBottom: '1px solid rgba(59, 130, 246, 0.1)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
<button
onClick={handleBack}
style={{
padding: '0.35rem 0.75rem',
borderRadius: '0.375rem',
border: '1px solid rgba(0, 0, 0, 0.08)',
background: 'transparent',
color: '#94a3b8',
cursor: 'pointer',
fontSize: '0.75rem',
transition: 'all 0.2s ease',
}}
>
Back
</button>
<h2 style={{ fontSize: '0.9rem', fontWeight: 600, color: '#1a1a2e', margin: 0 }}>
Checkout
</h2>
<button
onClick={handleClose}
style={{
width: '28px',
height: '28px',
borderRadius: '50%',
border: '1px solid rgba(0, 0, 0, 0.08)',
background: 'transparent',
color: '#94a3b8',
cursor: 'pointer',
fontSize: '0.9rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
aria-label="Close checkout"
>
x
</button>
</div>
{/* Progress bar */}
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
{STEPS.map((s, i) => {
const currentIdx = getStepIndex(step);
const isActive = i === currentIdx;
const isComplete = i < currentIdx;
return (
<div key={s.id} style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
<div style={{
height: '3px',
borderRadius: '2px',
background: isComplete
? '#3b82f6'
: isActive
? 'linear-gradient(90deg, #3b82f6, rgba(59, 130, 246, 0.2))'
: 'rgba(0, 0, 0, 0.06)',
transition: 'all 0.3s ease',
}} />
<span style={{
fontSize: '0.65rem',
color: isActive ? '#2563eb' : isComplete ? '#3b82f6' : '#64748b',
fontWeight: isActive ? 600 : 400,
textAlign: 'center',
}}>
{s.label}
</span>
</div>
);
})}
</div>
</div>
)}
{/* Step Content */}
<div style={{ padding: '1.5rem' }}>
{step === 'shipping' && <ShippingStep />}
{step === 'payment' && <PaymentStep />}
{step === 'review' && <ReviewStep />}
{step === 'confirmed' && <ConfirmationStep />}
</div>
</div>
</div>
);
}