feat(guides): organize schemas and export teams
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { ImageResponse } from "next/og";
|
||||
|
||||
import { getPublicTeamExport } from "@/lib/guides/queries";
|
||||
|
||||
const anuphanFont = fetch(
|
||||
"https://raw.githubusercontent.com/google/fonts/main/ofl/anuphan/Anuphan%5Bwght%5D.ttf",
|
||||
)
|
||||
.then((response) => (response.ok ? response.arrayBuffer() : null))
|
||||
.catch(() => null);
|
||||
|
||||
function amount(value: number): string {
|
||||
return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(value);
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
_request: Request,
|
||||
context: RouteContext<"/api/teams/[teamId]/image">,
|
||||
) {
|
||||
const { teamId } = await context.params;
|
||||
const team = await getPublicTeamExport(teamId);
|
||||
if (!team) return new Response("Team not found", { status: 404 });
|
||||
|
||||
const total = team.members.reduce(
|
||||
(sum, member) => sum + Number(member.damage),
|
||||
0,
|
||||
);
|
||||
const seconds = Number(team.rotationSeconds);
|
||||
const dps = seconds > 0 ? total / seconds : 0;
|
||||
const font = await anuphanFont;
|
||||
|
||||
return new ImageResponse(
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
color: "#f4fbfa",
|
||||
background:
|
||||
"radial-gradient(circle at 50% 0%, #16454a 0%, #0a2429 38%, #06171b 100%)",
|
||||
border: "2px solid #3aaaa2",
|
||||
padding: "34px 36px",
|
||||
fontFamily: "Anuphan",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", textAlign: "center" }}>
|
||||
<div style={{ display: "flex", justifyContent: "center", fontSize: 42, fontWeight: 700 }}>
|
||||
{team.guideName}
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "center", color: "#82d6ce", fontSize: 20 }}>
|
||||
{team.title} · {seconds.toLocaleString(undefined, { maximumFractionDigits: 2 })}s rotation
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", marginTop: 30, color: "#a6d8d4", fontSize: 22, fontWeight: 600 }}>
|
||||
Damage Share
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 16, marginTop: 10 }}>
|
||||
{team.members.map((member) => {
|
||||
const damage = Number(member.damage);
|
||||
const percent = total > 0 ? (damage / total) * 100 : 0;
|
||||
return (
|
||||
<div key={member.position} style={{ display: "flex", alignItems: "center", gap: 14 }}>
|
||||
<img
|
||||
src={member.character.imageUrl}
|
||||
alt=""
|
||||
width="78"
|
||||
height="78"
|
||||
style={{ borderRadius: 16, border: "2px solid #78cbc4", objectFit: "cover" }}
|
||||
/>
|
||||
<div style={{ display: "flex", flex: 1, flexDirection: "column", gap: 6 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<div style={{ display: "flex", fontSize: 20, fontWeight: 600 }}>{member.character.name}</div>
|
||||
<div style={{ display: "flex", color: "#d9f6f2", fontSize: 18 }}>{amount(damage)}</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||||
<div style={{ display: "flex", flex: 1, height: 24, overflow: "hidden", borderRadius: 8, background: "#17383d" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
width: `${Math.max(2, Math.min(100, percent))}%`,
|
||||
height: "100%",
|
||||
borderRadius: 8,
|
||||
background: "linear-gradient(90deg, #55b7ae 0%, #8be0d7 100%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", width: 66, justifyContent: "flex-end", fontSize: 20, fontWeight: 700 }}>
|
||||
{percent.toFixed(1)}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", flex: 1 }} />
|
||||
<div style={{ display: "flex", justifyContent: "center", gap: 16, borderTop: "1px solid #28555a", paddingTop: 20, fontSize: 26, fontWeight: 700 }}>
|
||||
<span style={{ color: "#f0c95f" }}>DPS: {amount(dps)}</span>
|
||||
<span style={{ color: "#688c90" }}>|</span>
|
||||
<span>DPR: {amount(total)}</span>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "center", marginTop: 14, textAlign: "center", color: "#d1e9e6", fontSize: 18, lineHeight: 1.45 }}>
|
||||
{team.combo}
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "center", marginTop: 18, color: "#5c9995", fontSize: 14 }}>
|
||||
BUZZ SHEET
|
||||
</div>
|
||||
</div>,
|
||||
{
|
||||
width: 550,
|
||||
height: 776,
|
||||
fonts: font
|
||||
? [{ name: "Anuphan", data: font, weight: 400, style: "normal" }]
|
||||
: undefined,
|
||||
headers: {
|
||||
"Cache-Control": "no-store",
|
||||
"Content-Disposition": `attachment; filename="buzz-team-${team.id}.png"`,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user