From dcc11d6b0f9bc696716c6f8bb7c9f5471284a111 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 7 Feb 2026 23:52:02 +0000 Subject: [PATCH] v1 --- frontend/src/components/NavBarItem.tsx | 5 +- frontend/src/components/PremiumOverview.tsx | 137 ++++++++ frontend/src/layouts/Authenticated.tsx | 5 +- frontend/src/pages/dashboard.tsx | 5 +- frontend/src/pages/index.tsx | 328 +++++++++++--------- frontend/src/stores/styleSlice.ts | 50 +-- frontend/src/styles.ts | 26 ++ 7 files changed, 376 insertions(+), 180 deletions(-) create mode 100644 frontend/src/components/PremiumOverview.tsx diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx index eb155e3..1986306 100644 --- a/frontend/src/components/NavBarItem.tsx +++ b/frontend/src/components/NavBarItem.tsx @@ -1,6 +1,5 @@ -import React, {useEffect, useRef} from 'react' +import React, {useEffect, useRef, useState} from 'react' import Link from 'next/link' -import { useState } from 'react' import { mdiChevronUp, mdiChevronDown } from '@mdi/js' import BaseDivider from './BaseDivider' import BaseIcon from './BaseIcon' @@ -129,4 +128,4 @@ export default function NavBarItem({ item }: Props) { } return
{NavBarItemComponentContents}
-} +} \ No newline at end of file diff --git a/frontend/src/components/PremiumOverview.tsx b/frontend/src/components/PremiumOverview.tsx new file mode 100644 index 0000000..ca569d2 --- /dev/null +++ b/frontend/src/components/PremiumOverview.tsx @@ -0,0 +1,137 @@ + +import React, { useEffect, useState } from 'react'; +import axios from 'axios'; +import * as icon from '@mdi/js'; +import BaseIcon from './BaseIcon'; +import { useAppSelector } from '../stores/hooks'; +import dynamic from 'next/dynamic'; + +const Chart = dynamic(() => import('react-apexcharts'), { ssr: false }); + +export const PremiumOverview = () => { + const [trades, setTrades] = useState([]); + const [loading, setLoading] = useState(true); + const cardsStyle = useAppSelector((state) => state.style.cardsStyle); + const corners = useAppSelector((state) => state.style.corners); + + useEffect(() => { + const fetchTrades = async () => { + try { + const response = await axios.get('/trades'); + setTrades(response.data.rows || []); + } catch (error) { + console.error('Error fetching trades:', error); + } finally { + setLoading(false); + } + }; + fetchTrades(); + }, []); + + const stats = React.useMemo(() => { + if (!trades.length) return { pnl: 0, winRate: 0, profitFactor: 0, avgRR: 0, equity: [] }; + + let totalPnL = 0; + let wins = 0; + let grossProfit = 0; + let grossLoss = 0; + let totalRR = 0; + const equityData = []; + let runningPnL = 0; + + trades.forEach((trade) => { + const pnl = parseFloat(trade.pnl_amount) || 0; + totalPnL += pnl; + runningPnL += pnl; + equityData.push(runningPnL); + + if (trade.is_win) { + wins++; + grossProfit += pnl; + } else { + grossLoss += Math.abs(pnl); + } + totalRR += parseFloat(trade.rr_ratio) || 0; + }); + + return { + pnl: totalPnL, + winRate: (wins / trades.length) * 100, + profitFactor: grossLoss === 0 ? grossProfit : grossProfit / grossLoss, + avgRR: totalRR / trades.length, + equity: equityData, + }; + }, [trades]); + + const chartOptions: any = { + chart: { + type: 'area', + height: 350, + toolbar: { show: false }, + animations: { enabled: true }, + background: 'transparent', + }, + colors: ['#0ea5e9'], + fill: { + type: 'gradient', + gradient: { + shadeIntensity: 1, + opacityFrom: 0.45, + opacityTo: 0.05, + stops: [20, 100, 100, 100], + }, + }, + dataLabels: { enabled: false }, + stroke: { curve: 'smooth', width: 3 }, + grid: { borderColor: 'rgba(255,255,255,0.05)', strokeDashArray: 4 }, + xaxis: { + labels: { show: false }, + axisBorder: { show: false }, + axisTicks: { show: false }, + }, + yaxis: { + labels: { + style: { colors: '#64748b' }, + formatter: (val) => `$${val.toFixed(0)}`, + }, + }, + tooltip: { theme: 'dark' }, + }; + + const series = [{ name: 'Equity', data: stats.equity }]; + + if (loading) return
Analyzing performance...
; + + return ( +
+
+ {[ + { label: 'Net P&L', value: `$${stats.pnl.toLocaleString()}`, icon: icon.mdiCurrencyUsd, color: stats.pnl >= 0 ? 'text-green-400' : 'text-red-400' }, + { label: 'Win Rate', value: `${stats.winRate.toFixed(1)}%`, icon: icon.mdiChartPie, color: 'text-[#0ea5e9]' }, + { label: 'Profit Factor', value: stats.profitFactor.toFixed(2), icon: icon.mdiTrendingUp, color: 'text-purple-400' }, + { label: 'Avg RR', value: stats.avgRR.toFixed(2), icon: icon.mdiScaleBalance, color: 'text-yellow-400' }, + ].map((item, i) => ( +
+
+ +
+
{item.label}
+
{item.value}
+
+ ))} +
+ +
+
+

Equity Curve

+
+ All Accounts +
+
+
+ +
+
+
+ ); +}; diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx index 1b9907d..26c3572 100644 --- a/frontend/src/layouts/Authenticated.tsx +++ b/frontend/src/layouts/Authenticated.tsx @@ -1,5 +1,4 @@ -import React, { ReactNode, useEffect } from 'react' -import { useState } from 'react' +import React, { ReactNode, useEffect, useState } from 'react' import jwt from 'jsonwebtoken'; import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js' import menuAside from '../menuAside' @@ -126,4 +125,4 @@ export default function LayoutAuthenticated({ ) -} +} \ No newline at end of file diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index 215fa47..aaf4498 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -14,6 +14,7 @@ import { hasPermission } from "../helpers/userPermissions"; import { fetchWidgets } from '../stores/roles/rolesSlice'; import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator'; import { SmartWidget } from '../components/SmartWidget/SmartWidget'; +import { PremiumOverview } from '../components/PremiumOverview'; import { useAppDispatch, useAppSelector } from '../stores/hooks'; const Dashboard = () => { @@ -118,6 +119,8 @@ const Dashboard = () => { main> {''} + + {hasPermission(currentUser, 'CREATE_ROLES') && {page} } -export default Dashboard +export default Dashboard \ No newline at end of file diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index b8966d9..162b90c 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,166 +1,198 @@ - -import React, { useEffect, useState } from 'react'; +import React from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import Link from 'next/link'; -import BaseButton from '../components/BaseButton'; -import CardBox from '../components/CardBox'; -import SectionFullScreen from '../components/SectionFullScreen'; +import * as icon from '@mdi/js'; +import BaseIcon from '../components/BaseIcon'; import LayoutGuest from '../layouts/Guest'; -import BaseDivider from '../components/BaseDivider'; -import BaseButtons from '../components/BaseButtons'; import { getPageTitle } from '../config'; -import { useAppSelector } from '../stores/hooks'; -import CardBoxComponentTitle from "../components/CardBoxComponentTitle"; -import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'; - - -export default function Starter() { - const [illustrationImage, setIllustrationImage] = useState({ - src: undefined, - photographer: undefined, - photographer_url: undefined, - }) - const [illustrationVideo, setIllustrationVideo] = useState({video_files: []}) - const [contentType, setContentType] = useState('image'); - const [contentPosition, setContentPosition] = useState('background'); - const textColor = useAppSelector((state) => state.style.linkColor); - - const title = 'Premium Trading Journal' - - // Fetch Pexels image/video - useEffect(() => { - async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); - } - fetchData(); - }, []); - - const imageBlock = (image) => ( -
-
- - Photo by {image?.photographer} on Pexels - -
-
- ); - - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- -
- - Video by {video.user.name} on Pexels - -
-
) - } - }; +export default function LandingPage() { return ( -
+
- {getPageTitle('Starter Page')} + {getPageTitle('Ultra-Premium Trading Journal')} - -
- {contentType === 'image' && contentPosition !== 'background' - ? imageBlock(illustrationImage) - : null} - {contentType === 'video' && contentPosition !== 'background' - ? videoBlock(illustrationVideo) - : null} -
- - - -
-

This is a React.js/Node.js app generated by the Flatlogic Web App Generator

-

For guides and documentation please check - your local README.md and the Flatlogic documentation

+ {/* Navigation */} +
- -
-

© 2026 {title}. All rights reserved

- - Privacy Policy - -
+ + {/* Hero Section */} +
+
+
+
+ + New: AI-Powered Trade Insights +
+

+ Master Your Edge with
Precision Analytics. +

+

+ The ultra-premium trading journal designed for high-performance traders. Track performance, analyze psychology, and unlock AI insights to reach your goals. +

+
+ + Start Free Journaling + + + + Explore Features + +
+
+ + {/* Dashboard Preview */} +
+
+
+
+
+
+
+
+
+
dashboard.tradelogpro.ai
+
+
+ {[ + { label: 'Total PnL', value: '+$12,450.00', color: 'text-green-400', icon: icon.mdiCurrencyUsd }, + { label: 'Win Rate', value: '68.5%', color: 'text-[#0ea5e9]', icon: icon.mdiChartPie }, + { label: 'Profit Factor', value: '2.4', color: 'text-purple-400', icon: icon.mdiTrendingUp } + ].map((stat, i) => ( +
+
+ {stat.label} + +
+
{stat.value}
+
+ ))} +
+
+ {[40, 60, 45, 70, 85, 65, 90, 110, 95, 130, 150].map((h, i) => ( +
+ ))} +
+
+
+
+ + {/* Pricing Section */} +
+
+
+

Choose Your Plan

+

Scale your trading with tools built for your success level.

+
+
+ {/* Free Tier */} +
+
+

Free

+
$0/mo
+

Perfect for beginners tracking their first steps in the market.

+
+
    + {['Manual trade entry', 'Basic dashboard', 'Win Rate, PnL, Total Trades', 'Last 30 days history', 'Single account'].map((feat, i) => ( +
  • + + {feat} +
  • + ))} +
+ + Get Started + +
+ + {/* $1 Tier */} +
+
Most Popular
+
+

Pro

+
$1/mo
+

Comprehensive analytics for serious individual traders.

+
+
    + {['CSV bulk import', 'Full dashboard metrics', 'Equity curve visualization', 'Performance breakdowns', 'Trade duration analytics', 'Screenshot attachments', 'Mood/emotions tracker', 'Economic calendar'].map((feat, i) => ( +
  • + + {feat} +
  • + ))} +
+ + Go Pro Now + +
+ + {/* $10 Tier */} +
+
+

AI Elite

+
$10/mo
+

The ultimate edge with AI pattern recognition and insights.

+
+
    + {['Everything in Pro', 'AI-powered trade analysis', 'AI pattern recognition', 'Weakness identification', 'Risk size recommendations', 'Market condition classifier', 'Broker API auto-sync'].map((feat, i) => ( +
  • + + {feat} +
  • + ))} +
+ + Unlock Elite AI + +
+
+
+
+ + {/* Footer */} +
+
+
+
+ +
+ TradeLogPro +
+
+ Privacy + Terms + Twitter + Support +
+
+ © 2026 TRADELOGPRO. ALL RIGHTS RESERVED. +
+
+
); } -Starter.getLayout = function getLayout(page: ReactElement) { +LandingPage.getLayout = function getLayout(page: ReactElement) { return {page}; -}; - +}; \ No newline at end of file diff --git a/frontend/src/stores/styleSlice.ts b/frontend/src/stores/styleSlice.ts index e786387..f8f307d 100644 --- a/frontend/src/stores/styleSlice.ts +++ b/frontend/src/stores/styleSlice.ts @@ -32,30 +32,30 @@ interface StyleState { const initialState: StyleState = { - asideStyle: styles.midnightBlueTheme.aside, - asideScrollbarsStyle: styles.white.asideScrollbars, - asideBrandStyle: styles.white.asideBrand, - asideMenuItemStyle: styles.midnightBlueTheme.asideMenuItem, - asideMenuItemActiveStyle: styles.midnightBlueTheme.asideMenuItemActive, - activeLinkColor: styles.midnightBlueTheme.activeLinkColor, - asideMenuDropdownStyle: styles.white.asideMenuDropdown, - navBarItemLabelStyle: styles.midnightBlueTheme.navBarItemLabel, - navBarItemLabelHoverStyle: styles.midnightBlueTheme.navBarItemLabelHover, - navBarItemLabelActiveColorStyle: styles.midnightBlueTheme.navBarItemLabelActiveColor, - overlayStyle: styles.midnightBlueTheme.overlay, - darkMode: false, - bgLayoutColor: styles.midnightBlueTheme.bgLayoutColor, - iconsColor: styles.midnightBlueTheme.iconsColor, - cardsColor: styles.midnightBlueTheme.cardsColor, - focusRingColor: styles.midnightBlueTheme.focusRingColor, - corners: styles.midnightBlueTheme.corners, - cardsStyle: styles.midnightBlueTheme.cardsStyle, - linkColor: styles.midnightBlueTheme.linkColor, - websiteHeder: styles.midnightBlueTheme.websiteHeder, - borders: styles.midnightBlueTheme.borders, - shadow: styles.midnightBlueTheme.shadow, - websiteSectionStyle: styles.midnightBlueTheme.websiteSectionStyle, - textSecondary: styles.midnightBlueTheme.textSecondary, + asideStyle: styles.onyx.aside, + asideScrollbarsStyle: styles.onyx.asideScrollbars, + asideBrandStyle: styles.onyx.asideBrand, + asideMenuItemStyle: styles.onyx.asideMenuItem, + asideMenuItemActiveStyle: styles.onyx.asideMenuItemActive, + activeLinkColor: styles.onyx.activeLinkColor, + asideMenuDropdownStyle: styles.onyx.asideMenuDropdown, + navBarItemLabelStyle: styles.onyx.navBarItemLabel, + navBarItemLabelHoverStyle: styles.onyx.navBarItemLabelHover, + navBarItemLabelActiveColorStyle: styles.onyx.navBarItemLabelActiveColor, + overlayStyle: styles.onyx.overlay, + darkMode: true, // Force dark mode for premium feel + bgLayoutColor: styles.onyx.bgLayoutColor, + iconsColor: styles.onyx.iconsColor, + cardsColor: styles.onyx.cardsColor, + focusRingColor: styles.onyx.focusRingColor, + corners: styles.onyx.corners, + cardsStyle: styles.onyx.cardsStyle, + linkColor: styles.onyx.linkColor, + websiteHeder: styles.onyx.websiteHeder, + borders: styles.onyx.borders, + shadow: styles.onyx.shadow, + websiteSectionStyle: styles.onyx.websiteSectionStyle, + textSecondary: styles.onyx.textSecondary, }; @@ -100,4 +100,4 @@ export const styleSlice = createSlice({ // Action creators are generated for each case reducer function export const { setDarkMode, setStyle } = styleSlice.actions -export default styleSlice.reducer +export default styleSlice.reducer \ No newline at end of file diff --git a/frontend/src/styles.ts b/frontend/src/styles.ts index 8eecb3a..f8a8f9e 100644 --- a/frontend/src/styles.ts +++ b/frontend/src/styles.ts @@ -108,6 +108,32 @@ export const dataGridStyles = { }, }; +export const onyx: StyleObject = { + aside: "bg-[#121212] text-white", + asideScrollbars: "aside-scrollbars-gray", + asideBrand: "bg-[#0a0a0a] text-white", + asideMenuItem: "text-gray-400 hover:text-white hover:bg-[#1e1e1e]", + asideMenuItemActive: "font-bold text-white bg-[#0ea5e9]", + asideMenuDropdown: "bg-[#1e1e1e]", + navBarItemLabel: "text-white", + navBarItemLabelHover: "hover:text-[#0ea5e9]", + navBarItemLabelActiveColor: "text-[#0ea5e9]", + overlay: "bg-black/80", + activeLinkColor: "bg-[#0ea5e9]", + bgLayoutColor: "bg-[#0a0a0a]", + iconsColor: "text-[#0ea5e9]", + cardsColor: "bg-[#121212]", + focusRingColor: "focus:ring focus:ring-[#0ea5e9] focus:border-[#0ea5e9] focus:outline-none", + corners: "rounded-xl", + cardsStyle: "bg-[#121212] border border-white/10 shadow-2xl backdrop-blur-md", + linkColor: "text-[#0ea5e9]", + websiteHeder: "bg-[#0a0a0a]/80 backdrop-blur-md border-b border-white/10", + borders: "border-white/10", + shadow: "shadow-2xl", + websiteSectionStyle: "bg-[#0a0a0a] text-white", + textSecondary: "text-gray-400", +} + export const basic: StyleObject = { aside: 'bg-gray-800', asideScrollbars: 'aside-scrollbars-gray',