feat : update
This commit is contained in:
@@ -1,23 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
constellationChartRows,
|
||||
imageContentDisposition,
|
||||
} from "@/lib/guides/export-image";
|
||||
import { constellationChartRows } from "@/lib/guides/export-image";
|
||||
|
||||
describe("guide image downloads", () => {
|
||||
it("provides a safe ASCII fallback and a UTF-8 filename", () => {
|
||||
expect(imageContentDisposition("ไกด์อาวุธ", "weapons")).toBe(
|
||||
"attachment; filename=\"weapons.png\"; filename*=UTF-8''%E0%B9%84%E0%B8%81%E0%B8%94%E0%B9%8C%E0%B8%AD%E0%B8%B2%E0%B8%A7%E0%B8%B8%E0%B8%98.png",
|
||||
);
|
||||
});
|
||||
|
||||
it("sanitizes punctuation in the fallback filename", () => {
|
||||
expect(imageContentDisposition("Arlecchino / R5", "weapons")).toContain(
|
||||
'filename="Arlecchino-R5.png"',
|
||||
);
|
||||
});
|
||||
|
||||
it("orders constellation levels, adds C0, and scales against the maximum", () => {
|
||||
const rows = constellationChartRows([
|
||||
{ level: 1, cumulativePercent: "117", isGreat: false },
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
export function imageContentDisposition(displayName: string, fallbackName: string): string {
|
||||
const safeDisplayName = displayName.trim() || fallbackName;
|
||||
const fallback = safeDisplayName
|
||||
.normalize("NFKD")
|
||||
.replace(/[^a-z0-9]+/giu, "-")
|
||||
.replace(/^-|-$/gu, "") || fallbackName;
|
||||
const encoded = encodeURIComponent(`${safeDisplayName}.png`).replace(
|
||||
/['()*]/gu,
|
||||
(character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,
|
||||
);
|
||||
return `attachment; filename="${fallback}.png"; filename*=UTF-8''${encoded}`;
|
||||
}
|
||||
|
||||
export interface ConstellationChartRow {
|
||||
level: number;
|
||||
percent: number;
|
||||
|
||||
@@ -142,57 +142,4 @@ export async function getPublicGuide(slug: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPublicTeamExport(teamId: string) {
|
||||
const [row] = await getDb()
|
||||
.select({ team: teamCompositions, guide: guides })
|
||||
.from(teamCompositions)
|
||||
.innerJoin(guides, eq(guides.id, teamCompositions.guideId))
|
||||
.innerJoin(
|
||||
guideSections,
|
||||
and(
|
||||
eq(guideSections.guideId, guides.id),
|
||||
eq(guideSections.slug, "team"),
|
||||
eq(guideSections.enabled, true),
|
||||
),
|
||||
)
|
||||
.where(and(eq(teamCompositions.id, teamId), isNull(guides.trashedAt)))
|
||||
.limit(1);
|
||||
if (!row) return null;
|
||||
|
||||
const members = await getDb()
|
||||
.select({ member: teamMembers, character: catalogCharacters })
|
||||
.from(teamMembers)
|
||||
.innerJoin(
|
||||
catalogCharacters,
|
||||
eq(catalogCharacters.key, teamMembers.characterKey),
|
||||
)
|
||||
.where(eq(teamMembers.teamId, teamId))
|
||||
.orderBy(asc(teamMembers.position));
|
||||
|
||||
const weaponKeys = members.map(({ member }) => member.weaponKey).filter((key): key is string => Boolean(key));
|
||||
const artifactKeys = members.flatMap(({ member }) => [member.artifactKey, member.secondArtifactKey]).filter((key): key is string => Boolean(key));
|
||||
const [weapons, artifacts] = await Promise.all([
|
||||
weaponKeys.length ? getDb().select().from(catalogWeapons).where(inArray(catalogWeapons.key, weaponKeys)) : [],
|
||||
artifactKeys.length ? getDb().select().from(catalogArtifacts).where(inArray(catalogArtifacts.key, artifactKeys)) : [],
|
||||
]);
|
||||
|
||||
return {
|
||||
...row.team,
|
||||
guideId: row.guide.id,
|
||||
guideName: row.guide.name,
|
||||
members: members.map(({ member, character }) => {
|
||||
const weapon = weapons.find((item) => item.key === member.weaponKey);
|
||||
const artifact = artifacts.find((item) => item.key === member.artifactKey);
|
||||
const secondArtifact = artifacts.find((item) => item.key === member.secondArtifactKey);
|
||||
return {
|
||||
...member,
|
||||
character: catalogCharacterWithUrls(character),
|
||||
weapon: weapon ? { ...weapon, imageUrl: publicMediaUrl(weapon.imageKey) } : null,
|
||||
artifact: artifact ? { ...artifact, imageUrl: publicMediaUrl(artifact.imageKey) } : null,
|
||||
secondArtifact: secondArtifact ? { ...secondArtifact, imageUrl: publicMediaUrl(secondArtifact.imageKey) } : null,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export type PublicGuide = NonNullable<Awaited<ReturnType<typeof getPublicGuide>>>;
|
||||
|
||||
Reference in New Issue
Block a user