Skip to content
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# dependencies
/node_modules
/.env
/.pnp
.pnp.*
.yarn/*
Expand Down
63 changes: 58 additions & 5 deletions app/dashboard/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,60 @@
import React from "react";
"use client";

const Settings = () => {
return <div>Settings</div>;
};
import NotificationTab from "@/components/settings/Notification";
import ProfileTab from "@/components/settings/ProfileTab";
import SecurityTab from "@/components/settings/SecurityTab";
import { FileIcon, ShieldIcon, BellIcon, IdCardIcon } from "@/public/svg"; // example icons
import { useState } from "react";

export default Settings;
const tabs = [
{ key: "profile", label: "Account Info", icon: FileIcon },
{ key: "security", label: "Security", icon: ShieldIcon },
{ key: "notification", label: "Notification", icon: BellIcon },
{ key: "identity", label: "Identity Verification", icon: IdCardIcon },
];

export default function Settings() {
const [activeTab, setActiveTab] = useState("profile");

return (
<div className="max-w-[1086px] md:p-6 p-2 pt-6 md:pt-auto">
{/* Tabs */}
<div className="overflow-x-auto whitespace-nowrap scrollbar-hide">
<div className="flex space-x-6 border-b text-xs md:text-base border-gray-200 mb-6">
{tabs.map((tab) => {
const IconComponent = tab.icon;
return (
<button
key={tab.key}
onClick={() => setActiveTab(tab.key)}
className={`flex items-center space-x-2 pb-2 border-b-2 transition-colors ${
activeTab === tab.key
? "border-yellow-500 text-yellow-600 font-semibold"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
{IconComponent && <IconComponent className="w-4 h-4" />}
<span>{tab.label}</span>
</button>
);
})}
</div>
</div>

{/* Tab Content */}
<div>
{activeTab === "profile" && <ProfileTab />}
{activeTab === "security" && <SecurityTab />}
{activeTab === "notification" && <NotificationTab />}
{activeTab === "identity" && (
<div>
{/* TODO: Integrate Identity Verification API when available */}
<p className="text-gray-500">
Identity Verification UI comming soon...
</p>
</div>
)}
</div>
</div>
);
}
25 changes: 19 additions & 6 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@

@custom-variant dark (&:is(.dark *));

/* globals.css */
@layer utilities {
.scrollbar-hide {
-webkit-overflow-scrolling: touch; /* smooth scrolling on iOS */
-ms-overflow-style: none; /* Internet Explorer/Edge */
scrollbar-width: none; /* Firefox */
}
.scrollbar-hide::-webkit-scrollbar {
display: none; /* Safari and Chrome */
height: 0;
}
}

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
Expand Down Expand Up @@ -47,7 +60,7 @@
--color-brand-primary: var(--brand-primary);
--color-brand-primary-hover: var(--brand-primary-hover);
--color-brand-secondary: var(--brand-secondary);
--color-brand-secondary-hover: var(--brand-secondary-hover);
--color-brand-secondary-hover: var(--brand-secondary-hover);
--color-text-primary: var(--text-primary);
--color-text-secondary: var(--text-secondary);
--color-text-tertiary: var(--text-tertiary);
Expand Down Expand Up @@ -128,7 +141,7 @@
--brand-primary: #ffd552;
--brand-primary-hover: #e6c04a;
--brand-secondary: #e9eaee;
--brand-secondary-hover: #d3d4d8;
--brand-secondary-hover: #d3d4d8;

/* Text Colors */
--text-primary: #262626;
Expand Down Expand Up @@ -160,8 +173,8 @@
--bg-dropdown-hover: rgba(224, 224, 224, 0.7);
--bg-selector-icon: rgba(183, 183, 183, 0.6);
--bg-contact-blue: #d9e9ff;
--bg-contact-yellow: #fdfcbd;
--bg-contact-orange: #E58600;
--bg-contact-yellow: #fdfcbd;
--bg-contact-orange: #e58600;

/* Border Colors */
--border-crypto-card: rgba(121, 121, 121, 0.4);
Expand All @@ -178,8 +191,8 @@
--contact-icon-yellow: #e8b300;

/* Mobile Admin Gradient Colors */
--admin-gradient-blue: #A0C3FD;
--admin-gradient-yellow: #FFE79C;
--admin-gradient-blue: #a0c3fd;
--admin-gradient-yellow: #ffe79c;
}

.dark {
Expand Down
70 changes: 27 additions & 43 deletions components/header/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,41 @@
// components/Sidebar.tsx
"use client";

import {
Home,
Wallet,
User,
ArrowDownUp,
ChevronLeft,
Copy,
Settings,
} from "lucide-react";
import { useSidebarStore } from "@/store/sidebarStore";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { usePathname, useRouter } from "next/navigation";
import Image from "next/image";

const initialMenuItems = [
{ icon: Home, label: "Dashboard", href: "/dashboard", active: true },
const menuItems = [
{ icon: Home, label: "Dashboard", href: "/dashboard" },
{ icon: ArrowDownUp, label: "Convert", href: "/dashboard/convert" },
{ icon: Wallet, label: "Transaction", href: "/dashboard/transactions" },
{ icon: User, label: "Profile", href: "/dashboard/profile" },
{ icon: Settings, label: "Setting", href: "/dashboard/settings" },
];

export default function Sidebar() {
const pathname = usePathname();
const router = useRouter();
const { isCollapsed, isMobileOpen, closeMobile, toggleCollapse } =
useSidebarStore();

const [menuItems, setMenuItems] = useState(initialMenuItems);
const router = useRouter();

const handleMenuClick = (href: string, label: string) => {
setMenuItems((prev) =>
prev.map((item) => ({
...item,
active: item.label === label,
}))
);
const handleMenuClick = (href: string) => {
router.push(href);
};

// Close mobile sidebar when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const sidebar = document.getElementById("sidebar");
const target = event.target as Node;

// Also close on click of a menu item on mobile
const menuButton = (event.target as HTMLElement).closest("button");
if (
isMobileOpen &&
Expand Down Expand Up @@ -109,30 +101,27 @@ export default function Sidebar() {
>
<div className="flex items-center gap-2">
{!isCollapsed && (
<div className="">
<Image src="/logo.png" alt="Logo" width={124} height={42} />
</div>
<Image src="/logo.png" alt="Logo" width={124} height={42} />
)}
</div>
<button
onClick={toggleCollapse}
className="hidden lg:flex p-2 rounded-full hover:bg-gray-100"
>
{isCollapsed ? (
<div className="">
<Image
src="/logo-mini.svg"
alt="Logo"
width={20}
height={20}
/>
</div>
<Image
src="/logo-mini.svg"
alt="Logo"
width={20}
height={20}
/>
) : (
<ChevronLeft className="w-5 h-5 text-gray-500" />
)}
</button>
</div>
</div>

{/* User Profile Section - Mobile */}
<div className="lg:hidden p-4 rounded-lg bg-[linear-gradient(240deg,rgba(160,195,253,0.40)_-1.74%,rgba(255,231,156,0.40)_99.3%)] mb-4">
<div className="flex items-center gap-4">
Expand Down Expand Up @@ -164,15 +153,21 @@ export default function Sidebar() {
</div>
</div>

{/* Menu */}
<ul className="space-y-3 font-medium">
{menuItems.map((item, index) => {
const IconComponent = item.icon;
const isActive =
item.href === "/dashboard"
? pathname === "/dashboard" // Only exact match for dashboard
: pathname.startsWith(item.href); // Prefix match for others

return (
<li key={index}>
<button
onClick={() => handleMenuClick(item.href, item.label)}
onClick={() => handleMenuClick(item.href)}
className={`flex items-center text-text-tertiary rounded-full group ${
item.active
isActive
? "bg-brand-primary text-black"
: "bg-transparent hover:bg-brand-primary/20"
} transition-all duration-300 ${
Expand All @@ -184,25 +179,14 @@ export default function Sidebar() {
>
<IconComponent
className={`w-5 h-5 text-text-tertiary transition duration-75 group-hover:text-black ${
item.active ? "text-black" : ""
isActive ? "text-black" : ""
}`}
/>
{!isCollapsed && <span className="ml-3">{item.label}</span>}
</button>
</li>
);
})}

{/* Logout button */}
{/* <li className="pt-4 mt-4 border-t border-gray-200">
<a
href="/logout"
className="flex items-center p-2 text-gray-900 rounded-lg hover:bg-red-50 hover:text-red-600 group"
title={isCollapsed ? "Logout" : ""}>
<LogOut className="w-5 h-5 text-gray-500 transition duration-75 group-hover:text-red-600" />
{!isCollapsed && <span className="ml-3">Logout</span>}
</a>
</li> */}
</ul>
</div>
</aside>
Expand Down
83 changes: 83 additions & 0 deletions components/settings/Notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use client";

import { useState } from "react";

export default function NotificationTab() {
const [settings, setSettings] = useState({
inApp: true,
sms: false,
email: true,
transaction: true,
});

const toggle = (key: keyof typeof settings) =>
setSettings({ ...settings, [key]: !settings[key] });

return (
<div className="space-y-6">
<div className="space-y-6 border border-gray-200 p-4 rounded-md">
<h1 className="text-gray-500 font-semibold border-b pb-4">
Notification Push
</h1>
{[
{ key: "inApp", label: "In-app notification" },
{ key: "sms", label: "SMS notification" },
{ key: "email", label: "Email notification" },
{ key: "transaction", label: "Transaction Alert" },
].map((item) => (
<div
key={item.key}
className="flex justify-between items-center pb-2"
>
<div>
<h3 className="font-medium">{item.label}</h3>
<p className="text-xs md:text-sm text-gray-500">
{/* TODO: Add API integration later */}
Placeholder description for {item.label}
</p>
</div>

<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={settings[item.key as keyof typeof settings]}
onChange={() => toggle(item.key as keyof typeof settings)}
className="sr-only"
/>
<span
className={`w-11 h-6 flex items-center rounded-full p-1 ${
settings[item.key as keyof typeof settings]
? "bg-gradient-to-r from-[#FFA200] to-[#3B82F6]"
: "bg-gray-300"
}`}
>
<span
className={`bg-white w-4 h-4 rounded-full shadow transform duration-300 ${
settings[item.key as keyof typeof settings]
? "translate-x-5"
: ""
}`}
/>
</span>
</label>
</div>
))}{" "}
</div>

{/* Desktop Footer (visible from sm and up) */}
<div className="hidden sm:flex space-x-4 mt-6 justify-end text-sm">
<button className="bg-yellow-500 px-6 py-2 rounded">
Save Changes
</button>
<button className="border px-6 py-2 rounded">Cancel</button>
</div>

{/* Mobile Sticky Footer (only visible on xs screens) */}
<div className="sm:hidden fixed bottom-0 left-0 right-0 text-sm font-semibold p-3">
<button className="bg-yellow-500 px-6 py-3 rounded w-full">
Save Changes
</button>
</div>
</div>
);
}
Loading
Loading