بنقرة واحدة
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.