Najjar\NajjarV02 05b540997e feat: add admin authentication and management features
- Implemented Prisma schema with models for AdminUser, AppSettings, and Snapshot.
- Created seed script to initialize the database with an admin user and JWT secret.
- Developed admin login page with form handling and error management.
- Added API routes for admin login, logout, change password, and JWT verification.
- Integrated Stripe for payment intent management in admin orders.
- Established middleware for protecting admin routes with JWT authentication.
- Created Zustand stores for managing persona and snapshot states.
2026-04-13 17:57:59 +04:00

86 lines
2.3 KiB
TypeScript

'use client';
import { useState } from 'react';
import { PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';
import { orderStore } from '@/store/useOrderStore';
export function PaymentStep() {
const stripe = useStripe();
const elements = useElements();
const [errorMsg, setErrorMsg] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async () => {
if (!stripe || !elements) return;
setIsLoading(true);
setErrorMsg('');
// Validate the payment element first
const { error: submitError } = await elements.submit();
if (submitError) {
setErrorMsg(submitError.message || 'Validation failed');
setIsLoading(false);
return;
}
// Move to review — actual confirmation happens on "Place Order"
orderStore.getState().setPayment({ status: 'idle' });
orderStore.getState().setStep('review');
setIsLoading(false);
};
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<h3 style={{ fontSize: '1rem', fontWeight: 600, color: '#1a1a2e', margin: 0 }}>
Payment Details
</h3>
<div style={{
padding: '1rem',
borderRadius: '0.5rem',
background: '#fff',
border: '1px solid rgba(0, 0, 0, 0.08)',
}}>
<PaymentElement
options={{
layout: 'tabs',
}}
/>
</div>
{errorMsg && (
<div style={{
padding: '0.6rem 0.75rem',
borderRadius: '0.375rem',
background: 'rgba(239, 68, 68, 0.06)',
border: '1px solid rgba(239, 68, 68, 0.15)',
fontSize: '0.75rem',
color: '#dc2626',
}}>
{errorMsg}
</div>
)}
<button
onClick={handleSubmit}
disabled={!stripe || isLoading}
style={{
marginTop: '0.5rem',
padding: '0.75rem',
borderRadius: '0.375rem',
border: '1px solid rgba(59, 130, 246, 0.5)',
background: isLoading ? 'rgba(59, 130, 246, 0.15)' : 'rgba(59, 130, 246, 0.08)',
color: '#2563eb',
cursor: isLoading ? 'wait' : 'pointer',
fontSize: '0.85rem',
fontWeight: 600,
transition: 'all 0.2s ease',
}}
>
{isLoading ? 'Validating...' : 'Review Order'}
</button>
</div>
);
}