This commit is contained in:
2025-11-26 17:03:29 +01:00
parent ff3528e5ef
commit fef75008d8
147 changed files with 112370 additions and 5162 deletions

View File

@@ -0,0 +1,40 @@
import { useEffect, useState } from 'react';
export function BrowserModeToggle() {
const [mode, setMode] = useState<'headed' | 'headless'>('headed');
const [isDevelopment, setIsDevelopment] = useState(false);
useEffect(() => {
window.electronAPI.getBrowserMode().then(({ mode, isDevelopment }) => {
setMode(mode);
setIsDevelopment(isDevelopment);
});
}, []);
if (!isDevelopment) return null;
const handleToggle = async () => {
const newMode = mode === 'headed' ? 'headless' : 'headed';
const result = await window.electronAPI.setBrowserMode(newMode);
if (result.success) {
setMode(newMode);
}
};
return (
<div style={{ padding: '10px', borderTop: '1px solid #333' }}>
<label style={{ color: '#aaa', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '8px' }}>
<input
type="checkbox"
checked={mode === 'headless'}
onChange={handleToggle}
style={{ cursor: 'pointer' }}
/>
Headless Browser Mode
</label>
<div style={{ fontSize: '0.85rem', color: '#666', marginTop: '4px', marginLeft: '24px' }}>
{mode === 'headless' ? 'Browser runs in background' : 'Browser window visible'}
</div>
</div>
);
}

View File

@@ -0,0 +1,116 @@
/**
* CheckoutConfirmationDialog component
* Displays checkout information and requests user confirmation before proceeding.
*/
import React, { useEffect, useState } from 'react';
interface CheckoutConfirmationRequest {
price: string;
state: 'ready' | 'insufficient_funds';
sessionMetadata: {
sessionName: string;
trackId: string;
carIds: string[];
};
timeoutMs: number;
}
interface CheckoutConfirmationDialogProps {
request: CheckoutConfirmationRequest;
}
export const CheckoutConfirmationDialog: React.FC<CheckoutConfirmationDialogProps> = ({
request,
}) => {
const [remainingSeconds, setRemainingSeconds] = useState(
Math.floor(request.timeoutMs / 1000)
);
useEffect(() => {
// Countdown timer
const intervalId = setInterval(() => {
setRemainingSeconds((prev) => {
if (prev <= 1) {
clearInterval(intervalId);
window.electronAPI.confirmCheckout('timeout');
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(intervalId);
}, []);
const handleConfirm = () => {
window.electronAPI.confirmCheckout('confirmed');
};
const handleCancel = () => {
window.electronAPI.confirmCheckout('cancelled');
};
return (
<div className="checkout-confirmation-dialog">
<div className="dialog-overlay">
<div className="dialog-content">
<h2>Confirm Checkout</h2>
<div className="checkout-details">
<div className="price-section">
<span className="price-label">Price:</span>
<span className="price-value">{request.price}</span>
</div>
{request.state === 'insufficient_funds' && (
<div className="warning-section">
<span className="warning-text"> Insufficient funds</span>
</div>
)}
<div className="session-info">
<div className="info-row">
<span className="info-label">Session:</span>
<span className="info-value">{request.sessionMetadata.sessionName}</span>
</div>
<div className="info-row">
<span className="info-label">Track:</span>
<span className="info-value">{request.sessionMetadata.trackId}</span>
</div>
<div className="info-row">
<span className="info-label">Cars:</span>
<span className="info-value">
{request.sessionMetadata.carIds.join(', ')}
</span>
</div>
</div>
<div className="countdown-section">
<span className="countdown-text">
Time remaining: {remainingSeconds}s
</span>
</div>
</div>
<div className="dialog-actions">
<button
type="button"
className="btn btn-cancel"
onClick={handleCancel}
>
Cancel
</button>
<button
type="button"
className="btn btn-confirm"
onClick={handleConfirm}
>
Confirm
</button>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,78 @@
/**
* RaceCreationSuccessScreen component
* Displays the successful race creation result with session details.
*/
import React from 'react';
interface RaceCreationResult {
sessionId: string;
sessionName: string;
trackId: string;
carIds: string[];
finalPrice: string;
createdAt: Date;
}
interface RaceCreationSuccessScreenProps {
result: RaceCreationResult;
}
export const RaceCreationSuccessScreen: React.FC<RaceCreationSuccessScreenProps> = ({
result,
}) => {
return (
<div className="race-creation-success-screen">
<div className="success-container">
<div className="success-header">
<h2> Race Created Successfully!</h2>
</div>
<div className="success-details">
<div className="detail-section">
<h3>Session Information</h3>
<div className="detail-row">
<span className="detail-label">Session Name:</span>
<span className="detail-value">{result.sessionName}</span>
</div>
<div className="detail-row">
<span className="detail-label">Session ID:</span>
<span className="detail-value">{result.sessionId}</span>
</div>
</div>
<div className="detail-section">
<h3>Track & Cars</h3>
<div className="detail-row">
<span className="detail-label">Track:</span>
<span className="detail-value">{result.trackId}</span>
</div>
<div className="detail-row">
<span className="detail-label">Cars:</span>
<span className="detail-value">{result.carIds.join(', ')}</span>
</div>
</div>
<div className="detail-section">
<h3>Financial</h3>
<div className="detail-row">
<span className="detail-label">Final Price:</span>
<span className="detail-value price">{result.finalPrice}</span>
</div>
</div>
<div className="detail-section">
<h3>Created</h3>
<div className="detail-row">
<span className="detail-label">Timestamp:</span>
<span className="detail-value">
{result.createdAt.toISOString().split('T')[0]} at{' '}
{result.createdAt.toLocaleTimeString()}
</span>
</div>
</div>
</div>
</div>
</div>
);
};

View File

@@ -32,8 +32,7 @@ const STEP_NAMES: { [key: number]: string } = {
14: 'Set Time of Day',
15: 'Configure Weather',
16: 'Set Race Options',
17: 'Configure Team Driving',
18: 'Set Track Conditions'
17: 'Set Track Conditions'
};
export function SessionProgressMonitor({ sessionId, progress, isRunning }: SessionProgressMonitorProps) {
@@ -142,7 +141,7 @@ export function SessionProgressMonitor({ sessionId, progress, isRunning }: Sessi
)}
<div style={{ marginBottom: '1rem', color: '#aaa', fontSize: '14px' }}>
Progress: {progress.completedSteps.length} / 18 steps
Progress: {progress.completedSteps.length} / 17 steps
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>