一键导入
custom-view
Build a custom dashboard view from a natural language request — query, API, React component, build, deploy
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Build a custom dashboard view from a natural language request — query, API, React component, build, deploy
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Daily financial summary — net worth, recent activity, payment due dates, alerts
Manage recurring scheduled tasks — add, list, pause, resume, remove
On-demand financial briefing — spending, balances, portfolio, transactions, reports, CSV export
Override transaction categories via natural language — update rules, reclassify
Guided first-bank setup — credentials, exploration, sync, extraction, import
Discover and build a complete bank integration by answering 9 required questions
| name | custom-view |
| description | Build a custom dashboard view from a natural language request — query, API, React component, build, deploy |
| trigger | manual |
Build a custom dashboard tab on demand. The user asks a question about their finances in natural language, and this skill creates a new query function, API endpoint, and React component, then builds and deploys the updated dashboard.
dashboard/dist/ must exist (run cd dashboard && npm run build if not)data/foliome.db must have data (run a sync first)If the user asks to remove a custom tab (e.g., "remove the restaurant spending tab"):
dashboard/src/App.tsx and find the TABS entry with a custom- prefixed ID matching the request.App.tsx.dashboard/src/tabs/Custom_*.tsx.dashboard-server.js.dashboard-queries.js.cd dashboard && npm run build.Parse the natural language request. Determine what data is needed using the schema reference in docs/dashboard-customization.md.
Key tables and patterns:
transactions — date, description, amount, category, user_category, account_id, institutionbalances — account_id, account_type, balance, synced_at (use latest-per-account pattern)holdings — symbol, name, quantity, price, market_valueinvestment_transactions — date, symbol, type, amount, quantity, priceWHERE category NOT IN ('Transfer', 'Income') AND amount < 0COALESCE(user_category, category)dashboard/src/App.tsx. Count existing custom tabs (IDs starting with custom-).custom- (e.g., custom-restaurant-monthly).-2, -3, etc.Add a new function to scripts/dashboard-queries.js following the existing pattern:
function getCustomRestaurantMonthly(dbPath) {
const db = openDb(dbPath);
// ... query logic ...
db.close();
return { /* results */ };
}
Rules:
openDb(dbPath) and db.close()..prepare() with parameterized queries for any user-supplied values.$ prefix (e.g., params.from not params.$from). The SQL uses $from but better-sqlite3 expects from in the params object.module.exports at the bottom of the file.Verify the query works before proceeding:
node -e "const q = require('./scripts/dashboard-queries.js'); const r = q.getCustomRestaurantMonthly(); console.log(JSON.stringify(r).slice(0, 200));"
If it errors, fix the query and test again.
In scripts/dashboard-server.js:
require('./dashboard-queries.js') destructuring at the top.if (parsed.pathname.startsWith('/api/')) block), before the 404 fallback:if (parsed.pathname === '/api/custom-restaurant-monthly') {
sendJson(getCustomRestaurantMonthly());
return;
}
New API routes require a server restart. The server is a Node.js process on port 3847.
# Kill existing server and restart
kill $(lsof -ti:3847) 2>/dev/null
node scripts/dashboard-server.js &
sleep 2
curl -s http://localhost:3847/health
Wait for the health check to return "ok" before proceeding.
Write dashboard/src/tabs/Custom_{PascalSlug}.tsx:
import { useEffect, useState } from 'react';
import { fetchWithAuth } from '@/lib/api';
import { EmptyState } from '@/components/shared/EmptyState';
interface CustomData { /* match the query return shape */ }
export function Custom{PascalSlug}() {
const [data, setData] = useState<CustomData | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetchWithAuth<CustomData>('/api/custom-{slug}')
.then(setData)
.catch(e => setError(e.message));
}, []);
if (error) return <EmptyState message={error} />;
if (!data) return <div className="py-12 text-center t-caption text-[var(--text-muted)]">Loading...</div>;
return (
<div className="animate-fade-in">
{/* Render the data using Tailwind + CSS variables */}
{/* Use existing shared components: EmptyState, KPICard, TransactionRow, etc. */}
{/* Use fmtAccounting, fmtShort, fmtPercent from @/lib/format */}
{/* Charts: import from recharts (PieChart, AreaChart, BarChart, LineChart) */}
</div>
);
}
Design guidelines:
@/components/shared/ as needed@/lib/formatrecharts (already a dependency)<div className="rounded-xl border border-[var(--border)] bg-[var(--bg-card)] p-4 mb-3">t-hero, t-value, t-body, t-caption, t-microtext-[var(--positive)], text-[var(--negative)], text-[var(--warning)], text-[var(--brand)]Add the import at the top of App.tsx:
import { CustomRestaurantMonthly } from '@/tabs/Custom_RestaurantMonthly';
Add to the TABS array:
{ id: 'custom-restaurant-monthly', label: 'Restaurants' },
Add the content switch case in the tab content section:
{activeTab === 'custom-restaurant-monthly' && <CustomRestaurantMonthly />}
cp -r dashboard/dist dashboard/dist-backup 2>/dev/null || true
cd dashboard && npm run build
If the build fails:
npm run build.curl -s http://localhost:3847/api/custom-restaurant-monthly
This will return 401 (no auth token), which confirms the route exists. A 404 means the server wasn't restarted or the route wasn't added correctly.
For a full data test, use a dev token if available, or verify the build output:
ls dashboard/dist/index.html && echo "Build output exists"
Tell the user the new tab is ready. If running as a Telegram agent, present the dashboard as a Mini App button so the user can tap to see the new tab immediately:
node scripts/telegram-notify.js --dashboard "<chatId>" "Built your 'Restaurant Spending' tab. Tap to see it." "<tunnel-url>"
Use the chatId from the inbound Telegram message and the active cloudflared tunnel URL. Do NOT send the dashboard URL as a plain text link — it won't work without Mini App initData.
If all build retries are exhausted:
dashboard-queries.js (remove the new function and export).dashboard-server.js (remove the API route and import).dashboard/src/tabs/Custom_*.tsx.App.tsx (remove tab entry and switch case).dashboard/dist-backup to dashboard/dist.The customization doc at docs/dashboard-customization.md is the instruction manual. Read it before building. It has the SQL patterns, the React component patterns, the chart patterns, and the design tokens. Follow it.