Good stuff

This commit is contained in:
Andras Schmelczer 2026-02-22 22:36:40 +00:00
parent 9da2db707f
commit 8032011708
32 changed files with 1052 additions and 374 deletions

View file

@ -1,19 +1,23 @@
import { useState, useCallback, useEffect } from 'react';
import { CheckIcon } from './icons/CheckIcon';
import { CloseIcon } from './icons/CloseIcon';
import { SpinnerIcon } from './icons/SpinnerIcon';
export default function SaveSearchModal({
onClose,
onSave,
onViewSearches,
saving,
error,
}: {
onClose: () => void;
onSave: (name: string) => Promise<void>;
onViewSearches: () => void;
saving: boolean;
error: string | null;
}) {
const [name, setName] = useState('');
const [saved, setSaved] = useState(false);
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
@ -21,12 +25,12 @@ export default function SaveSearchModal({
if (!name.trim() || saving) return;
try {
await onSave(name.trim());
onClose();
setSaved(true);
} catch {
// Error displayed in modal
}
},
[name, saving, onSave, onClose]
[name, saving, onSave]
);
useEffect(() => {
@ -45,7 +49,9 @@ export default function SaveSearchModal({
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between px-5 pt-5 pb-3">
<h2 className="text-lg font-semibold text-navy-950 dark:text-white">Save Search</h2>
<h2 className="text-lg font-semibold text-navy-950 dark:text-white">
{saved ? 'Search saved' : 'Save Search'}
</h2>
<button
onClick={onClose}
className="text-warm-400 hover:text-warm-700 dark:text-warm-400 dark:hover:text-warm-200"
@ -54,41 +60,68 @@ export default function SaveSearchModal({
</button>
</div>
<form onSubmit={handleSubmit} className="p-5 pt-2 space-y-4">
<div>
<label className="block text-sm font-medium text-warm-700 dark:text-warm-300 mb-1">
Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 text-sm rounded border border-warm-200 dark:border-warm-700 bg-white dark:bg-warm-800 text-navy-950 dark:text-white placeholder-warm-400 dark:placeholder-warm-500 outline-none focus:ring-2 ring-teal-400 dark:ring-teal-500"
placeholder="My search"
autoFocus
/>
{saved ? (
<div className="p-5 pt-2 space-y-4">
<div className="flex items-center gap-2 text-teal-600 dark:text-teal-400">
<CheckIcon className="w-5 h-5" />
<p className="text-sm text-warm-700 dark:text-warm-300">
Your search has been saved successfully.
</p>
</div>
<div className="flex gap-3 justify-end">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm rounded border border-warm-200 dark:border-warm-700 text-warm-700 dark:text-warm-300 hover:bg-warm-50 dark:hover:bg-warm-700"
>
Close
</button>
<button
type="button"
onClick={onViewSearches}
className="px-4 py-2 text-sm rounded bg-teal-600 text-white font-medium hover:bg-teal-700"
>
View saved searches
</button>
</div>
</div>
) : (
<form onSubmit={handleSubmit} className="p-5 pt-2 space-y-4">
<div>
<label className="block text-sm font-medium text-warm-700 dark:text-warm-300 mb-1">
Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 text-sm rounded border border-warm-200 dark:border-warm-700 bg-white dark:bg-warm-800 text-navy-950 dark:text-white placeholder-warm-400 dark:placeholder-warm-500 outline-none focus:ring-2 ring-teal-400 dark:ring-teal-500"
placeholder="My search"
autoFocus
/>
</div>
{error && <p className="text-sm text-red-600 dark:text-red-300">{error}</p>}
{error && <p className="text-sm text-red-600 dark:text-red-300">{error}</p>}
<div className="flex gap-3 justify-end">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm rounded border border-warm-200 dark:border-warm-700 text-warm-700 dark:text-warm-300 hover:bg-warm-50 dark:hover:bg-warm-700"
>
Cancel
</button>
<button
type="submit"
disabled={!name.trim() || saving}
className="flex items-center gap-2 px-4 py-2 text-sm rounded bg-teal-600 text-white font-medium hover:bg-teal-700 disabled:opacity-50 disabled:cursor-wait"
>
{saving && <SpinnerIcon className="w-4 h-4 animate-spin" />}
{saving ? 'Saving...' : 'Save'}
</button>
</div>
</form>
<div className="flex gap-3 justify-end">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm rounded border border-warm-200 dark:border-warm-700 text-warm-700 dark:text-warm-300 hover:bg-warm-50 dark:hover:bg-warm-700"
>
Cancel
</button>
<button
type="submit"
disabled={!name.trim() || saving}
className="flex items-center gap-2 px-4 py-2 text-sm rounded bg-teal-600 text-white font-medium hover:bg-teal-700 disabled:opacity-50 disabled:cursor-wait"
>
{saving && <SpinnerIcon className="w-4 h-4 animate-spin" />}
{saving ? 'Saving...' : 'Save'}
</button>
</div>
</form>
)}
</div>
</div>
);