Skip to content

Commit

Permalink
chore
Browse files Browse the repository at this point in the history
: created shared sidebar component
  • Loading branch information
farabi-deriv committed Feb 26, 2025
1 parent 50f6c91 commit 31d6d9c
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 21 deletions.
53 changes: 53 additions & 0 deletions src/components/Sidebar/MenuContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";
import { ExternalLink, Home, LogOut, Moon } from "lucide-react";
import { useLogout } from "@/hooks/useLogout";
import { useClientStore } from "@/stores/clientStore";

export const MenuContent: React.FC = () => {
const [isDarkMode, setIsDarkMode] = React.useState(false);
const { isLoggedIn } = useClientStore();
const logout = useLogout();

return (
<div className="p-4 space-y-4">
<a
href="https://champion.trade/"
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-between cursor-pointer py-2"
>
<div className="flex items-center gap-3">
<Home className="w-5 h-5" />
<span>Go to Home</span>
</div>
<ExternalLink className="w-5 h-5" />
</a>
<div className="flex items-center justify-between cursor-pointer py-2">
<div className="flex items-center gap-3">
<Moon className="w-5 h-5" />
<span>Theme</span>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
className="sr-only peer"
checked={isDarkMode}
onChange={() => setIsDarkMode(!isDarkMode)}
/>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
{isLoggedIn && (
<button
className="flex items-center gap-3 cursor-pointer py-2 w-full hover:bg-gray-100"
onClick={logout}
>
<LogOut className="w-5 h-5" />
<span>Log out</span>
</button>
)}
</div>
);
};

export default MenuContent;
109 changes: 109 additions & 0 deletions src/components/Sidebar/PositionsContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { FC, useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { OPEN_POSITIONS, CLOSED_POSITIONS, Position } from "../PositionsSidebar/positionsSidebarStub";
import { useFilteredPositions } from "../PositionsSidebar/hooks/useFilteredPositions";
import { FilterDropdown } from "../PositionsSidebar/components/FilterDropdown";

export const PositionsContent: FC = () => {
const [isOpenTab, setIsOpenTab] = useState(true);
const navigate = useNavigate();
const [allPositions, setAllPositions] = useState<Position[]>(OPEN_POSITIONS);

const { filteredPositions, selectedFilter, handleFilterSelect } = useFilteredPositions({
isOpenTab,
allPositions,
closedPositions: CLOSED_POSITIONS,
});

useEffect(() => {
fetch("/api/positions")
.then(response => response.json())
.then(data => {
setAllPositions(data);
})
.catch(error => console.error("Error fetching positions:", error));
}, []);

return (
<div className="flex flex-col h-full">
<div className="p-6 flex-1 overflow-auto">
<div className="flex gap-2 p-1 bg-gray-100 rounded-lg">
<button
className={`flex-1 h-8 flex items-center justify-center rounded-lg transition-all ${
isOpenTab
? "bg-white text-black shadow-sm"
: "text-gray-500 hover:bg-gray-50"
}`}
onClick={() => setIsOpenTab(true)}
>
Open
</button>
<button
className={`flex-1 h-8 flex items-center justify-center rounded-lg transition-all ${
!isOpenTab
? "bg-white text-black shadow-sm"
: "text-gray-500 hover:bg-gray-50"
}`}
onClick={() => setIsOpenTab(false)}
>
Closed
</button>
</div>
<div className="mt-4">
<FilterDropdown
isOpenTab={isOpenTab}
selectedFilter={selectedFilter}
onFilterSelect={handleFilterSelect}
/>
</div>
<div className="mt-4 space-y-4">
{filteredPositions.map((position) => (
<div
key={position.id}
className="p-3 rounded-lg shadow-sm cursor-pointer"
onClick={() => navigate(`/contract/${position.id}`)}
>
<div className="flex justify-between text-sm font-medium">
<div className="flex flex-col items-start">
<div className="flex items-center gap-2">
<img src="/market icon.svg" alt="Market Icon" className="w-5 h-8 mb-1" />
</div>
<span className="mb-[5] font-light text-black-400">{position.type}</span>
<span className="text-s font-light text-gray-500 mb-4">{position.market}</span>
</div>
<div>
<div className="flex flex-col items-end">
{isOpenTab ? (
<span className="text-gray-500 w-35 text-xs flex items-center bg-gray-50 px-2 py-1 rounded-md border border-transparent hover:border-gray-300 mb-3">
<span className="mr-2"></span> {position.ticks}
</span>
) : (
<span className="text-red-600 bg-red-50 px-2 py-1 rounded-md text-xs font-medium mb-3">
Closed
</span>
)}
<span className="text-s font-light text-gray-400 mb-[2]">{position.stake}</span>
<span className={`text-sm ${position.profit.startsWith('+') ? 'text-[#008832]' : 'text-red-500'}`}>
{position.profit}
</span>
</div>
</div>
</div>
{isOpenTab && (
<button className="w-full h-6 flex items-center justify-center py-2 border border-black text-xs font-bold rounded-[8]">
Close {position.stake}
</button>
)}
</div>
))}
</div>
</div>
<div className="p-4 font-bold border-t flex justify-between mt-auto">
<span className="text-black-300">Total profit/loss: </span>
<span className="text-red-500">-1.50 USD</span>
</div>
</div>
);
};

export default PositionsContent;
44 changes: 44 additions & 0 deletions src/components/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { useEffect, useRef } from "react";

interface SidebarProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}

export const Sidebar: React.FC<SidebarProps> = ({ isOpen, onClose, title, children }) => {
const sidebarRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (sidebarRef.current && !sidebarRef.current.contains(event.target as Node)) {
onClose();
}
};

document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [onClose]);

return (
<div
className={`fixed left-[65px] h-full w-[22%] bg-white shadow-lg transform transition-transform duration-300 ease-in-out ${
isOpen ? "translate-x-0" : "-translate-x-[calc(100%+65px)]"
} z-[51] flex flex-col overflow-hidden`}
ref={sidebarRef}
>
<div className="p-4 border-b flex justify-between items-center">
<h2 className="text-lg font-bold">{title}</h2>
<button onClick={onClose} className="text-gray-600 hover:text-gray-900"></button>
</div>
<div className="flex-1 overflow-auto">
{children}
</div>
</div>
);
};

export default Sidebar;
3 changes: 3 additions & 0 deletions src/components/Sidebar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as Sidebar } from './Sidebar';
export { default as MenuContent } from './MenuContent';
export { default as PositionsContent } from './PositionsContent';
41 changes: 20 additions & 21 deletions src/layouts/MainLayout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { useMainLayoutStore } from "@/stores/mainLayoutStore"
import { Footer } from "./Footer"
import { Header } from "./Header"
import { SideNav } from "@/components/SideNav"
import PositionsSidebar from "@/components/PositionsSidebar/PositionsSidebar"
import MenuSidebar from "@/components/SideNav/MenuSidebar"
import { Sidebar, MenuContent, PositionsContent } from "@/components/Sidebar"

interface MainLayoutProps {
children: React.ReactNode
Expand Down Expand Up @@ -55,25 +54,25 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
<div className="flex flex-1 overflow-hidden">
{isLandscape ? (
<div className="flex flex-1">
<div
className={`${
isSidebarOpen ? "w-[22%]" : "w-0"
} transition-all duration-300 flex-shrink-0`}
>
<PositionsSidebar
isOpen={isSidebarOpen}
onClose={() => setSidebarOpen(false)}
/>
</div>
<div
className={`${
isMenuOpen ? "w-[22%]" : "w-0"
} transition-all duration-300 flex-shrink-0`}
>
<MenuSidebar
isOpen={isMenuOpen}
onClose={() => setMenuOpen(false)}
/>
<div className="relative z-[50]">
{isSidebarOpen && (
<Sidebar
isOpen={isSidebarOpen}
onClose={() => setSidebarOpen(false)}
title="Positions"
>
<PositionsContent />
</Sidebar>
)}
{isMenuOpen && (
<Sidebar
isOpen={isMenuOpen}
onClose={() => setMenuOpen(false)}
title="Menu"
>
<MenuContent />
</Sidebar>
)}
</div>
<main className="flex-1 flex flex-row transition-all duration-300">
{children}
Expand Down

0 comments on commit 31d6d9c

Please sign in to comment.