Skip to content

Instantly share code, notes, and snippets.

@shricodev
Last active July 15, 2025 11:20
Show Gist options
  • Save shricodev/a5b5ad79ecfca26bf119e59c0b5bc95f to your computer and use it in GitHub Desktop.
Save shricodev/a5b5ad79ecfca26bf119e59c0b5bc95f to your computer and use it in GitHub Desktop.
Figma MCP Implementation - Gemini 2.5 Pro - Blog Demo
"use client";
export default function ExpenseChart() {
// Chart data representing different heights for each bar
const chartData = [
35, 52, 44, 35, 30, 44, 27, 27, 35, 52, 44, 35, 30, 44, 27, 27, 35, 52, 44, 35, 60, 44, 27
];
return (
<div className="h-[60px] flex items-end gap-[6px]">
{chartData.map((height, index) => (
<div
key={index}
className="w-[16px] bg-[#157AFF] rounded-sm transition-all hover:opacity-80"
style={{
height: `${height}px`,
opacity: index === 20 ? 1 : 0.2, // Highlight the 21st bar
}}
/>
))}
</div>
);
}
"use client";
interface ExpenseItemProps {
expense: {
id: number;
category: string;
description: string;
time: string;
amount: number;
color: string;
icon: string;
};
}
export default function ExpenseItem({ expense }: ExpenseItemProps) {
const formatAmount = (amount: number) => {
return amount.toLocaleString('id-ID').replace(/,/g, '.');
};
const getIcon = (type: string) => {
switch (type) {
case "grocery":
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3.19 3.2L13.01 3.2" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
<path d="M3.19 12L13.01 12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
<path d="M13.01 3.2V12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
</svg>
);
case "transport":
return (
<svg width="14" height="16" viewBox="0 0 14 16" fill="none">
<path d="M0 0H13.47V16L6.735 12.21L0 16V0Z" fill="currentColor"/>
<circle cx="2.53" cy="2.53" r="1.265" fill="white"/>
<circle cx="6.735" cy="2.53" r="1.265" fill="white"/>
<circle cx="10.94" cy="2.53" r="1.265" fill="white"/>
<rect x="2.53" y="6.32" width="7.58" height="4.21" fill="white"/>
</svg>
);
case "housing":
return (
<svg width="16" height="14" viewBox="0 0 16 14" fill="none">
<path d="M0 6L8 0L16 6V13.6H0V6Z" fill="currentColor"/>
</svg>
);
case "food":
return (
<svg width="18" height="11" viewBox="0 0 18 11" fill="none">
<path d="M0 0H17.83V11H0V0Z" fill="currentColor"/>
<rect x="14.75" y="1.71" width="3.08" height="1.71" fill="white"/>
<rect x="14.89" y="5.4" width="2.94" height="5.4" fill="white"/>
</svg>
);
case "entertainment":
return (
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
<circle cx="9" cy="9" r="9" fill="currentColor"/>
<path d="M7 6V12L12.4 9L7 6Z" fill="white"/>
</svg>
);
default:
return null;
}
};
return (
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
{/* Icon */}
<div
className="w-12 h-12 rounded-full flex items-center justify-center"
style={{ backgroundColor: expense.color }}
>
<div className="text-white opacity-70">
{getIcon(expense.icon)}
</div>
</div>
{/* Details */}
<div>
<h4 className="text-[16px] font-medium text-[#273240]">{expense.category}</h4>
<p className="text-[14px] text-[#404852] opacity-50">
{expense.time} • {expense.description}
</p>
</div>
</div>
{/* Amount */}
<span className="text-[16px] font-semibold text-[#273240]">
{formatAmount(expense.amount)}
</span>
</div>
);
}
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: #101010;
--foreground: #ffffff;
--primary: #157AFF;
--secondary: #273240;
--accent: #31BA96;
--muted: #404852;
--card: #F9FAFC;
--border: #D2DCE8;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
font-family: 'Poppins', sans-serif;
}
body {
color: var(--foreground);
background: var(--background);
}
a {
color: inherit;
text-decoration: none;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "Dashboard - Expenses Tracker",
description: "Track your expenses and manage your finances",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
"use client";
import Image from "next/image";
import ExpenseChart from "./ExpenseChart";
import ExpenseItem from "./ExpenseItem";
export default function MainContent() {
const expenses = [
{
id: 1,
category: "Grocery",
description: "Belanja di pasar",
time: "5:12 pm",
amount: -326800,
color: "#32A7E2",
icon: "grocery",
},
{
id: 2,
category: "Transportation",
description: "Naik bus umum",
time: "5:12 pm",
amount: -15000,
color: "#B548C6",
icon: "transport",
},
{
id: 3,
category: "Housing",
description: "Bayar Listrik",
time: "5:12 pm",
amount: -185750,
color: "#FF8700",
icon: "housing",
},
{
id: 4,
category: "Food and Drink",
description: "Makan Steak",
time: "5:12 pm",
amount: -156000,
color: "#DC3434",
icon: "food",
},
{
id: 5,
category: "Entertainment",
description: "Nonton Bioskop",
time: "5:12 pm",
amount: -35200,
color: "#4BA83D",
icon: "entertainment",
},
];
return (
<div className="flex-1 px-[100px] py-[60px] overflow-y-auto">
{/* Header */}
<div className="mb-12">
<div className="flex items-center justify-between mb-2">
<h1 className="text-[40px] font-semibold text-[#262A41]">Expenses</h1>
<div className="flex items-center gap-6">
{/* User Avatars */}
<div className="flex -space-x-3">
<div className="w-8 h-8 rounded-full border-2 border-white overflow-hidden">
<Image
src="/images/user3.png"
alt="User"
width={32}
height={32}
className="w-full h-full object-cover"
/>
</div>
<div className="w-8 h-8 rounded-full border-2 border-white overflow-hidden">
<Image
src="/images/user2.png"
alt="User"
width={32}
height={32}
className="w-full h-full object-cover"
/>
</div>
<div className="w-8 h-8 rounded-full border-2 border-white overflow-hidden">
<Image
src="/images/user1.png"
alt="User"
width={32}
height={32}
className="w-full h-full object-cover"
/>
</div>
<button className="w-8 h-8 rounded-full border-2 border-[#D2DCE8] bg-white flex items-center justify-center">
<svg width="9" height="9" viewBox="0 0 9 9" fill="none">
<path d="M4.5 0V9" stroke="#D8D8D8" strokeWidth="2"/>
<path d="M0 4.5H9" stroke="#D8D8D8" strokeWidth="2"/>
</svg>
</button>
</div>
</div>
</div>
<p className="text-[16px] text-[#101010] opacity-50">01 - 25 March, 2020</p>
</div>
{/* Expense Chart */}
<ExpenseChart />
{/* Date Section - Today */}
<div className="mt-12 mb-6">
<div className="flex items-center justify-between">
<h3 className="text-[18px] text-[#262A41]">Today</h3>
<div className="flex items-center gap-2">
<div className="flex gap-[5px]">
<div className="w-[5px] h-[5px] bg-[#D8D8D8] rounded-full"></div>
<div className="w-[5px] h-[5px] bg-[#D8D8D8] rounded-full"></div>
<div className="w-[5px] h-[5px] bg-[#D8D8D8] rounded-full"></div>
</div>
</div>
</div>
<div className="w-full h-[0.5px] bg-[#DEDEDE] mt-3"></div>
</div>
{/* Expense Items */}
<div className="space-y-6">
{expenses.slice(0, 3).map((expense) => (
<ExpenseItem key={expense.id} expense={expense} />
))}
</div>
{/* Date Section - Monday */}
<div className="mt-12 mb-6">
<div className="flex items-center justify-between">
<h3 className="text-[18px] text-[#262A41]">Monday, 23 March 2020</h3>
<div className="flex items-center gap-2">
<div className="flex gap-[5px]">
<div className="w-[5px] h-[5px] bg-[#D8D8D8] rounded-full"></div>
<div className="w-[5px] h-[5px] bg-[#D8D8D8] rounded-full"></div>
<div className="w-[5px] h-[5px] bg-[#D8D8D8] rounded-full"></div>
</div>
</div>
</div>
<div className="w-full h-[0.5px] bg-[#DEDEDE] mt-3"></div>
</div>
{/* More Expense Items */}
<div className="space-y-6">
{expenses.slice(3).map((expense) => (
<ExpenseItem key={expense.id} expense={expense} />
))}
</div>
</div>
);
}
"use client";
import { useState } from "react";
import Sidebar from "./components/Sidebar";
import MainContent from "./components/MainContent";
import RightPanel from "./components/RightPanel";
export default function Dashboard() {
const [activeMenu, setActiveMenu] = useState("expenses");
return (
<div className="flex h-screen bg-black overflow-hidden">
{/* Main Container with rounded corners */}
<div className="flex w-full h-full p-5">
<div className="flex w-full bg-white rounded-[30px] overflow-hidden">
{/* Sidebar */}
<Sidebar activeMenu={activeMenu} setActiveMenu={setActiveMenu} />
{/* Main Content Area */}
<MainContent />
{/* Right Panel */}
<RightPanel />
</div>
</div>
</div>
);
}
"use client";
import Image from "next/image";
export default function RightPanel() {
const stats = [
{ category: "Food and Drinks", amount: "872.400", percentage: 28, color: "#31BA96" },
{ category: "Shopping", amount: "1.378.200", percentage: 44, color: "#31BA96" },
{ category: "Housing", amount: "928.500", percentage: 36, color: "#31BA96" },
{ category: "Transportation", amount: "420.700", percentage: 24, color: "#31BA96" },
{ category: "Vehicle", amount: "520.000", percentage: 36, color: "#31BA96" },
];
return (
<div className="w-[350px] bg-[#F9FAFC] p-[50px] overflow-y-auto">
{/* Stats Section */}
<div className="mb-16">
<h2 className="text-[20px] text-[#262A41] mb-8">Where your money go?</h2>
<div className="space-y-8">
{stats.map((stat, index) => (
<div key={index}>
<div className="flex justify-between items-center mb-2">
<span className="text-[13px] font-medium text-[#273240]">
{stat.category}
</span>
<span className="text-[13px] text-[#273240]">{stat.amount}</span>
</div>
<div className="relative h-[5px] bg-[#ECEFF5] rounded-[5px]">
<div
className="absolute top-0 left-0 h-full rounded-[5px]"
style={{
width: `${stat.percentage}%`,
backgroundColor: stat.color,
}}
/>
</div>
</div>
))}
</div>
</div>
{/* Save More Money Section */}
<div className="relative">
{/* Background Card */}
<div className="bg-[#EDF0F6] rounded-[15px] p-6 pt-[120px]">
{/* Illustration */}
<div className="absolute top-0 right-6 w-[84px] h-[72px]">
{/* Shopping Bags Illustration */}
<div className="relative">
{/* Orange Bag */}
<div className="absolute bottom-0 left-0 w-[84px] h-[42px] bg-gradient-to-br from-[#FFB966] to-[#FF895F] rounded-sm">
<div className="absolute top-[17px] left-[44px] w-[20px] h-[2px] bg-white/30"></div>
<div className="absolute -top-[8px] right-[30px] w-[16px] h-[16px] rounded-full bg-[#E0987C]"></div>
<div className="absolute bottom-[8px] w-full h-[1px] bg-[#D33234]"></div>
</div>
{/* Blue Bag */}
<div className="absolute bottom-[30px] right-0 w-[61px] h-[30px] bg-gradient-to-br from-[#85D4FF] to-[#51A4FF] rounded-sm">
<div className="absolute top-[15px] left-[32px] w-[16px] h-[0.65px] bg-[#0069D5]"></div>
<div className="absolute bottom-[6px] w-full h-[1px] bg-[#0069D5]"></div>
</div>
</div>
{/* Coins */}
<div className="absolute -top-2 left-0">
<div className="flex gap-2">
<div className="w-[26px] h-[73px] relative">
<div className="absolute bottom-0 w-[25px] h-[34px] bg-[#C5DDF0] rounded-full"></div>
<div className="absolute bottom-[8px] left-[3px] w-[15px] h-[64px] bg-[#7297C4]"></div>
</div>
<div className="w-[27px] h-[50px] relative">
<div className="absolute bottom-0 w-[26px] h-[31px] bg-[#C5DDF0] rounded-full"></div>
<div className="absolute bottom-[10px] left-[9px] w-[14px] h-[40px] bg-[#7297C4]"></div>
</div>
<div className="w-[22px] h-[36px] relative">
<div className="absolute bottom-0 w-[21px] h-[23px] bg-[#C5DDF0] rounded-full"></div>
<div className="absolute bottom-[6px] left-[1px] w-[15px] h-[29px] bg-[#7297C4]"></div>
</div>
</div>
</div>
</div>
<h3 className="text-[16px] font-semibold text-[#273240] mb-2">
Save more money
</h3>
<p className="text-[12px] text-[#404852] opacity-70 leading-[21px] mb-6">
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim.
</p>
{/* View Tips Button */}
<button className="w-full h-[45px] bg-[#101010] text-white rounded-[8px] font-semibold text-[13px] tracking-[0.15em] hover:bg-gray-900 transition-colors">
VIEW TIPS
</button>
</div>
</div>
</div>
);
}
"use client";
import Image from "next/image";
interface SidebarProps {
activeMenu: string;
setActiveMenu: (menu: string) => void;
}
export default function Sidebar({ activeMenu, setActiveMenu }: SidebarProps) {
const menuItems = [
{ id: "dashboard", label: "Dashboard", active: false },
{ id: "expenses", label: "Expenses", active: true },
{ id: "wallets", label: "Wallets", active: false },
{ id: "summary", label: "Summary", active: false },
{ id: "accounts", label: "Accounts", active: false },
{ id: "settings", label: "Settings", active: false },
];
return (
<div className="w-[280px] bg-black text-white p-20 flex flex-col">
{/* Profile Section */}
<div className="mb-16">
<div className="relative mb-5">
<div className="w-[72px] h-[72px] rounded-[13px] overflow-hidden">
<Image
src="/images/profile.png"
alt="Profile"
width={72}
height={72}
className="w-full h-full object-cover"
/>
</div>
{/* Notification Badge */}
<div className="absolute -top-3 -right-3 w-[29px] h-[29px] bg-[#DC3434] rounded-full flex items-center justify-center">
<span className="text-white text-[13px] font-semibold">4</span>
</div>
</div>
<h2 className="text-[30px] font-semibold mb-2">Samantha</h2>
<p className="text-[17px] opacity-60">samantha@email.com</p>
</div>
{/* Menu Items */}
<nav className="flex-1">
<ul className="space-y-[30px]">
{menuItems.map((item) => (
<li key={item.id}>
<button
onClick={() => setActiveMenu(item.id)}
className={`text-[25px] font-semibold transition-opacity ${
item.id === activeMenu ? "opacity-100" : "opacity-50 hover:opacity-75"
}`}
>
{item.label}
</button>
</li>
))}
</ul>
</nav>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment