feat: add save as site logo button to logo generator

This commit is contained in:
openhands committed 2026-07-03 16:45:43 +02:00
1 parent 98506fea1e
commit af2d5b4943
2 files changed
+95 -1

No files matched your search

+56 -1
View File
@@ -1,7 +1,8 @@
"use client";
import { useCallback, useMemo, useRef, useState } from "react";
import { useCallback, useMemo, useRef, useState, useTransition } from "react";
import { ContentCard } from "@/components/public/ui";
import { saveLogo } from "@/actions/save-logo";
/**
* Client-side logo generator. Lets the visitor type a hotel name, pick a fun
@@ -38,6 +39,8 @@ export default function LogoGenerator() {
const [bold, setBold] = useState(true);
const linkRef = useRef<HTMLAnchorElement | null>(null);
const [saveStatus, setSaveStatus] = useState<"idle" | "saving" | "done" | "error">("idle");
const [, startTransition] = useTransition();
const safeText = text.trim().length > 0 ? text : "Your hotel";
const fontWeight = bold ? "bold" : "normal";
@@ -109,6 +112,50 @@ export default function LogoGenerator() {
}, "image/png");
}, [bgColor, bgEnabled, fontStack, fontWeight, safeText, size, textColor]);
const handleSave = useCallback(() => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) return;
const fontSpec = `${fontWeight} ${size}px ${fontStack}`;
ctx.font = fontSpec;
const metrics = ctx.measureText(safeText);
const padX = Math.round(size * 0.4);
const padY = Math.round(size * 0.45);
const textWidth = Math.max(1, Math.ceil(metrics.width));
const width = textWidth + padX * 2;
const height = Math.ceil(size * 1.35) + padY * 2;
const dpr = Math.min(typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1, 3);
canvas.width = Math.round(width * dpr);
canvas.height = Math.round(height * dpr);
ctx.scale(dpr, dpr);
if (bgEnabled) {
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, width, height);
}
ctx.font = fontSpec;
ctx.fillStyle = textColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(safeText, width / 2, height / 2);
setSaveStatus("saving");
canvas.toBlob(async (blob) => {
if (!blob) { setSaveStatus("error"); return; }
const file = new File([blob], "logo.png", { type: "image/png" });
const fd = new FormData();
fd.append("file", file);
startTransition(async () => {
const result = await saveLogo(fd);
setSaveStatus(result.success ? "done" : "error");
if (result.success) setTimeout(() => setSaveStatus("idle"), 3000);
});
}, "image/png");
}, [bgColor, bgEnabled, fontStack, fontWeight, safeText, size, textColor, startTransition]);
return (
<div className="card-grid md-2" style={{ alignItems: "start" }}>
{/* ── Controls ──────────────────────────────── */}
@@ -206,6 +253,14 @@ export default function LogoGenerator() {
<button type="button" className="btn btn-primary" onClick={handleDownload}>
⬇️ Download PNG
</button>
<button
type="button"
className="btn btn-primary"
onClick={handleSave}
disabled={saveStatus === "saving"}
>
{saveStatus === "saving" ? "⏳ Saving..." : saveStatus === "done" ? "✅ Saved!" : saveStatus === "error" ? "❌ Error" : "💾 Save as site logo"}
</button>
{/* Reused, hidden anchor used to trigger the file download. */}
<a ref={linkRef} style={{ display: "none" }} aria-hidden href="/">
download