wip
This commit is contained in:
40
apps/companion/renderer/components/BrowserModeToggle.tsx
Normal file
40
apps/companion/renderer/components/BrowserModeToggle.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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' }}>
|
||||
|
||||
Reference in New Issue
Block a user