Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions frontend/app/components/dashboard/FeaturedGoalCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Plane, Calendar, ChevronRight } from 'lucide-react';
import CircularProgress from './CircularProgress';
import Button from '../ui/Button';

interface FeaturedGoalCardProps {
title: string;
Expand Down Expand Up @@ -82,13 +83,12 @@ const FeaturedGoalCard: React.FC<FeaturedGoalCardProps> = ({
</div>

<div className="flex items-center gap-4 mt-2">
<button className="flex-1 md:flex-none px-6 py-3.5 bg-cyan-500 hover:bg-cyan-400 text-[#061a1a] font-bold rounded-2xl transition-all duration-300 shadow-[0_10px_20px_rgba(0,212,192,0.2)] hover:shadow-[0_15px_30px_rgba(0,212,192,0.4)] active:scale-95 cursor-pointer">
<Button variant="primary" size="lg" className="flex-1 md:flex-none">
Contribute Now
</button>
<button className="flex items-center gap-1 text-cyan-400 font-bold hover:text-cyan-300 transition-colors cursor-pointer group px-2">
</Button>
<Button variant="ghost" rightIcon={<ChevronRight size={18} className="transition-transform group-hover:translate-x-1" />}>
View Details
<ChevronRight size={18} className="transition-transform group-hover:translate-x-1" />
</button>
</Button>
</div>
</div>
</div>
Expand Down
5 changes: 3 additions & 2 deletions frontend/app/components/dashboard/GoalCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import Link from "next/link";
import { Calendar, ChevronRight } from "lucide-react";
import Button from "../ui/Button";

export type GoalStatus = "On Track" | "At Risk" | "Completed" | "Paused";

Expand Down Expand Up @@ -111,9 +112,9 @@ export default function GoalCard({
</div>

<div className="flex items-center justify-between gap-3">
<button className="px-4 py-2.5 bg-cyan-500 hover:bg-cyan-400 text-[#061a1a] font-bold rounded-2xl transition-all shadow-[0_10px_18px_rgba(0,212,192,0.15)] active:scale-95">
<Button variant="primary" size="sm">
Contribute
</button>
</Button>

<Link
href={href}
Expand Down
19 changes: 11 additions & 8 deletions frontend/app/components/dashboard/GoalOverviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
CheckCircle2,
} from "lucide-react";
import CircularProgress from "./CircularProgress";
import Button from "../ui/Button";

interface Milestone {
label: string;
Expand Down Expand Up @@ -219,20 +220,22 @@ const GoalOverviewCard: React.FC<GoalOverviewCardProps> = ({

{/* ── Action buttons ── */}
<div className="mt-8 flex flex-wrap gap-3">
<button
<Button
variant="ghost"
size="md"
onClick={onEditGoal}
className="flex items-center gap-2 px-6 py-3 rounded-xl border border-white/10 bg-white/[0.04] hover:bg-white/[0.08] text-white font-semibold text-sm transition-all active:scale-95 cursor-pointer"
leftIcon={<Edit3 size={16} />}
>
<Edit3 size={16} />
Edit Goal
</button>
<button
</Button>
<Button
variant="primary"
size="md"
onClick={onAddFunds}
className="flex items-center gap-2 px-6 py-3 rounded-xl bg-cyan-500 hover:bg-cyan-400 text-[#061a1a] font-bold text-sm transition-all shadow-[0_8px_20px_rgba(0,212,192,0.2)] hover:shadow-[0_12px_28px_rgba(0,212,192,0.35)] active:scale-95 cursor-pointer"
leftIcon={<PlusCircle size={16} />}
>
<PlusCircle size={16} />
Add Funds
</button>
</Button>
</div>
</div>
);
Expand Down
5 changes: 3 additions & 2 deletions frontend/app/components/dashboard/PassedProposalCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React from "react";
import { CheckCircle2, ChevronRight } from "lucide-react";
import Button from "../ui/Button";

export interface PassedProposal {
id: string;
Expand Down Expand Up @@ -74,9 +75,9 @@ export default function PassedProposalCard({ proposal }: { proposal: PassedPropo
</div>

<div className="mt-5 flex items-center justify-between">
<button className="px-4 py-2.5 rounded-xl bg-cyan-500 hover:bg-cyan-400 text-[#061a1a] font-bold transition-all active:scale-95">
<Button variant="primary" size="sm">
View details
</button>
</Button>
<a
href="#"
className="inline-flex items-center gap-1 text-[#60f0ec] font-semibold hover:text-[#9ef0f0] transition-colors group"
Expand Down
17 changes: 11 additions & 6 deletions frontend/app/components/dashboard/ProposalCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use client";

import Button from "../ui/Button";

type ProposalCardProps = {
id: string;
title: string;
Expand Down Expand Up @@ -114,23 +116,26 @@ export default function ProposalCard({
</div>

<div>
<button
<Button
variant="primary"
size="lg"
onClick={onVote}
className="px-5 py-3 rounded-xl bg-linear-to-r from-sky-400 to-cyan-300 text-[#042024] font-semibold shadow-md hover:brightness-110"
>
{ctaLabel}
</button>
</Button>
</div>
</div>

{/* Mobile-only full-width Vote button placed as the last row */}
<div className="w-full mt-4 md:hidden">
<button
<Button
variant="primary"
size="lg"
fullWidth
onClick={onVote}
className="w-full px-5 py-3 rounded-xl bg-linear-to-r from-sky-400 to-cyan-300 text-[#042024] font-semibold shadow-md hover:brightness-110"
>
{ctaLabel}
</button>
</Button>
</div>
</div>
);
Expand Down
8 changes: 5 additions & 3 deletions frontend/app/components/dashboard/SavingsPoolCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import React from "react";
import Button from "../ui/Button";

export type RiskLevel = "Low Risk" | "Medium Risk" | "High Risk";

Expand Down Expand Up @@ -106,12 +107,13 @@ const SavingsPoolCard: React.FC<SavingsPoolCardProps> = ({
</div>

{/* Deposit Button */}
<button
<Button
variant="outline"
fullWidth
onClick={() => onDeposit?.(pool.id)}
className="w-full py-3 bg-transparent border border-cyan-500/30 text-cyan-400 rounded-xl font-semibold hover:bg-cyan-500/10 hover:border-cyan-500/50 transition-all duration-200 active:scale-[0.98] group-hover:border-cyan-500/50"
>
Deposit
</button>
</Button>
</div>
);
};
Expand Down
87 changes: 87 additions & 0 deletions frontend/app/components/ui/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"use client";

import React from "react";

type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "danger";
type ButtonSize = "sm" | "md" | "lg";

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
fullWidth?: boolean;
}

const variantStyles: Record<ButtonVariant, string> = {
primary:
"bg-cyan-500 hover:bg-cyan-400 text-[#061a1a] font-bold shadow-lg hover:shadow-[0_15px_30px_rgba(0,212,192,0.4)] active:scale-95",
secondary:
"bg-cyan-500/20 border border-cyan-500/30 text-cyan-300 font-semibold hover:bg-cyan-500/30",
outline:
"border border-cyan-400/40 text-cyan-200 hover:text-white hover:border-cyan-300",
ghost:
"bg-transparent text-[#5e8c96] hover:text-[#e2f8f8]",
danger:
"bg-red-500/20 border border-red-500/30 text-red-300 hover:bg-red-500/30 hover:text-red-200",
};

const sizeStyles: Record<ButtonSize, string> = {
sm: "px-3 py-1.5 text-xs rounded-lg gap-1",
md: "px-5 py-2.5 text-sm rounded-xl gap-2",
lg: "px-6 py-3.5 text-base rounded-2xl gap-2",
};

export default function Button({
variant = "primary",
size = "md",
loading = false,
leftIcon,
rightIcon,
fullWidth = false,
disabled,
children,
className = "",
...props
}: ButtonProps) {
const baseStyles =
"inline-flex items-center justify-center transition-all duration-300 cursor-pointer disabled:cursor-not-allowed disabled:opacity-70";

const widthClass = fullWidth ? "w-full" : "";

return (
<button
className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]} ${widthClass} ${className}`}
disabled={disabled || loading}
{...props}
>
{loading ? (
<svg
className="animate-spin h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
/>
</svg>
) : (
leftIcon
)}
{children}
{!loading && rightIcon}
</button>
);
}
10 changes: 6 additions & 4 deletions frontend/app/dashboard/notifications/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React, { useState } from "react";
import { Bell, CheckCheck, ArrowUpRight, ShieldCheck, Target, Megaphone } from "lucide-react";
import Button from "../../../components/ui/Button";

type NotifType = "transaction" | "governance" | "milestone" | "announcement";

Expand Down Expand Up @@ -77,13 +78,14 @@ export default function NotificationsPage() {
</div>
</div>
{unread > 0 && (
<button
<Button
variant="secondary"
size="sm"
onClick={markAllRead}
className="flex items-center gap-2 px-4 py-2 rounded-xl border border-cyan-500/20 bg-cyan-500/10 text-cyan-300 text-sm font-medium hover:bg-cyan-500/20 transition-colors cursor-pointer"
leftIcon={<CheckCheck size={15} />}
>
<CheckCheck size={15} />
Mark all read
</button>
</Button>
)}
</div>

Expand Down
13 changes: 8 additions & 5 deletions frontend/app/dashboard/portfolio/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React, { useState } from "react";
import { Briefcase, TrendingUp, TrendingDown, Download, MoreHorizontal } from "lucide-react";
import Button from "../../components/ui/Button";

const ASSETS = [
{ name: "USDC Flexible", type: "Savings", balance: 2400, value: 2400, apy: 6.5, pnl: 156, pnlPct: 6.9 },
Expand Down Expand Up @@ -66,14 +67,16 @@ export default function PortfolioPage() {
<p className="text-[#5e8c96] text-sm m-0">All assets and positions</p>
</div>
</div>
<button
<Button
variant="secondary"
size="sm"
onClick={handleExport}
disabled={exporting}
className="flex items-center gap-2 px-4 py-2 rounded-xl border border-cyan-500/20 bg-cyan-500/10 text-cyan-300 text-sm font-medium hover:bg-cyan-500/20 transition-colors cursor-pointer disabled:opacity-50"
loading={exporting}
leftIcon={<Download size={15} />}
>
<Download size={15} />
{exporting ? "Exporting…" : "Export CSV"}
</button>
Export CSV
</Button>
</div>

{/* Summary cards */}
Expand Down
10 changes: 6 additions & 4 deletions frontend/app/dashboard/referrals/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, { useState } from "react";
import { Users, Copy, Check, Trophy, Gift, TrendingUp } from "lucide-react";
import { env } from "../../lib/env";
import Button from "../../components/ui/Button";

const REFERRED_USERS = [
{ name: "Alice K.", joined: "Apr 20, 2026", status: "Active", reward: "$12.00" },
Expand Down Expand Up @@ -71,13 +72,14 @@ export default function ReferralsPage() {
<div className="flex-1 px-4 py-2.5 rounded-xl bg-white/5 border border-white/8 text-sm text-[#8cc0c7] font-mono truncate">
{REFERRAL_LINK}
</div>
<button
<Button
variant="secondary"
size="sm"
onClick={copy}
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-cyan-500/15 border border-cyan-500/25 text-cyan-300 text-sm font-medium hover:bg-cyan-500/25 transition-colors cursor-pointer shrink-0"
leftIcon={copied ? <Check size={15} /> : <Copy size={15} />}
>
{copied ? <Check size={15} /> : <Copy size={15} />}
{copied ? "Copied!" : "Copy"}
</button>
</Button>
</div>
<p className="text-xs text-[#4a7080] mt-3 m-0">
Earn $12 USDC for every friend who deposits at least $100.
Expand Down
11 changes: 6 additions & 5 deletions frontend/app/dashboard/savings-pools/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SavingsPoolCard, {
type SavingsPool,
} from "@/app/components/dashboard/SavingsPoolCard";
import { useToast } from "@/app/context/ToastContext";
import Button from "../../components/ui/Button";

export default function GoalBasedSavingsPage() {
const [searchQuery, setSearchQuery] = useState("");
Expand Down Expand Up @@ -134,9 +135,9 @@ export default function GoalBasedSavingsPage() {
<List size={18} />
</button>
</div>
<button className="min-h-11 rounded-xl bg-cyan-500 px-5 py-2.5 font-bold text-[#061a1a] shadow-lg transition-all hover:bg-cyan-400 active:scale-95" aria-label="Create new goal">
<Button variant="primary" size="md" aria-label="Create new goal">
Create New Goal
</button>
</Button>
</div>
</div>

Expand Down Expand Up @@ -224,12 +225,12 @@ export default function GoalBasedSavingsPage() {
Try adjusting your search terms or filters to find what you're
looking for.
</p>
<button
<Button
variant="secondary"
onClick={() => setSearchQuery("")}
className="mt-6 px-6 py-2.5 bg-cyan-500/10 border border-cyan-500/30 text-cyan-400 rounded-xl font-medium hover:bg-cyan-500/20 transition-all"
>
Clear Search
</button>
</Button>
</div>
)}
</div>
Expand Down
Loading