feat(analytics): add SEO/GEO page dashboard and retire Rybbit SSO
Expose Umami memind-pages discoveryChannel data in adm with a public-page style table, proxy it via admin-api, and remove Rybbit configuration paths. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useState, type FormEvent } from 'react';
|
||||
import { getMindSpaceAdminConfig, getRybbitSsoUrl, getUmamiSsoUrl, updateMindSpaceAdminConfig } from '../../api/client';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getMindSpaceAdminConfig, getUmamiSsoUrl, updateMindSpaceAdminConfig } from '../../api/client';
|
||||
|
||||
const initial = {
|
||||
enabled: false,
|
||||
@@ -17,7 +18,6 @@ export function AnalyticsConfigPage() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [message, setMessage] = useState<string | null>(null);
|
||||
const [openingUmami, setOpeningUmami] = useState(false);
|
||||
const [openingRybbit, setOpeningRybbit] = useState(false);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -63,28 +63,15 @@ export function AnalyticsConfigPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const openRybbit = async () => {
|
||||
setOpeningRybbit(true);
|
||||
setError(null);
|
||||
try {
|
||||
window.location.assign(await getRybbitSsoUrl());
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '无法打开 Rybbit');
|
||||
setOpeningRybbit(false);
|
||||
}
|
||||
};
|
||||
|
||||
return <div className="admin-page">
|
||||
<div className="admin-page-head"><h2>Analytics 配置</h2><p className="muted">进入统计后台,或配置 Memind 生成页面的 Umami 统计。</p></div>
|
||||
<div className="admin-page-head"><h2>Analytics 配置</h2><p className="muted">配置 Umami 并进入统计后台。Rybbit 已退役,见 Memind 文档 analytics-platform.md。</p></div>
|
||||
{error && <p className="banner banner-error">{error}</p>}
|
||||
{message && <p className="banner banner-info">{message}</p>}
|
||||
<section className="admin-card">
|
||||
<h2>Rybbit 行为分析</h2>
|
||||
<p className="muted">查看页面访问、用户会话、停留时间、点击和会话回放等行为明细。</p>
|
||||
<h2>SEO / GEO 页面流量</h2>
|
||||
<p className="muted">按页面查看搜索引擎与生成式引擎来源访问,表格口径与 Umami「所有生成页面明细」一致。</p>
|
||||
<div className="admin-actions">
|
||||
<button type="button" className="send-btn" onClick={() => void openRybbit()} disabled={openingRybbit}>
|
||||
{openingRybbit ? '正在进入 Rybbit...' : '打开 Rybbit 分析后台'}
|
||||
</button>
|
||||
<Link to="/analytics/seo-geo" className="send-btn">打开 SEO / GEO 流量看板</Link>
|
||||
</div>
|
||||
</section>
|
||||
<section className="admin-card">
|
||||
@@ -96,6 +83,7 @@ export function AnalyticsConfigPage() {
|
||||
<label className="admin-form-row"><span>Umami 地址</span><input value={config.analyticsUrl} disabled={loading || busy} onChange={(e) => setConfig({ ...config, analyticsUrl: e.target.value })} placeholder="http://127.0.0.1:3100" /></label>
|
||||
<label className="admin-form-row"><span>统计域名</span><input value={config.domains} disabled={loading || busy} onChange={(e) => setConfig({ ...config, domains: e.target.value })} placeholder="127.0.0.1,localhost" /></label>
|
||||
<label className="admin-form-row"><span>匿名化密钥 {config.idSecretConfigured ? '(已配置,留空保持不变)' : ''}</span><input type="password" value={secret} disabled={loading || busy} onChange={(e) => setSecret(e.target.value)} placeholder={config.idSecretConfigured ? '留空保持当前密钥' : '输入本地随机密钥'} autoComplete="new-password" /></label>
|
||||
<p className="muted">SEO/GEO 看板还需在 memind_adm 环境变量配置 <code>UMAMI_ADMIN_PASSWORD</code>(与 Umami admin 账号匹配)。</p>
|
||||
<div className="admin-actions"><button type="submit" className="send-btn" disabled={busy || loading}>{busy ? '保存中...' : '保存配置'}</button><button type="button" className="ghost-btn" onClick={() => void load()} disabled={busy}>重新加载</button></div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user