Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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