fix(workshop): expose moderation

This commit is contained in:
Thomas Camlong committed 2026-08-10 17:37:13 +02:00
1 parent be93fb330a
commit 378bc0cd28
13 files changed
+58 -12

No files matched your search

@@ -451,7 +451,7 @@ const MarketplaceDetail = ({ workshopUrl }: { workshopUrl: string }) => {
try {
await backend.report(
submission.id,
reportCategory as "malicious" | "spam" | "copyright" | "inappropriate" | "other",
reportCategory as "outdated" | "malicious" | "spam" | "copyright" | "inappropriate" | "other",
reportExplanation,
);
setReportOpen(false);
@@ -881,6 +881,7 @@ const MarketplaceDetail = ({ workshopUrl }: { workshopUrl: string }) => {
<SelectValue>{(value) => String(value).replace(/^./u, (letter) => letter.toUpperCase())}</SelectValue>
</SelectTrigger>
<SelectContent align="start">
<SelectItem value="outdated">Outdated or no longer working</SelectItem>
<SelectItem value="malicious">Malicious</SelectItem>
<SelectItem value="spam">Spam</SelectItem>
<SelectItem value="copyright">Copyright</SelectItem>
@@ -109,7 +109,11 @@ export function WorkshopAdmin({ workshopUrl }: { workshopUrl?: string }) {
submission,
reports: reports.filter((report) => report.submission === submission.id),
}))
.sort((left, right) => Number(right.reports.length > 0) - Number(left.reports.length > 0));
.sort(
(left, right) =>
right.reports.length - left.reports.length ||
Date.parse(right.submission.updated) - Date.parse(left.submission.updated),
);
return (
<main className="marketplace mx-auto max-w-6xl px-4 pb-20 text-foreground sm:px-6">
@@ -8,12 +8,14 @@ import {
IconChevronLeft,
IconChevronRight,
IconEye,
IconFlag,
IconMessage,
IconPackage,
IconPlus,
IconPuzzle,
IconRefresh,
IconSearch,
IconShield,
} from "@tabler/icons-react";
import type { WorkshopSubmission } from "@site/src/lib/pocketbase";
@@ -116,6 +118,7 @@ export const WorkshopApp = ({ workshopUrl }: { workshopUrl: string }) => {
const availableTypeFilters = workshop.user
? [...typeFilters, { value: "yours" as const, label: "Yours" }]
: typeFilters;
const openReportCount = workshop.submissions.reduce((total, submission) => total + submission.reportCount, 0);
return (
<div className="mx-auto max-w-7xl px-4 pb-16">
@@ -126,9 +129,19 @@ export const WorkshopApp = ({ workshopUrl }: { workshopUrl: string }) => {
Discover community-made widgets and CSS. Review the source, then import it into Homarr.
</p>
</div>
<div className="flex items-center gap-2 self-start sm:self-auto">
<div className="flex flex-wrap items-center gap-2 self-start sm:self-auto">
{workshop.user ? (
<>
{workshop.user.isAdmin && (
<Button
variant="outline"
className="h-10 sm:h-8"
nativeButton={false}
render={<a href="/workshop/admin" aria-label={`Open moderation, ${openReportCount} open reports`} />}
>
<IconShield size={14} /> Moderation{openReportCount > 0 ? ` (${openReportCount})` : ""}
</Button>
)}
<Button className="h-10 sm:h-8" onClick={() => setShowSubmit(true)}>
<IconPlus size={14} /> Share yours
</Button>
@@ -406,9 +419,14 @@ const SubmissionCard = ({ submission, backend, userVote, onVote }: SubmissionCar
</CardHeader>
<CardContent className="min-h-[4.5rem] flex-1 overflow-hidden">
{submission.outdated && (
{(submission.outdated || submission.reportCount > 0) && (
<div className="mb-2 flex flex-wrap gap-1.5">
<Badge variant="secondary">Outdated</Badge>
{submission.outdated && <Badge variant="secondary">Outdated</Badge>}
{submission.reportCount > 0 && (
<Badge variant="destructive" className="gap-1">
<IconFlag size={12} /> {submission.reportCount} {submission.reportCount === 1 ? "report" : "reports"}
</Badge>
)}
</div>
)}
{submission.description && (
@@ -162,7 +162,7 @@ export const useWorkshop = (workshopUrl: string) => {
const report = useCallback(
async (
submissionId: string,
category: "malicious" | "spam" | "copyright" | "inappropriate" | "other",
category: "outdated" | "malicious" | "spam" | "copyright" | "inappropriate" | "other",
explanation: string,
) => {
try {