feat: 可选社区上报与官方精选展柜

默认关闭,仪表盘一键开关;枢纽由运维配置收报,人工精选后展示于 /showcase。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-31 04:22:59 +08:00
parent 0abfb32194
commit 95c2154087
26 changed files with 1730 additions and 147 deletions

View File

@@ -0,0 +1,85 @@
import { useEffect, useState } from 'react';
import { Heart } from 'lucide-react';
import { notify } from '@/lib/notify';
import { api } from '../../api/client';
import type { CommunityConfig } from '../../api/types';
const EMPTY_COMMUNITY: CommunityConfig = {
report_enabled: false,
hub_enabled: false,
hub_url: 'https://bbs.iioio.com',
site_url: '',
instance_id: '',
};
/** 仪表盘页脚:自愿社区上报开关(默认关,即时保存) */
export default function CommunitySupportStrip() {
const [community, setCommunity] = useState<CommunityConfig>(EMPTY_COMMUNITY);
const [saving, setSaving] = useState(false);
const [ready, setReady] = useState(false);
useEffect(() => {
let cancelled = false;
api.adminSettings()
.then((s) => {
if (!cancelled) {
setCommunity({ ...EMPTY_COMMUNITY, ...(s.community ?? {}) });
setReady(true);
}
})
.catch(() => {
if (!cancelled) setReady(true);
});
return () => { cancelled = true; };
}, []);
const handleToggle = async () => {
if (saving || !ready) return;
const next = !community.report_enabled;
const prev = community;
setCommunity((c) => ({ ...c, report_enabled: next }));
setSaving(true);
try {
const r = await api.adminUpdateCommunitySettings({
...EMPTY_COMMUNITY,
report_enabled: next,
});
setCommunity({ ...EMPTY_COMMUNITY, ...r.community });
if (r.heartbeat_error) {
notify.warning(`${r.message}${r.heartbeat_error}`);
} else {
notify.success(next ? '已开启社区上报' : '已关闭社区上报');
}
} catch (e: unknown) {
setCommunity(prev);
notify.error(e instanceof Error ? e.message : '保存失败');
} finally {
setSaving(false);
}
};
return (
<div className="admin-community-support-strip" role="group" aria-label="支持姜十三开源">
<div className="admin-community-support-strip-main">
<Heart size={16} className="admin-community-support-strip-icon" aria-hidden />
<div className="admin-community-support-strip-copy">
<strong></strong>
<span>
bbs.iioio.com
</span>
</div>
</div>
<button
type="button"
role="switch"
aria-checked={community.report_enabled}
aria-busy={saving}
disabled={saving || !ready}
className={`admin-settings-switch${community.report_enabled ? ' is-on' : ''}`}
onClick={() => void handleToggle()}
>
<span className="admin-settings-switch-ui" aria-hidden />
</button>
</div>
);
}