Skip to content
28 changes: 15 additions & 13 deletions public/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Redirecting...</title>
<title>Loading...</title>
<script>
// Single Page Application redirect for GitHub Pages
// This redirects all routes to index.html so React Router can handle them
const pathSegmentsToKeep = 1;
const l = window.location;
l.replace(
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') +
'/?/' + l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
);
// Surge serves 404.html for unknown routes unless a 200 fallback is provided.
// Instead of mutating the URL (which produced /?/… redirects), load the main
// SPA shell in-place so React Router can hydrate the correct route.
fetch('/index.html', { credentials: 'same-origin' })
.then((response) => response.text())
.then((html) => {
document.open();
document.write(html);
document.close();
})
.catch(() => {
window.location.replace('/');
});
</script>
</head>
<body>
<p>Redirecting to the app...</p>
<p>Loading the application…</p>
</body>
</html>
14 changes: 12 additions & 2 deletions src/components/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const LoginForm: React.FC = () => {
const [showPassword, setShowPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [selectedUser, setSelectedUser] = useState<string>('');
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { login } = useUser();
const navigate = useNavigate();

Expand All @@ -23,15 +24,16 @@ const LoginForm: React.FC = () => {
const matchedUser = userProfiles.find(u => u.email === email && u.password === password);
if (!matchedUser) {
setIsLoading(false);
alert('Login failed. Please check your credentials.');
setErrorMessage('Login failed. Please check your credentials.');
return;
}
const success = await login(matchedUser.userId.toString(), email);
setIsLoading(false);
if (success) {
setErrorMessage(null);
navigate('/products');
} else {
alert('Login failed. Please check your credentials.');
setErrorMessage('Login failed. Please check your credentials.');
}
};

Expand All @@ -51,6 +53,14 @@ const LoginForm: React.FC = () => {

{/* Quick user selector keeps demo credentials handy */}
<form onSubmit={handleSubmit} className="space-y-6">
{errorMessage && (
<div
id="login-error"
className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm font-medium text-red-700"
>
{errorMessage}
</div>
)}
<div>
<label className="block text-sm font-medium mb-2 text-gray-700">Quick Select User</label>
<select
Expand Down
10 changes: 0 additions & 10 deletions src/data/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,5 @@ export const products = [
description: 'Samsung Galaxy S20+ with Snapdragon 865, 6.7-inch Dynamic AMOLED, quad camera system.',
image: getAssetPath('/static/products/samsung-S20+.png'),
rating: { rate: 4.7, count: 160 }
},
{
id: 10,
title: 'Samsung Galaxy S20 Ultra',
price: 999.99,
category: 'android',
os: 'android',
description: 'Samsung Galaxy S20 Ultra with Snapdragon 865, 6.9-inch Dynamic AMOLED, 108MP camera.',
image: getAssetPath('/static/products/samsung-S20Ultra.png'),
rating: { rate: 4.8, count: 175 }
}
];
84 changes: 66 additions & 18 deletions src/pages/Scenarios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,58 @@ import { Toaster } from '../components/ui/toaster';
const Scenarios: React.FC = () => {
const [toggleState, setToggleState] = useState(false);
const [progressValue, setProgressValue] = useState(45);
const [lastCopiedLink, setLastCopiedLink] = useState('');
const toastStyles = 'border-2 shadow-lg rounded-lg px-4 py-3 font-semibold transition-all duration-200 bg-blue-100 border-blue-300 text-blue-800';

const showCopyToast = (description: string) =>
toast({
title: 'Link copied',
description,
className: toastStyles,
duration: 800
});

const fallbackCopyToClipboard = (value: string) => {
const textarea = document.createElement('textarea');
textarea.value = value;
textarea.setAttribute('readonly', '');
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
} catch (error) {
console.warn('Clipboard fallback failed', error);
}
document.body.removeChild(textarea);
};

const handleCopyShareLink = () => {
const text = document.getElementById('share-link')?.textContent?.trim();
if (!text) {
return;
}

if (navigator.clipboard?.writeText) {
navigator.clipboard
.writeText(text)
.then(() => {
setLastCopiedLink(text);
showCopyToast('The scenario URL is ready to share.');
})
.catch(() => {
fallbackCopyToClipboard(text);
setLastCopiedLink(text);
showCopyToast('Copied using fallback clipboard support.');
});
} else {
fallbackCopyToClipboard(text);
setLastCopiedLink(text);
showCopyToast('Copied using fallback clipboard support.');
}
};

return (
<div className="min-h-screen py-8">
<Toaster />
Expand All @@ -21,18 +71,18 @@ const Scenarios: React.FC = () => {
</p>
</div>

<div className="grid lg:grid-cols-2 gap-8">
<div className="grid gap-8 md:grid-cols-1 lg:grid-cols-2">
<Card className="bg-white p-8 rounded-xl shadow-lg">
<div className="flex items-center gap-3 mb-6">
<div className="p-2 bg-blue-100 text-blue-600 rounded-lg">
<CheckCircle className="w-6 h-6" />
</div>
<div>
<h3 className="text-xl font-bold">Form Field Target</h3>
<p className="text-gray-600">Demonstrate how your flows handle predictable customer reference fields.</p>
<h3 className="text-xl font-bold">Static ID Field</h3>
<p className="text-gray-600">Demonstrate how your scripts store and reuse customer references.</p>
</div>
</div>
<div className="space-y-2">
<div className="space-y-4">
<label htmlFor="static-id-field" className="text-sm font-medium text-gray-600">Customer reference</label>
<input
type="text"
Expand Down Expand Up @@ -92,7 +142,7 @@ const Scenarios: React.FC = () => {
title: 'Demo notification',
description: 'Content Description button clicked!',
className: toastStyles,
duration: 500
duration: 5000
})}
>
Submit
Expand All @@ -116,22 +166,20 @@ const Scenarios: React.FC = () => {
</div>
<button
className="w-full py-3 px-4 rounded-lg font-medium transition-all bg-blue-600 hover:bg-blue-700 text-white"
onClick={() => {
const text = document.getElementById('share-link')?.textContent?.trim();
if (text) {
navigator.clipboard.writeText(text).then(() => {
toast({
title: 'Link copied',
description: 'The scenario URL is ready to share.',
className: toastStyles,
duration: 3000
});
});
}
}}
onClick={handleCopyShareLink}
>
Copy share link
</button>
{lastCopiedLink && (
<div
id="share-copy-result"
className="mt-3 rounded-lg border border-blue-100 bg-blue-50 px-3 py-2 text-sm font-medium text-blue-700"
aria-live="polite"
data-automation="copy-result"
>
Last copied: <span className="font-mono">{lastCopiedLink}</span>
</div>
)}
</div>
</Card>

Expand Down