import { useState } from “react”;
const C = {
bg: “#0a0a0f”, surface: “#12121a”, card: “#1a1a26”, border: “#2a2a3e”,
accent: “#7c6af7”, accentSoft: “#2a2550”, text: “#e8e8f0”, muted: “#7070a0”,
green: “#34d399”, red: “#f87171”, amber: “#fbbf24”, blue: “#60a5fa”, pink: “#f472b6”,
};
const I18N = {
vi: {
greeting: “Chào buổi sáng 👋”, dateLabel: “Thứ Hai, 23 tháng 3”,
dynamicIsland: “FlowOS · Đang trong giờ tập trung”,
tabs: [“Trang chủ”, “Dinh dưỡng”, “Tài chính”, “Đầu tư”, “Kỷ niệm”],
home: {
progress: “Tiến độ hôm nay”, done: “hoàn thành”,
milestone: “Còn {{n}} ngày đến sinh nhật Mẹ”,
taskflow: “Dòng Công Việc”, schedule: “Lịch Trình Hôm Nay”,
cols: { todo: “Chờ làm”, doing: “Đang làm”, done: “Hoàn thành” },
blocks: [
{ time: “05:30”, label: “Bình minh”, items: [“Thủ tục thức dậy”, “Thiền 10 phút”, “Giãn cơ nhẹ”] },
{ time: “08:00”, label: “Tập trung sâu”, items: [“Lộ trình sản phẩm”, “Họp nhóm”, “Email”] },
{ time: “12:00”, label: “Buổi trưa”, items: [“Ăn trưa + ghi macro”, “Đi bộ ngắn”] },
{ time: “14:00”, label: “Buổi chiều”, items: [“Gặp khách hàng”, “Xem lại công việc”, “Hành chính”] },
{ time: “19:00”, label: “Buổi tối”, items: [“Thời gian gia đình”, “Học 30 phút”, “Thư giãn”] },
],
},
nutrition: {
title: “Dinh Dưỡng & Thể Hình”, quickAdd: “Thêm Nhanh Thực Phẩm”,
add: “+ Thêm”, macros: [“Protein”, “Tinh bột”, “Chất béo”],
foods: [[“Ức gà”, “31g đạm”, “165 kcal”], [“Gạo lứt”, “45g tinh bột”, “216 kcal”], [“Cá hồi”, “25g đạm”, “208 kcal”], [“Khoai lang”, “27g tinh bột”, “103 kcal”], [“Bơ”, “15g chất béo”, “234 kcal”]],
},
finance: {
title: “Tài Chính Cá Nhân”, totalBalance: “Tổng số dư”, show: “Hiện”, hide: “Ẩn”,
funds: “Phân Bổ Quỹ”, bills: “Hóa Đơn Sắp Tới”,
fundNames: [“Quỹ dự phòng”, “Tiết kiệm”, “Mục tiêu du lịch”, “Đầu tư”],
billNames: [“Tiền điện”, “Internet”, “Tiền nhà”],
billDates: [“28 Th3”, “01 Th4”, “05 Th4”],
},
invest: {
portfolioValue: “Giá trị danh mục”, todayPL: “Lãi/lỗ hôm nay”,
portfolio: “Danh Mục Cổ Phiếu”, dca: “Công Cụ Tính DCA”,
ticker: “Mã CK”, buyQty: “Số lượng mua”, buyPrice: “Giá mua”,
currentAvg: “Giá vốn hiện tại”, newAvg: “Giá vốn mới”,
},
milestones: {
title: “Sự Kiện Sắp Tới”, daysLeft: “ngày còn lại”, journal: “Nhật Ký Nhanh”,
placeholder: “Viết ghi chú cho hôm nay…”, save: “Lưu”,
events: [
{ event: “Sinh nhật Mẹ”, date: “15/04”, days: 23, category: “Gia đình”, note: “Mua thực phẩm chức năng”, icon: “♥” },
{ event: “Kỷ niệm ngày cưới”, date: “20/05”, days: 58, category: “Cá nhân”, note: “Đặt bàn nhà hàng”, icon: “★” },
{ event: “Ngày thành lập công ty”, date: “01/06”, days: 70, category: “Công việc”, note: “Tiệc nhóm”, icon: “◆” },
{ event: “Sinh nhật Ba”, date: “10/07”, days: 109, category: “Gia đình”, note: “Chuyến đi cuối tuần”, icon: “♥” },
],
},
settings: { lang: “Ngôn ngữ”, currency: “Đơn vị tiền tệ” },
tasks: {
todo: [[“Xem lại báo cáo Q3”, “Công việc”], [“Gọi bảo hiểm”, “Hành chính”], [“Mua thực phẩm bổ sung”, “Sức khỏe”]],
doing: [[“Lộ trình sản phẩm”, “Công việc”], [“Tập gym buổi sáng”, “Sức khỏe”]],
done: [[“Chuẩn bị bữa ăn CN”, “Sức khỏe”], [“Đóng tiền điện”, “Tài chính”]],
},
},
en: {
greeting: “Good morning 👋”, dateLabel: “Monday, 23 March”,
dynamicIsland: “FlowOS · Deep Work in progress”,
tabs: [“Home”, “Nutrition”, “Finance”, “Invest”, “Milestones”],
home: {
progress: “Today’s progress”, done: “done”,
milestone: “Mom’s Birthday in {{n}} days”,
taskflow: “Task Flow”, schedule: “Daily Schedule”,
cols: { todo: “To-Do”, doing: “In Progress”, done: “Done” },
blocks: [
{ time: “05:30”, label: “Dawn”, items: [“Wake up ritual”, “Meditation 10min”, “Light stretch”] },
{ time: “08:00”, label: “Deep Work”, items: [“Product roadmap”, “Team standup”, “Emails”] },
{ time: “12:00”, label: “Midday”, items: [“Lunch + macros log”, “Short walk”] },
{ time: “14:00”, label: “Afternoon”, items: [“Client calls”, “Review tasks”, “Admin”] },
{ time: “19:00”, label: “Evening”, items: [“Family time”, “Study 30min”, “Wind-down”] },
],
},
nutrition: {
title: “Gym & Nutrition”, quickAdd: “Quick Add Foods”,
add: “+ Add”, macros: [“Protein”, “Carbs”, “Fat”],
foods: [[“Chicken Breast”, “31g protein”, “165 kcal”], [“Brown Rice”, “45g carbs”, “216 kcal”], [“Salmon”, “25g protein”, “208 kcal”], [“Sweet Potato”, “27g carbs”, “103 kcal”], [“Avocado”, “15g fat”, “234 kcal”]],
},
finance: {
title: “Personal Finance”, totalBalance: “Total balance”, show: “Show”, hide: “Hide”,
funds: “Funds Allocation”, bills: “Upcoming Bills”,
fundNames: [“Emergency Fund”, “Savings”, “Travel Goal”, “Investment”],
billNames: [“Electricity”, “Internet”, “Rent”],
billDates: [“28 Mar”, “01 Apr”, “05 Apr”],
},
invest: {
portfolioValue: “Portfolio Value”, todayPL: “Today’s P&L”,
portfolio: “Portfolio”, dca: “DCA Calculator”,
ticker: “Ticker”, buyQty: “Buy qty”, buyPrice: “Buy price”,
currentAvg: “Current avg cost”, newAvg: “New avg cost”,
},
milestones: {
title: “Upcoming Events”, daysLeft: “days left”, journal: “Quick Journal”,
placeholder: “Write a note for today…”, save: “Save”,
events: [
{ event: “Mom’s Birthday”, date: “15/04”, days: 23, category: “Family”, note: “Order supplements”, icon: “♥” },
{ event: “Wedding Anniversary”, date: “20/05”, days: 58, category: “Personal”, note: “Book restaurant”, icon: “★” },
{ event: “Company founding day”, date: “01/06”, days: 70, category: “Work”, note: “Team celebration”, icon: “◆” },
{ event: “Dad’s Birthday”, date: “10/07”, days: 109, category: “Family”, note: “Weekend trip”, icon: “♥” },
],
},
settings: { lang: “Language”, currency: “Currency” },
tasks: {
todo: [[“Review Q3 report”, “Work”], [“Call insurance agent”, “Admin”], [“Buy supplements”, “Health”]],
doing: [[“Deep work: product roadmap”, “Work”], [“Morning workout”, “Health”]],
done: [[“Meal prep Sunday”, “Health”], [“Pay electricity bill”, “Finance”]],
},
},
de: {
greeting: “Guten Morgen 👋”, dateLabel: “Montag, 23. März”,
dynamicIsland: “FlowOS · Tiefarbeit läuft”,
tabs: [“Start”, “Ernährung”, “Finanzen”, “Investition”, “Meilensteine”],
home: {
progress: “Heutiger Fortschritt”, done: “erledigt”,
milestone: “Geburtstag Mama in {{n}} Tagen”,
taskflow: “Aufgabenfluss”, schedule: “Tagesplan”,
cols: { todo: “Offen”, doing: “In Arbeit”, done: “Erledigt” },
blocks: [
{ time: “05:30”, label: “Morgenritual”, items: [“Aufwachroutine”, “Meditation 10min”, “Dehnen”] },
{ time: “08:00”, label: “Tiefarbeit”, items: [“Produkt-Roadmap”, “Team-Standup”, “E-Mails”] },
{ time: “12:00”, label: “Mittag”, items: [“Mittagessen + Makros”, “Kurzer Spaziergang”] },
{ time: “14:00”, label: “Nachmittag”, items: [“Kundengespräche”, “Aufgaben prüfen”, “Verwaltung”] },
{ time: “19:00”, label: “Abend”, items: [“Familienzeit”, “Lernen 30min”, “Entspannen”] },
],
},
nutrition: {
title: “Gym & Ernährung”, quickAdd: “Lebensmittel schnell hinzufügen”,
add: “+ Hinzu”, macros: [“Eiweiß”, “Kohlenhydrate”, “Fett”],
foods: [[“Hähnchenbrust”, “31g Eiweiß”, “165 kcal”], [“Brauner Reis”, “45g Kohlenhydrate”, “216 kcal”], [“Lachs”, “25g Eiweiß”, “208 kcal”], [“Süßkartoffel”, “27g Kohlenhydrate”, “103 kcal”], [“Avocado”, “15g Fett”, “234 kcal”]],
},
finance: {
title: “Persönliche Finanzen”, totalBalance: “Gesamtguthaben”, show: “Anzeigen”, hide: “Verbergen”,
funds: “Fondsverteilung”, bills: “Anstehende Rechnungen”,
fundNames: [“Notfallfonds”, “Ersparnisse”, “Reiseziel”, “Investition”],
billNames: [“Strom”, “Internet”, “Miete”],
billDates: [“28. Mär”, “01. Apr”, “05. Apr”],
},
invest: {
portfolioValue: “Portfoliowert”, todayPL: “Heutiger G/V”,
portfolio: “Aktienportfolio”, dca: “DCA-Rechner”,
ticker: “Kürzel”, buyQty: “Kaufmenge”, buyPrice: “Kaufpreis”,
currentAvg: “Aktueller Einstandspreis”, newAvg: “Neuer Einstandspreis”,
},
milestones: {
title: “Anstehende Ereignisse”, daysLeft: “Tage verbleibend”, journal: “Schnelltagebuch”,
placeholder: “Notiz für heute schreiben…”, save: “Speichern”,
events: [
{ event: “Geburtstag Mama”, date: “15.04.”, days: 23, category: “Familie”, note: “Nahrungsergänzung bestellen”, icon: “♥” },
{ event: “Hochzeitstag”, date: “20.05.”, days: 58, category: “Persönlich”, note: “Restaurant reservieren”, icon: “★” },
{ event: “Firmengründungstag”, date: “01.06.”, days: 70, category: “Arbeit”, note: “Teamfeier”, icon: “◆” },
{ event: “Geburtstag Papa”, date: “10.07.”, days: 109, category: “Familie”, note: “Wochenendausflug”, icon: “♥” },
],
},
settings: { lang: “Sprache”, currency: “Währung” },
tasks: {
todo: [[“Q3-Bericht prüfen”, “Arbeit”], [“Versicherung anrufen”, “Verwaltung”], [“Nahrungsergänzung kaufen”, “Gesundheit”]],
doing: [[“Tiefarbeit: Produkt-Roadmap”, “Arbeit”], [“Morgensport”, “Gesundheit”]],
done: [[“Meal Prep Sonntag”, “Gesundheit”], [“Stromrechnung bezahlen”, “Finanzen”]],
},
},
};
const LANG_META = { vi: { flag: “🇻🇳”, label: “Tiếng Việt” }, en: { flag: “🇺🇸”, label: “English” }, de: { flag: “🇩🇪”, label: “Deutsch” } };
const accounts = [
{ name: { vi: “Vietcombank”, en: “Vietcombank”, de: “Vietcombank” }, bal: 42500000, icon: “V” },
{ name: { vi: “Techcombank”, en: “Techcombank”, de: “Techcombank” }, bal: 18300000, icon: “T” },
{ name: { vi: “Ví MoMo”, en: “MoMo Wallet”, de: “MoMo Wallet” }, bal: 3200000, icon: “M” },
];
const portfolio = [
{ ticker: “VCB”, qty: 1000, avg: 85.0, price: 92.5, sector: { vi: “Ngân hàng”, en: “Banking”, de: “Banken” } },
{ ticker: “HPG”, qty: 2000, avg: 28.5, price: 27.0, sector: { vi: “Thép”, en: “Steel”, de: “Stahl” } },
{ ticker: “VNM”, qty: 500, avg: 72.0, price: 76.5, sector: { vi: “Hàng tiêu dùng”, en: “FMCG”, de: “Konsumgüter” } },
{ ticker: “FPT”, qty: 300, avg: 115.0, price: 128.0, sector: { vi: “Công nghệ”, en: “Tech”, de: “Technologie” } },
];
const funds = [
[15000000, 30000000, C.red], [8000000, 10000000, C.green], [3500000, 20000000, C.blue], [50000000, 100000000, C.accent]
];
const bills = [[580000, C.amber], [250000, C.blue], [8500000, C.red]];
const macroDef = { protein: { val: 142, goal: 180 }, carbs: { val: 210, goal: 250 }, fat: { val: 55, goal: 65 }, kcal: { val: 1890, goal: 2400 } };
const macroColors = [C.blue, C.amber, C.pink];
const fmtVND = n => new Intl.NumberFormat(“vi-VN”).format(n) + “ ₫”;
const fmtEUR = n => new Intl.NumberFormat(“de-DE”, { style: “currency”, currency: “EUR” }).format(n / 25000);
const fmtUSD = n => new Intl.NumberFormat(“en-US”, { style: “currency”, currency: “USD” }).format(n / 25000);
const fmtMoney = (n, lang) => lang === “vi” ? fmtVND(n) : lang === “de” ? fmtEUR(n) : fmtUSD(n);
function Badge({ label, color }) {
return <span style={{ fontSize: 10, padding: “2px 7px”, borderRadius: 20, background: color + “30”, color, fontWeight: 500 }}>{label}</span>;
}
function Card({ children, style }) {
return <div style={{ background: C.card, border: `1px solid ${C.border}`, borderRadius: 16, padding: “16px”, …style }}>{children}</div>;
}
function SecTitle({ children }) {
return <p style={{ fontSize: 11, fontWeight: 500, color: C.muted, letterSpacing: “0.08em”, textTransform: “uppercase”, margin: “0 0 12px” }}>{children}</p>;
}
function MacroBar({ label, val, goal, color }) {
const pct = Math.min(100, Math.round(val / goal * 100));
return (
<div style={{ marginBottom: 12 }}>
<div style={{ display: “flex”, justifyContent: “space-between”, fontSize: 12, color: C.muted, marginBottom: 4 }}>
<span>{label}</span><span style={{ color: C.text }}>{val}g / {goal}g</span>
</div>
<div style={{ height: 6, background: C.border, borderRadius: 3 }}>
<div style={{ height: “100%”, width: pct + “%”, background: color, borderRadius: 3 }} />
</div>
</div>
);
}
const TABS_ICONS = [“⊞”, “◉”, “◈”, “△”, “◇”];
export default function FlowOS() {
const [lang, setLang] = useState(“vi”);
const [tab, setTab] = useState(“home”);
const [showLang, setShowLang] = useState(false);
const [hideBalance, setHideBalance] = useState(true);
const [dcaTicker, setDcaTicker] = useState(“VCB”);
const [dcaQty, setDcaQty] = useState(200);
const [dcaPrice, setDcaPrice] = useState(90);
const t = I18N[lang];
const tabIds = [“home”, “nutrition”, “finance”, “invest”, “milestones”];
const totalBal = accounts.reduce((s, a) => s + a.bal, 0);
const totalPort = portfolio.reduce((s, p) => s + p.qty * p.price, 0);
const dcaStock = portfolio.find(p => p.ticker === dcaTicker);
const newAvg = dcaStock ? ((dcaStock.qty * dcaStock.avg + dcaQty * dcaPrice) / (dcaStock.qty + dcaQty)).toFixed(2) : 0;
const taskCols = [“todo”, “doing”, “done”];
const tagColor = tag => {
const map = {
Work: C.accent, Arbeit: C.accent, “Công việc”: C.accent,
Health: C.green, Gesundheit: C.green, “Sức khỏe”: C.green,
Finance: C.amber, Finanzen: C.amber, “Tài chính”: C.amber,
Admin: C.blue, Verwaltung: C.blue, “Hành chính”: C.blue,
};
return map[tag] || C.muted;
};
return (
<div style={{ background: C.bg, minHeight: “100vh”, fontFamily: “system-ui,sans-serif”, color: C.text, display: “flex”, flexDirection: “column”, maxWidth: 420, margin: “0 auto”, position: “relative” }}>
{/* Dynamic Island */}
<div style={{ display: “flex”, justifyContent: “center”, padding: “10px 0 0” }}>
<div style={{ background: “#000”, borderRadius: 20, padding: “5px 18px”, fontSize: 11, color: “#888”, display: “flex”, gap: 8, alignItems: “center” }}>
<span style={{ width: 8, height: 8, borderRadius: “50%”, background: C.accent, display: “inline-block” }} />
{t.dynamicIsland}
</div>
</div>
{/* Header */}
<div style={{ padding: “16px 20px 8px”, display: “flex”, justifyContent: “space-between”, alignItems: “center” }}>
<div>
<p style={{ margin: 0, fontSize: 13, color: C.muted }}>{t.dateLabel}</p>
<h1 style={{ margin: 0, fontSize: 22, fontWeight: 600 }}>{t.greeting}</h1>
</div>
<div style={{ position: “relative” }}>
<button onClick={() => setShowLang(!showLang)} style={{ background: C.accentSoft, border: `1px solid ${C.border}`, borderRadius: 12, padding: “6px 12px”, color: C.accent, fontSize: 13, cursor: “pointer”, display: “flex”, alignItems: “center”, gap: 6 }}>
{LANG_META[lang].flag} {lang.toUpperCase()}
</button>
{showLang && (
<div style={{ position: “absolute”, right: 0, top: 42, background: C.card, border: `1px solid ${C.border}`, borderRadius: 12, overflow: “hidden”, zIndex: 200, minWidth: 150 }}>
{Object.entries(LANG_META).map(([k, v]) => (
<button key={k} onClick={() => { setLang(k); setShowLang(false); }} style={{ display: “flex”, alignItems: “center”, gap: 8, width: “100%”, padding: “10px 14px”, background: lang === k ? C.accentSoft : “none”, border: “none”, color: lang === k ? C.accent : C.text, fontSize: 13, cursor: “pointer”, textAlign: “left” }}>
{v.flag} {v.label}
</button>
))}
</div>
)}
</div>
</div>
Get Leducthang’s stories in your inbox
Join Medium for free to get updates from this writer.
{/* Content */}
<div style={{ flex: 1, overflowY: “auto”, padding: “0 16px 100px” }}>
{tab === “home” && (
<div>
<Card style={{ marginBottom: 12 }}>
<div style={{ display: “flex”, justifyContent: “space-between”, marginBottom: 8 }}>
<span style={{ fontSize: 13, color: C.muted }}>{t.home.progress}</span>
<span style={{ fontSize: 13, fontWeight: 600 }}>2 / 7 {t.home.done}</span>
</div>
<div style={{ height: 6, background: C.border, borderRadius: 3 }}>
<div style={{ height: “100%”, width: “28%”, background: `linear-gradient(90deg,${C.accent},${C.blue})`, borderRadius: 3 }} />
</div>
<div style={{ marginTop: 8, fontSize: 11, color: C.pink }}>◇ {t.home.milestone.replace(“{{n}}”, 23)}</div>
</Card>
<SecTitle>{t.home.taskflow}</SecTitle>
{taskCols.map(col => (
<Card key={col} style={{ marginBottom: 10 }}>
<div style={{ display: “flex”, justifyContent: “space-between”, marginBottom: 8 }}>
<span style={{ fontSize: 12, fontWeight: 600, color: col === “doing” ? C.accent : col === “done” ? C.green : C.muted }}>{t.home.cols[col]}</span>
<span style={{ fontSize: 11, color: C.muted }}>{t.tasks[col].length}</span>
</div>
{t.tasks[col].map(([title, tag], i) => (
<div key={i} style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center”, padding: “7px 0”, borderTop: `1px solid ${C.border}` }}>
<span style={{ fontSize: 13 }}>{title}</span>
<Badge label={tag} color={tagColor(tag)} />
</div>
))}
</Card>
))}
<SecTitle>{t.home.schedule}</SecTitle>
{t.home.blocks.map((s, i) => {
const colors = [C.amber, C.accent, C.green, C.blue, C.pink];
return (
<div key={i} style={{ display: “flex”, gap: 12, marginBottom: 12 }}>
<div style={{ width: 40, textAlign: “right”, flexShrink: 0 }}>
<span style={{ fontSize: 11, color: C.muted }}>{s.time}</span>
</div>
<div style={{ width: 3, borderRadius: 2, background: colors[i], flexShrink: 0 }} />
<Card style={{ flex: 1, padding: “10px 12px” }}>
<p style={{ margin: “0 0 4px”, fontSize: 12, fontWeight: 600, color: colors[i] }}>{s.label}</p>
{s.items.map((item, j) => <p key={j} style={{ margin: “2px 0”, fontSize: 12, color: C.muted }}>{item}</p>)}
</Card>
</div>
);
})}
</div>
)}
{tab === “nutrition” && (
<div>
<Card style={{ marginBottom: 12, textAlign: “center” }}>
<div style={{ position: “relative”, width: 140, height: 140, margin: “0 auto 12px” }}>
<svg width=”140" height=”140" style={{ transform: “rotate(-90deg)” }}>
<circle cx=”70" cy=”70" r=”58" fill=”none” stroke={C.border} strokeWidth=”10" />
<circle cx=”70" cy=”70" r=”58" fill=”none” stroke={C.accent} strokeWidth=”10"
strokeDasharray={`${2 * Math.PI * 58 * (macroDef.kcal.val / macroDef.kcal.goal)} ${2 * Math.PI * 58}`} strokeLinecap=”round” />
</svg>
<div style={{ position: “absolute”, top: “50%”, left: “50%”, transform: “translate(-50%,-50%)”, textAlign: “center” }}>
<div style={{ fontSize: 22, fontWeight: 700 }}>{macroDef.kcal.val}</div>
<div style={{ fontSize: 11, color: C.muted }}>/ {macroDef.kcal.goal} kcal</div>
</div>
</div>
{[“protein”, “carbs”, “fat”].map((k, i) => (
<MacroBar key={k} label={t.nutrition.macros[i]} val={macroDef[k]?.val ?? macroDef.protein.val} goal={macroDef[k]?.goal ?? macroDef.protein.goal} color={macroColors[i]} />
))}
</Card>
<SecTitle>{t.nutrition.quickAdd}</SecTitle>
{t.nutrition.foods.map(([name, macro, kcal], i) => (
<Card key={i} style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center”, marginBottom: 8, padding: “10px 14px” }}>
<div>
<p style={{ margin: 0, fontSize: 13, fontWeight: 500 }}>{name}</p>
<p style={{ margin: 0, fontSize: 11, color: C.muted }}>{macro} · {kcal}</p>
</div>
<button style={{ background: C.accentSoft, border: “none”, color: C.accent, borderRadius: 8, padding: “5px 12px”, fontSize: 12, cursor: “pointer” }}>{t.nutrition.add}</button>
</Card>
))}
</div>
)}
{tab === “finance” && (
<div>
<Card style={{ marginBottom: 12 }}>
<div style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center” }}>
<span style={{ fontSize: 12, color: C.muted }}>{t.finance.totalBalance}</span>
<button onClick={() => setHideBalance(!hideBalance)} style={{ background: “none”, border: “none”, color: C.muted, cursor: “pointer”, fontSize: 12 }}>{hideBalance ? t.finance.show : t.finance.hide}</button>
</div>
<p style={{ fontSize: 28, fontWeight: 700, margin: “4px 0” }}>{hideBalance ? “••••••••” : fmtMoney(totalBal, lang)}</p>
{accounts.map((a, i) => (
<div key={i} style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center”, padding: “6px 0”, borderTop: `1px solid ${C.border}` }}>
<div style={{ display: “flex”, alignItems: “center”, gap: 8 }}>
<div style={{ width: 28, height: 28, borderRadius: 8, background: C.accentSoft, display: “flex”, alignItems: “center”, justifyContent: “center”, fontSize: 11, fontWeight: 700, color: C.accent }}>{a.icon}</div>
<span style={{ fontSize: 13 }}>{a.name[lang]}</span>
</div>
<span style={{ fontSize: 13, color: hideBalance ? C.muted : C.text }}>{hideBalance ? “•••” : fmtMoney(a.bal, lang)}</span>
</div>
))}
</Card>
<SecTitle>{t.finance.funds}</SecTitle>
{funds.map(([val, goal, color], i) => (
<Card key={i} style={{ marginBottom: 8 }}>
<div style={{ display: “flex”, justifyContent: “space-between”, marginBottom: 6 }}>
<span style={{ fontSize: 13 }}>{t.finance.fundNames[i]}</span>
<span style={{ fontSize: 12, color: C.muted }}>{Math.round(val / goal * 100)}%</span>
</div>
<div style={{ height: 5, background: C.border, borderRadius: 3 }}>
<div style={{ height: “100%”, width: `${Math.round(val / goal * 100)}%`, background: color, borderRadius: 3 }} />
</div>
<p style={{ margin: “4px 0 0”, fontSize: 11, color: C.muted }}>{fmtMoney(val, lang)} / {fmtMoney(goal, lang)}</p>
</Card>
))}
<SecTitle>{t.finance.bills}</SecTitle>
{bills.map(([amt, color], i) => (
<Card key={i} style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center”, marginBottom: 8 }}>
<div>
<p style={{ margin: 0, fontSize: 13, fontWeight: 500 }}>{t.finance.billNames[i]}</p>
<p style={{ margin: 0, fontSize: 11, color }}>{t.finance.billDates[i]}</p>
</div>
<span style={{ fontSize: 14, fontWeight: 600 }}>{fmtMoney(amt, lang)}</span>
</Card>
))}
</div>
)}
{tab === “invest” && (
<div>
<div style={{ display: “grid”, gridTemplateColumns: “1fr 1fr”, gap: 10, marginBottom: 12 }}>
{[[t.invest.portfolioValue, fmtMoney(totalPort * 1000, lang), C.text], [t.invest.todayPL, “+2.4%”, C.green]].map(([label, val, color]) => (
<Card key={label} style={{ padding: “12px 14px” }}>
<p style={{ margin: “0 0 4px”, fontSize: 11, color: C.muted }}>{label}</p>
<p style={{ margin: 0, fontSize: 16, fontWeight: 700, color }}>{val}</p>
</Card>
))}
</div>
<SecTitle>{t.invest.portfolio}</SecTitle>
{portfolio.map(s => {
const pl = ((s.price — s.avg) / s.avg * 100).toFixed(1);
const w = (s.qty * s.price / totalPort * 100).toFixed(0);
return (
<Card key={s.ticker} style={{ marginBottom: 8 }}>
<div style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center” }}>
<div style={{ display: “flex”, gap: 10, alignItems: “center” }}>
<div style={{ width: 36, height: 36, borderRadius: 10, background: C.accentSoft, display: “flex”, alignItems: “center”, justifyContent: “center”, fontSize: 12, fontWeight: 700, color: C.accent }}>{s.ticker}</div>
<div>
<p style={{ margin: 0, fontSize: 13, fontWeight: 600 }}>{s.ticker} <span style={{ fontSize: 11, color: C.muted }}>· {s.sector[lang]}</span></p>
<p style={{ margin: 0, fontSize: 11, color: C.muted }}>{lang === “vi” ? “TB” : lang === “de” ? “Ø” : “Avg”} {s.avg} · {s.qty} {lang === “vi” ? “CP” : lang === “de” ? “Aktien” : “shares”} · {w}%</p>
</div>
</div>
<div style={{ textAlign: “right” }}>
<p style={{ margin: 0, fontSize: 14, fontWeight: 700 }}>{s.price}</p>
<p style={{ margin: 0, fontSize: 12, color: pl >= 0 ? C.green : C.red }}>{pl >= 0 ? “+” : “”}{pl}%</p>
</div>
</div>
</Card>
);
})}
<SecTitle>{t.invest.dca}</SecTitle>
<Card>
<div style={{ marginBottom: 12 }}>
<label style={{ fontSize: 12, color: C.muted }}>{t.invest.ticker}</label>
<select value={dcaTicker} onChange={e => setDcaTicker(e.target.value)} style={{ width: “100%”, marginTop: 4, background: C.surface, color: C.text, border: `1px solid ${C.border}`, borderRadius: 8, padding: “7px 10px”, fontSize: 13 }}>
{portfolio.map(p => <option key={p.ticker} value={p.ticker}>{p.ticker}</option>)}
</select>
</div>
<div style={{ display: “grid”, gridTemplateColumns: “1fr 1fr”, gap: 10, marginBottom: 14 }}>
{[[t.invest.buyQty, dcaQty, setDcaQty], [t.invest.buyPrice, dcaPrice, setDcaPrice]].map(([label, val, setter]) => (
<div key={label}>
<label style={{ fontSize: 12, color: C.muted }}>{label}</label>
<input type=”number” value={val} onChange={e => setter(+e.target.value)} style={{ width: “100%”, marginTop: 4, background: C.surface, color: C.text, border: `1px solid ${C.border}`, borderRadius: 8, padding: “7px 10px”, fontSize: 13, boxSizing: “border-box” }} />
</div>
))}
</div>
{dcaStock && (
<div style={{ background: C.surface, borderRadius: 10, padding: “12px 14px” }}>
{[[t.invest.currentAvg, dcaStock.avg, C.text], [t.invest.newAvg, newAvg, +newAvg < dcaStock.avg ? C.green : C.amber]].map(([label, val, color]) => (
<div key={label} style={{ display: “flex”, justifyContent: “space-between”, marginBottom: 6 }}>
<span style={{ fontSize: 12, color: C.muted }}>{label}</span>
<span style={{ fontSize: 14, fontWeight: 700, color }}>{val}</span>
</div>
))}
</div>
)}
</Card>
</div>
)}
{tab === “milestones” && (
<div>
<SecTitle>{t.milestones.title}</SecTitle>
{t.milestones.events.map((m, i) => (
<Card key={i} style={{ marginBottom: 10 }}>
<div style={{ display: “flex”, gap: 12, alignItems: “center” }}>
<div style={{ width: 44, height: 44, borderRadius: 14, background: C.accentSoft, display: “flex”, alignItems: “center”, justifyContent: “center”, fontSize: 18, flexShrink: 0 }}>{m.icon}</div>
<div style={{ flex: 1 }}>
<div style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center” }}>
<p style={{ margin: 0, fontSize: 14, fontWeight: 600 }}>{m.event}</p>
<span style={{ fontSize: 20, fontWeight: 700, color: m.days < 30 ? C.pink : C.muted }}>{m.days}</span>
</div>
<div style={{ display: “flex”, justifyContent: “space-between”, alignItems: “center”, marginTop: 2 }}>
<p style={{ margin: 0, fontSize: 11, color: C.muted }}>{m.date}</p>
<span style={{ fontSize: 10, color: C.muted }}>{t.milestones.daysLeft}</span>
</div>
<p style={{ margin: “5px 0 0”, fontSize: 12, color: C.amber }}>💡 {m.note}</p>
</div>
</div>
</Card>
))}
<SecTitle>{t.milestones.journal}</SecTitle>
<Card>
<textarea placeholder={t.milestones.placeholder} rows={3} style={{ width: “100%”, background: “none”, border: “none”, color: C.text, fontSize: 13, resize: “none”, outline: “none”, lineHeight: 1.6, boxSizing: “border-box” }} />
<div style={{ display: “flex”, justifyContent: “flex-end”, borderTop: `1px solid ${C.border}`, paddingTop: 8, marginTop: 4 }}>
<button style={{ background: C.accentSoft, border: “none”, color: C.accent, borderRadius: 8, padding: “6px 14px”, fontSize: 12, cursor: “pointer” }}>{t.milestones.save}</button>
</div>
</Card>
</div>
)}
</div>
{/* Tab Bar */}
<div style={{ position: “fixed”, bottom: 0, left: “50%”, transform: “translateX(-50%)”, width: “100%”, maxWidth: 420, background: C.surface, borderTop: `1px solid ${C.border}`, display: “flex”, justifyContent: “space-around”, padding: “10px 0 20px”, zIndex: 100 }}>
{tabIds.map((id, i) => (
<button key={id} onClick={() => setTab(id)} style={{ background: “none”, border: “none”, display: “flex”, flexDirection: “column”, alignItems: “center”, gap: 3, cursor: “pointer”, padding: “0 8px” }}>
<span style={{ fontSize: 18, color: tab === id ? C.accent : C.muted }}>{TABS_ICONS[i]}</span>
<span style={{ fontSize: 10, fontWeight: tab === id ? 600 : 400, color: tab === id ? C.accent : C.muted }}>{t.tabs[i]}</span>
</button>
))}
</div>
</div>
);
}




