Files
john 4e21ca937a
Deploy Documentation / deploy (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / bundle-desktop-windows-cuda (push) Has been cancelled
Canary / Release (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
CI / Check MSRV (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
CI / H5 Plaza Tests and Build (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Add TKMind platform extensions, H5/MindSpace stack, and deployment tooling.
Fork goose with custom MCP widgets, platform extensions (aider, git, web, search),
MindSpace H5 backend/frontend, Plaza/Ops UIs, and deploy scripts for tkmind.cn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 21:30:20 +08:00

109 lines
3.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState } from 'react';
import { LoginPrompt } from '@/components/auth/LoginPrompt';
import { PlazaApiError, submitPostReport } from '@/lib/api';
const REPORT_REASONS = [
{ value: 'spam', label: '垃圾广告' },
{ value: 'violence', label: '暴力内容' },
{ value: 'porn', label: '色情低俗' },
{ value: 'political', label: '政治敏感' },
{ value: 'privacy', label: '侵犯隐私' },
{ value: 'other', label: '其他' },
] as const;
type PostReportButtonProps = {
postId: string;
};
export function PostReportButton({ postId }: PostReportButtonProps) {
const [open, setOpen] = useState(false);
const [reason, setReason] = useState<(typeof REPORT_REASONS)[number]['value']>('spam');
const [detail, setDetail] = useState('');
const [message, setMessage] = useState('');
const [busy, setBusy] = useState(false);
const [loginOpen, setLoginOpen] = useState(false);
const submit = async () => {
setBusy(true);
setMessage('');
try {
await submitPostReport(postId, reason, detail.trim() || undefined);
setMessage('举报已提交,感谢反馈');
setOpen(false);
setDetail('');
} catch (error) {
if (error instanceof PlazaApiError && error.code === 'unauthorized') {
setLoginOpen(true);
} else {
setMessage(error instanceof Error ? error.message : '提交失败');
}
} finally {
setBusy(false);
}
};
return (
<>
<button
type="button"
onClick={() => setOpen((value) => !value)}
className="text-sm font-semibold text-[#8a928c] hover:text-red-700"
>
</button>
{open ? (
<div className="mt-3 rounded-xl border border-[#ebe4d6] bg-[#faf7f0] p-4">
<p className="text-sm font-semibold text-[#17221d]"></p>
<label className="mt-3 block text-xs text-[#68716c]">
<select
value={reason}
onChange={(event) =>
setReason(event.target.value as (typeof REPORT_REASONS)[number]['value'])
}
className="mt-1 w-full rounded-lg border border-[#d6d0c3] bg-white px-3 py-2 text-sm text-[#17221d]"
>
{REPORT_REASONS.map((item) => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
</label>
<label className="mt-3 block text-xs text-[#68716c]">
<textarea
value={detail}
onChange={(event) => setDetail(event.target.value)}
maxLength={500}
rows={3}
className="mt-1 w-full rounded-lg border border-[#d6d0c3] bg-white px-3 py-2 text-sm text-[#17221d]"
/>
</label>
<div className="mt-3 flex gap-2">
<button
type="button"
disabled={busy}
onClick={() => void submit()}
className="rounded-full bg-red-700 px-4 py-2 text-sm font-semibold text-white disabled:opacity-60"
>
{busy ? '提交中…' : '提交举报'}
</button>
<button
type="button"
onClick={() => setOpen(false)}
className="rounded-full border border-[#d6d0c3] px-4 py-2 text-sm text-[#68716c]"
>
</button>
</div>
</div>
) : null}
{message ? <p className="mt-2 text-sm text-[#2f6f57]">{message}</p> : null}
<LoginPrompt open={loginOpen} onClose={() => setLoginOpen(false)} message="登录后可举报" />
</>
);
}