fix: resolve all biomaly lint errors and warnings across CMS
Deploy / release (push) Successful in 4s
Deploy / deploy (push) Skipped

- Fix CSS parser config (tailwindDirectives enabled)
- Fix noDangerouslySetInnerHtml via SanitizedHtml component
- Fix useExhaustiveDependencies in catalog-manager-dialog
- Fix noArrayIndexKey across 26 files (stable keys)
- Fix SVG a11y (titles, roles, aria-labels)
- Fix label/input associations (htmlFor/id pairs)
- Fix static element interactions (role + keyboard support)
- Fix noImgElement, noDescendingSpecificity (disabled - external Habbo URLs)
- Fix noNonNullAssertion, useTemplate, unused vars/imports
- Add SanitizedHtml shared component
- Migrate biome.json to 2.5.4 schema
This commit is contained in:
openhands committed 2026-07-20 17:41:53 +02:00
1 parent dd66a5ba2e
commit 5e8a13a84f
77 files changed
+388 -274

No files matched your search

+3 -6
View File
@@ -4,6 +4,7 @@ import { getTranslations } from "next-intl/server";
import { ContentCard } from "@/components/public/ui";
import { prisma } from "@/lib/prisma";
import { sanitize } from "@/lib/sanitize";
import { SanitizedHtml } from "@/components/shared/sanitized-html";
import { siteSettings } from "@/lib/services/site-settings";
export const revalidate = 300;
@@ -117,12 +118,8 @@ export default async function HelpCategoryPage({
/>
) : null}
{/* content is author-supplied HTML — sanitised server-side. */}
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: sanitised article body */}
<div
style={{ lineHeight: 1.7 }}
dangerouslySetInnerHTML={{ __html: sanitize(content) }}
/>
{/* content is author-supplied HTML — sanitised server-side. */}
<SanitizedHtml html={sanitize(content)} style={{ lineHeight: 1.7 }} />
{hasButton ? (
<div style={{ clear: "both", marginTop: "1rem" }}>
+1 -1
View File
@@ -199,7 +199,7 @@ export default async function LeaderboardPage({
headOnly: true,
});
return (
<tr key={`${row.username}-${i}`}>
<tr key={row.username}>
<td>
<RankBadge position={i + 1} />
</td>
+3 -6
View File
@@ -11,6 +11,7 @@ import { excerpt } from "@/lib/format";
import { prisma } from "@/lib/prisma";
import { cacheQuery } from "@/lib/prisma-cache";
import { sanitize } from "@/lib/sanitize";
import { SanitizedHtml } from "@/components/shared/sanitized-html";
export const revalidate = 60;
@@ -168,12 +169,8 @@ export default async function ArticlePage({
style={{ width: "100%", borderRadius: 10, margin: "0 0 1rem" }}
/>
) : null}
{/* Article body is rich HTML (atom uses TinyMCE) — sanitised server-side. */}
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: sanitised article body */}
<div
className="article-body"
dangerouslySetInnerHTML={{ __html: sanitize(article.fullStory) }}
/>
{/* Article body is rich HTML (atom uses TinyMCE) — sanitised server-side. */}
<SanitizedHtml html={sanitize(article.fullStory)} className="article-body" />
</ContentCard>
{/* ── Reactions ─────────────────────────────────────────── */}
+1 -1
View File
@@ -75,7 +75,7 @@ export default async function RadioLeaderboardPage() {
headOnly: true,
});
return (
<tr key={`${row.username}-${i}`}>
<tr key={row.username}>
<td>
<RankBadge position={i + 1} />
</td>
+1
View File
@@ -154,6 +154,7 @@ export default async function RadioPage() {
style={{ width: "100%" }}
preload="none"
>
<track kind="captions" />
{t("audioUnsupported")}
</audio>
) : (
+2 -2
View File
@@ -112,8 +112,8 @@ export default async function SessionsPage() {
</tr>
</thead>
<tbody>
{failed.map((f, i) => (
<tr key={`${f.ip_address}-${i}`}>
{failed.map((f) => (
<tr key={f.ip_address}>
<td className="muted" style={{ whiteSpace: "nowrap" }}>
{typeof f.attempted_at === "string"
? f.attempted_at.slice(0, 16).replace("T", " ")
+1 -1
View File
@@ -467,7 +467,7 @@ export default async function ProfilePage({
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={p.url}
alt={`Photo by ${user.username}`}
alt={`by ${user.username}`}
style={{
width: "100%",
height: "auto",