164 lines
5.7 KiB
TypeScript
164 lines
5.7 KiB
TypeScript
"use client";
|
||
|
||
import React, { useState } from "react";
|
||
import { useDocumentInfo, toast } from "@payloadcms/ui";
|
||
|
||
type Action = "upscale" | "recover";
|
||
|
||
interface ActionState {
|
||
loading: boolean;
|
||
resultId?: string | number;
|
||
}
|
||
|
||
export const AiMediaButtons: React.FC = () => {
|
||
const { id } = useDocumentInfo();
|
||
|
||
const [upscale, setUpscale] = useState<ActionState>({ loading: false });
|
||
const [recover, setRecover] = useState<ActionState>({ loading: false });
|
||
|
||
if (!id) return null; // Only show on existing documents
|
||
|
||
const runAction = async (action: Action) => {
|
||
const setter = action === "upscale" ? setUpscale : setRecover;
|
||
setter({ loading: true });
|
||
|
||
const label = action === "upscale" ? "AI Upscale" : "AI Recover";
|
||
|
||
toast.info(
|
||
`${label} started – this can take 30–90 seconds, please wait…`,
|
||
);
|
||
|
||
try {
|
||
// The API path is hardcoded here and assuming that's where the host app registers the endpoint.
|
||
const response = await fetch(`/api/media/${id}/ai-process`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ action }),
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (!response.ok) {
|
||
throw new Error(result.error || `${label} failed`);
|
||
}
|
||
|
||
setter({ loading: false, resultId: result.mediaId });
|
||
|
||
toast.success(
|
||
`✅ ${label} erfolgreich! Neues Bild (ID: ${result.mediaId}) wurde gespeichert.`,
|
||
);
|
||
} catch (err: any) {
|
||
console.error(`[AiMediaButtons] ${action} error:`, err);
|
||
toast.error(
|
||
err instanceof Error ? err.message : `${label} fehlgeschlagen`,
|
||
);
|
||
setter({ loading: false });
|
||
}
|
||
};
|
||
|
||
const buttonStyle: React.CSSProperties = {
|
||
background: "var(--theme-elevation-150)",
|
||
border: "1px solid var(--theme-elevation-200)",
|
||
color: "var(--theme-text)",
|
||
padding: "8px 14px",
|
||
borderRadius: "4px",
|
||
fontSize: "13px",
|
||
fontWeight: 500,
|
||
display: "inline-flex",
|
||
alignItems: "center",
|
||
gap: "6px",
|
||
transition: "opacity 0.15s ease",
|
||
};
|
||
|
||
const disabledStyle: React.CSSProperties = {
|
||
opacity: 0.55,
|
||
cursor: "not-allowed",
|
||
};
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexWrap: "wrap",
|
||
gap: "10px",
|
||
marginBottom: "1.5rem",
|
||
marginTop: "0.5rem",
|
||
}}
|
||
>
|
||
{/* AI Upscale */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||
<button
|
||
type="button"
|
||
disabled={upscale.loading || recover.loading}
|
||
onClick={() => runAction("upscale")}
|
||
style={{
|
||
...buttonStyle,
|
||
...(upscale.loading || recover.loading ? disabledStyle : { cursor: "pointer" }),
|
||
}}
|
||
>
|
||
{upscale.loading ? "⏳ AI Upscale läuft…" : "✨ AI Upscale"}
|
||
</button>
|
||
{upscale.resultId && (
|
||
<a
|
||
href={`/admin/collections/media/${upscale.resultId}`}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
style={{
|
||
fontSize: "12px",
|
||
color: "var(--theme-elevation-500)",
|
||
textDecoration: "underline",
|
||
}}
|
||
>
|
||
→ Neues Bild öffnen (ID: {upscale.resultId})
|
||
</a>
|
||
)}
|
||
</div>
|
||
|
||
{/* AI Recover */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||
<button
|
||
type="button"
|
||
disabled={upscale.loading || recover.loading}
|
||
onClick={() => runAction("recover")}
|
||
style={{
|
||
...buttonStyle,
|
||
...(upscale.loading || recover.loading ? disabledStyle : { cursor: "pointer" }),
|
||
}}
|
||
>
|
||
{recover.loading ? "⏳ AI Recover läuft…" : "🔄 AI Recover"}
|
||
</button>
|
||
{recover.resultId && (
|
||
<a
|
||
href={`/admin/collections/media/${recover.resultId}`}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
style={{
|
||
fontSize: "12px",
|
||
color: "var(--theme-elevation-500)",
|
||
textDecoration: "underline",
|
||
}}
|
||
>
|
||
→ Neues Bild öffnen (ID: {recover.resultId})
|
||
</a>
|
||
)}
|
||
</div>
|
||
|
||
<p
|
||
style={{
|
||
width: "100%",
|
||
fontSize: "0.8rem",
|
||
color: "var(--theme-elevation-500)",
|
||
margin: 0,
|
||
lineHeight: 1.4,
|
||
}}
|
||
>
|
||
<strong>AI Upscale</strong> verbessert die Auflösung via{" "}
|
||
<code>google/upscaler</code>. <strong>AI Recover</strong> restauriert
|
||
alte/beschädigte Fotos via{" "}
|
||
<code>microsoft/bringing-old-photos-back-to-life</code>. Das
|
||
Ergebnis wird als neues Medium gespeichert.
|
||
</p>
|
||
</div>
|
||
);
|
||
};
|