[dyad] Improved form UX - wrote 1 file(s)
This commit is contained in:
@@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Globe, Edit, Check } from "lucide-react";
|
import { Globe, Edit, Check, Loader2, X, ImageOff } from "lucide-react";
|
||||||
import { extractMetaData } from "@/app/actions";
|
import { extractMetaData } from "@/app/actions";
|
||||||
import { LengthIndicator } from "./length-indicator";
|
import { LengthIndicator } from "./length-indicator";
|
||||||
import { CopyButton } from "./copy-button";
|
import { CopyButton } from "./copy-button";
|
||||||
@@ -17,6 +17,7 @@ interface MetaData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function MetaForm() {
|
export function MetaForm() {
|
||||||
|
const [url, setUrl] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [metaData, setMetaData] = useState<MetaData | null>(null);
|
const [metaData, setMetaData] = useState<MetaData | null>(null);
|
||||||
@@ -26,11 +27,13 @@ export function MetaForm() {
|
|||||||
|
|
||||||
const [editableTitle, setEditableTitle] = useState("");
|
const [editableTitle, setEditableTitle] = useState("");
|
||||||
const [editableDescription, setEditableDescription] = useState("");
|
const [editableDescription, setEditableDescription] = useState("");
|
||||||
|
const [imageError, setImageError] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (metaData) {
|
if (metaData) {
|
||||||
setEditableTitle(metaData.title);
|
setEditableTitle(metaData.title);
|
||||||
setEditableDescription(metaData.description);
|
setEditableDescription(metaData.description);
|
||||||
|
setImageError(false);
|
||||||
}
|
}
|
||||||
}, [metaData]);
|
}, [metaData]);
|
||||||
|
|
||||||
@@ -41,9 +44,7 @@ export function MetaForm() {
|
|||||||
setMetaData(null);
|
setMetaData(null);
|
||||||
setIsEditingTitle(false);
|
setIsEditingTitle(false);
|
||||||
setIsEditingDescription(false);
|
setIsEditingDescription(false);
|
||||||
|
setImageError(false);
|
||||||
const formData = new FormData(event.currentTarget);
|
|
||||||
const url = formData.get("url") as string;
|
|
||||||
|
|
||||||
const result = await extractMetaData(url);
|
const result = await extractMetaData(url);
|
||||||
|
|
||||||
@@ -56,6 +57,13 @@ export function MetaForm() {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
setUrl("");
|
||||||
|
setLoading(false);
|
||||||
|
setError(null);
|
||||||
|
setMetaData(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full space-y-6">
|
<div className="w-full space-y-6">
|
||||||
<form
|
<form
|
||||||
@@ -70,15 +78,30 @@ export function MetaForm() {
|
|||||||
placeholder="https://example.com"
|
placeholder="https://example.com"
|
||||||
required
|
required
|
||||||
className="pl-10 h-12 text-base rounded-lg shadow-sm"
|
className="pl-10 h-12 text-base rounded-lg shadow-sm"
|
||||||
|
value={url}
|
||||||
|
onChange={(e) => setUrl(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full sm:w-auto h-12 px-8 rounded-lg font-semibold transition-all"
|
className="w-full sm:w-auto h-12 px-8 rounded-lg font-semibold transition-all flex items-center gap-2"
|
||||||
>
|
>
|
||||||
|
{loading && <Loader2 className="h-5 w-5 animate-spin" />}
|
||||||
{loading ? "Extracting..." : "Extract"}
|
{loading ? "Extracting..." : "Extract"}
|
||||||
</Button>
|
</Button>
|
||||||
|
{(url || metaData || error) && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={handleClear}
|
||||||
|
className="h-12 w-12"
|
||||||
|
>
|
||||||
|
<X className="h-5 w-5" />
|
||||||
|
<span className="sr-only">Clear</span>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
@@ -102,12 +125,20 @@ export function MetaForm() {
|
|||||||
<h3 className="font-semibold text-card-foreground mb-2">
|
<h3 className="font-semibold text-card-foreground mb-2">
|
||||||
Preview Image
|
Preview Image
|
||||||
</h3>
|
</h3>
|
||||||
<div className="aspect-video bg-muted rounded-md overflow-hidden relative">
|
<div className="aspect-video bg-muted rounded-md overflow-hidden relative flex items-center justify-center">
|
||||||
|
{!imageError ? (
|
||||||
<img
|
<img
|
||||||
src={metaData.image}
|
src={metaData.image}
|
||||||
alt="Meta preview image"
|
alt="Meta preview image"
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
|
onError={() => setImageError(true)}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center gap-2 text-muted-foreground">
|
||||||
|
<ImageOff className="h-8 w-8" />
|
||||||
|
<span>Image not available</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user