diff --git a/dashboard/.gitignore b/dashboard/.gitignore new file mode 100644 index 00000000..139ed98a --- /dev/null +++ b/dashboard/.gitignore @@ -0,0 +1,5 @@ +*.db +*.py[cod] +.web +__pycache__/ +assets/external/ diff --git a/dashboard/assets/favicon.ico b/dashboard/assets/favicon.ico new file mode 100644 index 00000000..166ae995 Binary files /dev/null and b/dashboard/assets/favicon.ico differ diff --git a/dashboard/dashboard/__init__.py b/dashboard/dashboard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboard.py b/dashboard/dashboard/dashboard.py new file mode 100644 index 00000000..a68f221b --- /dev/null +++ b/dashboard/dashboard/dashboard.py @@ -0,0 +1,6 @@ +import reflex as rx + +from .dashboardApp.main import dashboardApp + +app = rx.App(theme=rx.theme(accent_color="violet", scaling="95%")) +app.add_page(dashboardApp(), route="/", title="Reflex Dashboard") diff --git a/dashboard/dashboard/dashboardApp/__init__.py b/dashboard/dashboard/dashboardApp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardApp/main.py b/dashboard/dashboard/dashboardApp/main.py new file mode 100644 index 00000000..50868cf9 --- /dev/null +++ b/dashboard/dashboard/dashboardApp/main.py @@ -0,0 +1,33 @@ +import reflex as rx +from typing import Callable + +from .style import DashboardAppStyle + +from ..dashboardComponents.sideBar.main import dashboardSidebar +from ..dashboardComponents.navBar.main import dashboardNavbar +from ..dashboardComponents.statBar.main import dashboardStatbar +from ..dashboardComponents.trafficBar.main import dashboardTrafficbar +from ..dashboardComponents.expenseBar.main import dashboardExpensebar +from ..dashboardComponents.employeeTable.main import dashbaordEmployee + +dashboardContentArea: Callable[[], rx.vstack()] = lambda: rx.vstack( + dashboardNavbar(), + dashboardStatbar(), + rx.hstack( + dashboardTrafficbar(), + dashboardExpensebar(), + **DashboardAppStyle.trafficAndExpenses, + ), + rx.box(dashbaordEmployee(), width="100%", padding="1em"), + **DashboardAppStyle.contentArea, +) + +# ... Main app function here ... +dashboardApp: Callable[[], rx.hstack] = lambda: rx.hstack( + # ... App side bar ... + dashboardSidebar(), + # ... App main content area ... + dashboardContentArea(), + # ... App style object ... + **DashboardAppStyle.base, +) diff --git a/dashboard/dashboard/dashboardApp/style.py b/dashboard/dashboard/dashboardApp/style.py new file mode 100644 index 00000000..5e98334e --- /dev/null +++ b/dashboard/dashboard/dashboardApp/style.py @@ -0,0 +1,41 @@ +from dataclasses import dataclass, field +from typing import Dict + + +@dataclass +class DashboardAppStyle: + base: Dict[str, str] = field( + default_factory=lambda: { + "width": "100%", + "height": "100vh", + "spacing": "0", + "overscroll_behavior": "none", + } + ) + + contentArea: Dict[str, str] = field( + default_factory=lambda: { + "width": "100%", + "height": "100%", + "position": "relative", + "overflow": "scroll", + "spacing": "2", + } + ) + + trafficAndExpenses: Dict[str, str] = field( + default_factory=lambda: { + "width": "100%", + "height": "100%", + "display": "grid", + "grid_template_columns": [ + f"repeat({i}, minmax(0, 1fr))" for i in [1, 1, 1, 1, 4, 4] + ], + "padding": "1em", + "row_gap": "1em", + "column_gap": "1em", + } + ) + + +DashboardAppStyle: DashboardAppStyle = DashboardAppStyle() diff --git a/dashboard/dashboard/dashboardComponents/__init__.py b/dashboard/dashboard/dashboardComponents/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardComponents/employeeTable/__init__.py b/dashboard/dashboard/dashboardComponents/employeeTable/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardComponents/employeeTable/main.py b/dashboard/dashboard/dashboardComponents/employeeTable/main.py new file mode 100644 index 00000000..478cf8a7 --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/employeeTable/main.py @@ -0,0 +1,165 @@ +from typing import Callable, Dict + +import reflex as rx + + +employeeDataSet: list[dict[str, str]] = [ + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-4.png", + "name": "Alice Skydancer", + "job": "Engineer", + "email": "alice.dancer@skynet.com", + "rate": "34", + "phone": "+1 213-555-0173", + }, + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-6.png", + "name": "Frank Codebreaker", + "job": "Developer", + "email": "frank.breaker@code.io", + "rate": "58", + "phone": "+1 310-555-0192", + }, + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-7.png", + "name": "Diana Windwalker", + "job": "Architect", + "email": "diana.walker@designs.org", + "rate": "72", + "phone": "+1 415-555-0124", + }, + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-11.png", + "name": "Nina Seawatcher", + "job": "Manager", + "email": "nina.watcher@seaviews.org", + "rate": "93", + "phone": "+1 408-555-0165", + }, + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-9.png", + "name": "Sara Cloudshaper", + "job": "Engineer", + "email": "sara.shaper@cloudtech.net", + "rate": "50", + "phone": "+1 323-555-0112", + }, + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-10.png", + "name": "Tom Trailfinder", + "job": "Designer", + "email": "tom.finder@trails.net", + "rate": "27", + "phone": "+1 805-555-0147", + }, + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-12.png", + "name": "Oliver Hillclimber", + "job": "Developer", + "email": "oliver.climb@hillcode.com", + "rate": "65", + "phone": "+1 909-555-0189", + }, + { + "avatar": "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-8.png", + "name": "Eli Stonecarver", + "job": "Manager", + "email": "eli.carver@stones.com", + "rate": "88", + "phone": "+1 626-555-0186", + }, +] + + +employeTableMenuItem: Callable[[str, str, str], rx.hstack] = ( + lambda name, tag, color: rx.hstack( + rx.icon(tag=tag, size=14), + rx.text(name, font_size="11px", color_scheme="gray"), + color=color, + width="100%", + justify="start", + align="center", + ) +) + +emplyeeTableMenu: Callable[[], rx.menu.root] = lambda: rx.menu.root( + rx.menu.trigger( + rx.icon(tag="ellipsis", size=14, cursor="pointer"), + ), + rx.menu.content( + rx.menu.item(employeTableMenuItem("Send message", "mails", "inherit")), + rx.menu.item(employeTableMenuItem("Add note", "notepad-text", "inherit")), + rx.menu.item(employeTableMenuItem("Terminate Contract", "trash-2", "red")), + size="1", + ), +) + + +employeeDataRow: Callable[[Dict[str, str]], rx.table.row] = lambda data: rx.table.row( + rx.table.cell( + rx.hstack( + rx.avatar(src=data["avatar"], size="2", radius="full"), + rx.vstack( + rx.text(data["name"], font_size="12px", weight="medium"), + rx.text( + data["job"], + color_scheme="gray", + font_size="10px", + weight="medium", + ), + spacing="0", + ), + align="center", + ), + ), + rx.table.cell( + rx.vstack( + rx.text(data["email"], font_size="12px"), + rx.text("Email", color_scheme="gray", font_size="10px"), + spacing="0", + ) + ), + rx.table.cell( + rx.text(data["phone"], color_scheme="gray", font_size="11px", weight="regular"), + ), + rx.table.cell( + rx.vstack( + rx.text(f"${data['rate']}.0/h", font_size="12px"), + rx.text("Rate", font_size="10px", color_scheme="gray"), + spacing="0", + ) + ), + rx.table.cell( + rx.hstack( + rx.button( + rx.icon(tag="pencil", size=13, color="gray"), + variant="ghost", + cursor="pointer", + ), + emplyeeTableMenu(), + ), + ), + width="100%", + align="center", + white_space="nowrap", +) + + +dashbaordEmployee: Callable[[], rx.table.root] = lambda: rx.table.root( + rx.table.header( + rx.table.row( + rx.foreach( + ["Employee", "Email", "Phone", "Rate (USD)", ""], + lambda title: rx.table.column_header_cell( + rx.text(title, font_size="12px", weight="bold") + ), + ) + ), + ), + rx.table.body( + *[employeeDataRow(data) for data in employeeDataSet], + ), + width="100%", + variant="surface", + size="2", +) diff --git a/dashboard/dashboard/dashboardComponents/expenseBar/__init__.py b/dashboard/dashboard/dashboardComponents/expenseBar/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardComponents/expenseBar/main.py b/dashboard/dashboard/dashboardComponents/expenseBar/main.py new file mode 100644 index 00000000..22dc6399 --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/expenseBar/main.py @@ -0,0 +1,58 @@ +import reflex as rx +from typing import Callable + +from .style import DashboardExpenseBarStyle + +expenseDataSet = [ + {"month": "Jan", "new": 45, "overdue": 12}, + {"month": "Feb", "new": 38, "overdue": 15}, + {"month": "Mar", "new": 50, "overdue": 8}, + {"month": "Apr", "new": 60, "overdue": 20}, + {"month": "May", "new": 70, "overdue": 10}, + {"month": "Jun", "new": 55, "overdue": 18}, + {"month": "Jul", "new": 48, "overdue": 25}, + {"month": "Aug", "new": 65, "overdue": 12}, + {"month": "Sep", "new": 58, "overdue": 16}, + {"month": "Oct", "new": 62, "overdue": 14}, + {"month": "Nov", "new": 50, "overdue": 22}, + {"month": "Dec", "new": 75, "overdue": 9}, +] + + +dashboardExpensebar: Callable[[], rx.vstack] = lambda: rx.vstack( + rx.vstack( + rx.heading("Monthly Expenses", size="4", weight="bold"), + rx.text("January - December 2024", size="1", color=rx.color("slate", 11)), + spacing="1", + ), + rx.divider(height="1rem", opacity="0"), + rx.recharts.bar_chart( + rx.recharts.graphing_tooltip( + label_style={"fontWeight": "700"}, item_style={"padding": "0px"} + ), + rx.recharts.cartesian_grid(horizontal=True, vertical=False), + *[ + rx.recharts.bar( + data_key=name, + fill=rx.color("gray", 8) if index == 1 else rx.color("violet", 10), + ) + for index, name in enumerate(["new", "overdue"]) + ], + rx.recharts.x_axis(data_key="month", axis_line=False), + data=expenseDataSet, + height=300, + bar_size=10, + bar_gap=5, + bar_category_gap="2%", + ), + rx.vstack( + rx.heading("Expenses up by 5.2% this year", size="2", weight="bold"), + rx.text( + "Showing total expenses for the last 12 months", + size="1", + color=rx.color("slate", 11), + ), + spacing="1", + ), + **DashboardExpenseBarStyle.base, +) diff --git a/dashboard/dashboard/dashboardComponents/expenseBar/style.py b/dashboard/dashboard/dashboardComponents/expenseBar/style.py new file mode 100644 index 00000000..1960700d --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/expenseBar/style.py @@ -0,0 +1,21 @@ +from dataclasses import dataclass, field +from typing import Dict + +import reflex as rx + + +@dataclass +class DashboardExpenseBarStyle: + base: Dict[str, str] = field( + default_factory=lambda: { + "height": "100%", + "padding": "1em", + "grid_column": "span 3", + "border_radius": "8px", + "background": rx.color("gray", 2), + "border": f"1px solid {rx.color('gray', 4)}", + } + ) + + +DashboardExpenseBarStyle: DashboardExpenseBarStyle = DashboardExpenseBarStyle() diff --git a/dashboard/dashboard/dashboardComponents/navBar/__init__.py b/dashboard/dashboard/dashboardComponents/navBar/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardComponents/navBar/main.py b/dashboard/dashboard/dashboardComponents/navBar/main.py new file mode 100644 index 00000000..b8d90e46 --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/navBar/main.py @@ -0,0 +1,58 @@ +import reflex as rx +from typing import Callable + +from .style import DashboardNavBarStyle + + +navbarAvatar: Callable[[], rx.avatar] = lambda: rx.avatar( + src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-5.png", + size="2", + radius="full", +) + +utilityIconMap = { + 2: {"icon": "info", "msg": "Help & Support"}, + 1: {"icon": "bell", "msg": "Notifications"}, + 0: {"icon": "activity", "msg": "Activity"}, +} + +navbarUtilityIcons: Callable[[str, str], rx.hover_card] = ( + lambda icon, msg: rx.hover_card.root( + rx.hover_card.trigger( + rx.icon(tag=icon, size=14, color=rx.color("slate", 11)), + cursor="pointer", + ), + rx.hover_card.content(rx.text(msg, size="1", weight="bold"), size="1"), + ) +) + +theme = rx.el.button( + rx.color_mode.icon( + light_component=rx.icon( + "moon", + size=14, + color=rx.color("slate", 12), + ), + dark_component=rx.icon( + "sun", + size=14, + color=rx.color("slate", 12), + ), + ), + on_click=rx.toggle_color_mode, +) + +dashboardNavbar: Callable[[], rx.hstack] = lambda: rx.hstack( + rx.hstack( + *[ + navbarUtilityIcons(item["icon"], item["msg"]) + for item in utilityIconMap.values() + ], + theme, + align="center", + spacing="5", + ), + rx.divider(width="0.75px", height="24px", orientation="vertical"), + navbarAvatar(), + **DashboardNavBarStyle.base, +) diff --git a/dashboard/dashboard/dashboardComponents/navBar/style.py b/dashboard/dashboard/dashboardComponents/navBar/style.py new file mode 100644 index 00000000..19585c88 --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/navBar/style.py @@ -0,0 +1,26 @@ +from dataclasses import dataclass, field +from typing import Dict + +import reflex as rx + + +@dataclass +class DashboardNavBarStyle: + base: Dict[str, str] = field( + default_factory=lambda: { + "top": "0", + "left": "0", + "width": "100%", + "spacing": "4", + "z_index": "2", + "justify": "end", + "align": "center", + "position": "sticky", + "padding": "0.75em 2em", + "background": rx.color("gray", 2), + "border_bottom": f"1px solid {rx.color('gray', 4)}", + } + ) + + +DashboardNavBarStyle: DashboardNavBarStyle = DashboardNavBarStyle() diff --git a/dashboard/dashboard/dashboardComponents/sideBar/__init__.py b/dashboard/dashboard/dashboardComponents/sideBar/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardComponents/sideBar/main.py b/dashboard/dashboard/dashboardComponents/sideBar/main.py new file mode 100644 index 00000000..79ca7ada --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/sideBar/main.py @@ -0,0 +1,57 @@ +from typing import Callable +from .style import DashboardSideBarStyle + +import reflex as rx + +menuItemMap = { + 0: {"icon": "home", "name": "Dashboard"}, + 1: {"icon": "users", "name": "Users"}, + 2: {"icon": "contact", "name": "User Profile"}, + 3: {"icon": "eye", "name": "Welcome"}, + 4: {"icon": "file", "name": "Analytics"}, +} + +reflexLogoPath = [ + "M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z", + "M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z", + "M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z", + "M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z", + "M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z", + "M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z", +] + + +sidebarMenuHeader: Callable[[], rx.hstack] = lambda: rx.hstack( + rx.el.svg( + *[ + rx.el.svg.path( + d=d, + fill=rx.color_mode_cond("#110F1F", "white"), + ) + for d in reflexLogoPath + ], + width="56", + height="12", + viewBox="0 0 56 12", + fill="none", + xmlns="http://www.w3.org/2000/svg", + ), + rx.badge(rx.text("Dashboard", size="1"), variant="surface", size="1"), + align="center", + width="100%", +) + +sidebarMenuItem: Callable[[str, str], rx.hstack] = lambda icon, name: rx.hstack( + rx.icon(tag=icon, size=14, color=rx.color("slate", 11)), + rx.text(name, weight="medium", size="2", color=rx.color("slate", 11)), + align="center", + justify="start", + height="28px", +) + +dashboardSidebar: Callable[[], rx.vstack] = lambda: rx.vstack( + sidebarMenuHeader(), + rx.divider(height="1em", opacity="0"), + *[sidebarMenuItem(item["icon"], item["name"]) for item in menuItemMap.values()], + **DashboardSideBarStyle.base, +) diff --git a/dashboard/dashboard/dashboardComponents/sideBar/style.py b/dashboard/dashboard/dashboardComponents/sideBar/style.py new file mode 100644 index 00000000..4b537d1c --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/sideBar/style.py @@ -0,0 +1,25 @@ +from dataclasses import dataclass, field +from typing import Dict + +import reflex as rx + + +@dataclass +class DashboardSideBarStyle: + base: Dict[str, str] = field( + default_factory=lambda: { + "top": "0", + "left": "0", + "width": "280px", + "max_width": "280px", + "height": "100vh", + "position": "sticky", + "padding": "1em 2em", + "background": rx.color("gray", 2), + "border_right": f"1px solid {rx.color('gray', 4)}", + "display": ["none" if i <= 3 else "flex" for i in range(6)], + } + ) + + +DashboardSideBarStyle: DashboardSideBarStyle = DashboardSideBarStyle() diff --git a/dashboard/dashboard/dashboardComponents/statBar/__init__.py b/dashboard/dashboard/dashboardComponents/statBar/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardComponents/statBar/main.py b/dashboard/dashboard/dashboardComponents/statBar/main.py new file mode 100644 index 00000000..47e3f7e8 --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/statBar/main.py @@ -0,0 +1,137 @@ +import reflex as rx +from typing import Callable, Dict, List + +from .style import DashboardStatBarStyle + +statbarDataSet = { + 0: { + "title": "USERS", + "current_month": "14,482", + "previous_month": "12,142", + "delta": "+3.76%", + "chart": [ + {"month": "January", "Users": 800}, + {"month": "February", "Users": 950}, + {"month": "March", "Users": 1200}, + {"month": "April", "Users": 1700}, + {"month": "May", "Users": 2200}, + {"month": "June", "Users": 2400}, + {"month": "July", "Users": 1500}, + {"month": "August", "Users": 1800}, + {"month": "September", "Users": 1100}, + {"month": "October", "Users": 1900}, + {"month": "November", "Users": 1700}, + {"month": "December", "Users": 2500}, + ], + }, + 1: { + "title": "AVG. CLICK RATE", + "current_month": "64,482", + "previous_month": "32,142", + "delta": "+52.76%", + "chart": [ + {"month": "January", "Clicks": 5000}, + {"month": "February", "Clicks": 7000}, + {"month": "March", "Clicks": 9000}, + {"month": "April", "Clicks": 4000}, + {"month": "May", "Clicks": 6000}, + {"month": "June", "Clicks": 15000}, + {"month": "July", "Clicks": 18000}, + {"month": "August", "Clicks": 9000}, + {"month": "September", "Clicks": 6500}, + {"month": "October", "Clicks": 3000}, + {"month": "November", "Clicks": 12000}, + {"month": "December", "Clicks": 22000}, + ], + }, + 2: { + "title": "SESSIONS", + "current_month": "19,336", + "previous_month": "21,642", + "delta": "-4.24%", + "chart": [ + {"month": "January", "Sessions": 300}, + {"month": "February", "Sessions": 600}, + {"month": "March", "Sessions": 1000}, + {"month": "April", "Sessions": 500}, + {"month": "May", "Sessions": 800}, + {"month": "June", "Sessions": 1500}, + {"month": "July", "Sessions": 2000}, + {"month": "August", "Sessions": 900}, + {"month": "September", "Sessions": 600}, + {"month": "October", "Sessions": 400}, + {"month": "November", "Sessions": 1800}, + {"month": "December", "Sessions": 500}, + ], + }, + 3: { + "title": "SESSION DURATION", + "current_month": "10m 23s", + "previous_month": "6m 15s", + "delta": "+57.60%", + "chart": [ + {"month": "January", "Avg. Duration": 10}, + {"month": "February", "Avg. Duration": 40}, + {"month": "March", "Avg. Duration": 100}, + {"month": "April", "Avg. Duration": 15}, + {"month": "May", "Avg. Duration": 50}, + {"month": "June", "Avg. Duration": 200}, + {"month": "July", "Avg. Duration": 300}, + {"month": "August", "Avg. Duration": 100}, + {"month": "September", "Avg. Duration": 75}, + {"month": "October", "Avg. Duration": 25}, + {"month": "November", "Avg. Duration": 200}, + {"month": "December", "Avg. Duration": 120}, + ], + }, +} + +statbarChart: Callable[[Dict[str, str]], rx.recharts.line_chart] = ( + lambda dataSet: rx.recharts.line_chart( + rx.recharts.line( + data_key=list(dataSet[0].keys())[1], type_="linear", dot=False + ), + data=dataSet, + height="100%", + ) +) + +statbarItemWrapper: Callable[[str, str, str, str, List[Dict[str, str]]], rx.hstack] = ( + lambda title, currentMonth, previousMonth, delta, data: rx.vstack( + rx.vstack( + rx.text(title, size="1", weight="bold", color=rx.color("slate", 11)), + rx.text(currentMonth, size="6", weight="bold", color=rx.color("slate", 12)), + spacing="2", + ), + rx.hstack( + rx.badge( + delta, + color_scheme="grass" if list(delta)[0] == "+" else "ruby", + size="1", + ), + rx.text( + f"from {previousMonth}", + size="1", + weight="bold", + color=rx.color("slate", 11), + ), + align="center", + ), + statbarChart(data), + **DashboardStatBarStyle.itemWrapper, + ) +) + +dashboardStatbar: Callable[[], rx.hstack()] = lambda: rx.hstack( + *[ + statbarItemWrapper( + item["title"], + item["current_month"], + item["previous_month"], + item["delta"], + item["chart"], + ) + for item in statbarDataSet.values() + ], + **DashboardStatBarStyle.base, +) diff --git a/dashboard/dashboard/dashboardComponents/statBar/style.py b/dashboard/dashboard/dashboardComponents/statBar/style.py new file mode 100644 index 00000000..a9a65cee --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/statBar/style.py @@ -0,0 +1,35 @@ +from dataclasses import dataclass, field +from typing import Dict + +import reflex as rx + + +@dataclass +class DashboardStatBarStyle: + base: Dict[str, str] = field( + default_factory=lambda: { + "width": "100%", + "display": "grid", + "grid_template_columns": [ + f"repeat({i}, minmax(0, 1fr))" for i in [1, 1, 2, 2, 4, 4] + ], + "padding": "1em", + "row_gap": "1em", + "column_gap": "1em", + } + ) + + itemWrapper: Dict[str, str] = field( + default_factory=lambda: { + "height": "180px", + "flex": "1 1 300px", + "border_radius": "8px", + "position": "relative", + "padding": "1em", + "background": rx.color("gray", 2), + "border": f"1px solid {rx.color('gray', 4)}", + } + ) + + +DashboardStatBarStyle: DashboardStatBarStyle = DashboardStatBarStyle() diff --git a/dashboard/dashboard/dashboardComponents/trafficBar/__init__.py b/dashboard/dashboard/dashboardComponents/trafficBar/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dashboard/dashboard/dashboardComponents/trafficBar/main.py b/dashboard/dashboard/dashboardComponents/trafficBar/main.py new file mode 100644 index 00000000..beebb4ce --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/trafficBar/main.py @@ -0,0 +1,87 @@ +import reflex as rx +from typing import Callable + +from .style import DashboardTrafficBarStyle + +trafficDataSet = { + 0: {"color": "blue", "title": "github.com", "qty": 132}, + 1: {"color": "purple", "title": " accounts.google.com ", "qty": 32}, + 2: {"color": "teal", "title": "discord.app", "qty": 54}, + 3: {"color": "orange", "title": "twitter.com", "qty": 23}, + 4: {"color": "gray", "title": "Others", "qty": 16}, +} + +trafficQuantitySum = sum(entry["qty"] for entry in trafficDataSet.values()) +trafficDataSet = { + key: {**entry, "percent": (entry["qty"] / trafficQuantitySum) * 100} + for key, entry in trafficDataSet.items() +} + + +trafficMenuTitle: Callable[[], rx.heading] = lambda: rx.heading( + "Referral Traffic", size="2", color=rx.color("slate", 12), weight="bold" +) + +trafficMenuQuantity: Callable[[], rx.hstack] = lambda: rx.hstack( + *[ + rx.box( + width=f"{entry['percent']}%", + height="10px", + bg=rx.color(entry["color"]), + border_radius=( + "10px 0 0 10px" + if idx == 0 + else "0 10px 10px 0" if idx == len(trafficDataSet) - 1 else "0" + ), + ) + for idx, entry in enumerate(trafficDataSet.values()) + ], + spacing="1", + align="center", + width="100%", +) + + +trafficMenuItem: Callable[[str, str, str], rx.hstack] = ( + lambda color, title, qty: rx.hstack( + rx.hstack( + rx.box( + width="8px", + height="8px", + border_radius="8px", + background=rx.color(color), + ), + rx.text(title, size="1", weight="bold", color=rx.color("slate", 12)), + align="center", + spacing="2", + ), + rx.text(f"{qty}k", size="1", weight="bold", color=rx.color("slate", 11)), + align="center", + justify="between", + width="100%", + ) +) + + +dashboardTrafficbar: Callable[[], rx.vstack] = lambda: rx.vstack( + rx.hstack( + trafficMenuTitle(), + rx.icon(tag="download", size=14), + width="100%", + align="center", + justify="between", + ), + rx.divider(height="0.5em", opacity="0"), + trafficMenuQuantity(), + rx.divider(height="0.5em", opacity="0"), + rx.vstack( + *[ + trafficMenuItem(item["color"], item["title"], item["qty"]) + for item in trafficDataSet.values() + ], + width="100%", + ), + rx.spacer(), + rx.button("See More Analytics", width="100%", variant="surface", cursor="pointer"), + **DashboardTrafficBarStyle.base, +) diff --git a/dashboard/dashboard/dashboardComponents/trafficBar/style.py b/dashboard/dashboard/dashboardComponents/trafficBar/style.py new file mode 100644 index 00000000..c2f6fd56 --- /dev/null +++ b/dashboard/dashboard/dashboardComponents/trafficBar/style.py @@ -0,0 +1,21 @@ +from dataclasses import dataclass, field +from typing import Dict + +import reflex as rx + + +@dataclass +class DashboardTrafficBarStyle: + base: Dict[str, str] = field( + default_factory=lambda: { + "height": "100%", + "padding": "1em", + "grid_column": ["span 1" if i >= 4 else "span 3" for i in range(6)], + "border_radius": "8px", + "background": rx.color("gray", 2), + "border": f"1px solid {rx.color('gray', 4)}", + }, + ) + + +DashboardTrafficBarStyle: DashboardTrafficBarStyle = DashboardTrafficBarStyle() diff --git a/dashboard/requirements.txt b/dashboard/requirements.txt new file mode 100644 index 00000000..1de84caa --- /dev/null +++ b/dashboard/requirements.txt @@ -0,0 +1 @@ +reflex>=0.6.5 diff --git a/dashboard/rxconfig.py b/dashboard/rxconfig.py new file mode 100644 index 00000000..03b0e9b5 --- /dev/null +++ b/dashboard/rxconfig.py @@ -0,0 +1,3 @@ +import reflex as rx + +config = rx.Config(app_name="dashboard")