diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a8f37b8f..1956c590 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,6 +20,8 @@ "@mui/styled-engine-sc": "^6.4.6", "@mui/x-charts": "^7.27.1", "@mui/x-charts-pro": "^7.27.1", + "@mui/x-data-grid": "^7.28.2", + "@mui/x-date-pickers": "^7.28.2", "d3": "^7.9.0", "leaflet": "^1.9.4", "lucide-react": "^0.485.0", @@ -1775,6 +1777,110 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@mui/x-data-grid": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.28.2.tgz", + "integrity": "sha512-ac/CPZ/zOeHjCvv3LwTUSFy+dofELP/Cl2nXLYWrqVHUgFAkkWxhk85kiI/N1G+Rf4WiKw+oJfeyGe8ZSeY4tg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta", + "@mui/x-internals": "7.28.0", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.28.2.tgz", + "integrity": "sha512-py8u0iOShM8m/Ocs/giS8MwTNFn+iRFG6m5L6YJ/JmKzNfQfkVJOjpbdSIWxdCJ8plJn95oar1nKUhDdwn88HA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta", + "@mui/x-internals": "7.28.0", + "@types/react-transition-group": "^4.4.11", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0 || ^7.0.0-beta", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2 || ^3.0.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, "node_modules/@mui/x-internals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.28.0.tgz", @@ -4877,6 +4983,12 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "license": "MIT" }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -5257,6 +5369,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index df91891e..48ec181c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,8 @@ "@mui/styled-engine-sc": "^6.4.6", "@mui/x-charts": "^7.27.1", "@mui/x-charts-pro": "^7.27.1", + "@mui/x-data-grid": "^7.28.2", + "@mui/x-date-pickers": "^7.28.2", "d3": "^7.9.0", "leaflet": "^1.9.4", "lucide-react": "^0.485.0", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index ef1a6a29..2c6e512d 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,44 +1,26 @@ -import React, { useState } from 'react'; -import Navbar from './components/Navbar'; -import Dashboard from './components/Dashboard'; -import CheckLog from './components/CheckLog'; -import ChartLine from './components/ChartLine'; -import LogOut from './components/LogOut'; -import Collab from './components/Collab'; -import './App.css'; - -function App() { - const [activeTab, setActiveTab] = useState('dashboard'); - - const renderContent = () => { - switch (activeTab) { - case 'dashboard': - return ; - case 'checkLog': - return ; - case 'chartLine': - return ; - case 'logOut': - return ; - default: - return ; - } - }; +import * as React from "react"; +import CssBaseline from "@mui/material/CssBaseline"; +import Divider from "@mui/material/Divider"; +import AppTheme from "./theme/AppTheme"; +import AppAppBar from "./components/AppAppBar"; +import Hero from "./components/Hero"; +import Features from "./components/Features"; +import FAQ from "./components/FAQ"; +import Footer from "./components/Footer"; +export default function App(props) { return ( -
-
- -
-
- {renderContent()} + + + + +
+ + + + +
- -
- -
-
+ ); } - -export default App; diff --git a/frontend/src/assets/assets.js b/frontend/src/assets/assets.js deleted file mode 100644 index 61d54887..00000000 --- a/frontend/src/assets/assets.js +++ /dev/null @@ -1,13 +0,0 @@ -import user1 from './user1.png' -import user2 from './user2.png' -import user3 from './user3.png' -import user4 from './user4.png' -import user5 from './user5.png' - -export const assets = { - user1, - user2, - user3, - user4, - user5 -} \ No newline at end of file diff --git a/frontend/src/assets/dashboard.png b/frontend/src/assets/dashboard.png new file mode 100644 index 00000000..89aef989 Binary files /dev/null and b/frontend/src/assets/dashboard.png differ diff --git a/frontend/src/assets/user1.png b/frontend/src/assets/user1.png deleted file mode 100644 index 18cc06f4..00000000 Binary files a/frontend/src/assets/user1.png and /dev/null differ diff --git a/frontend/src/assets/user2.png b/frontend/src/assets/user2.png deleted file mode 100644 index c3bd39e7..00000000 Binary files a/frontend/src/assets/user2.png and /dev/null differ diff --git a/frontend/src/assets/user3.png b/frontend/src/assets/user3.png deleted file mode 100644 index 696e5730..00000000 Binary files a/frontend/src/assets/user3.png and /dev/null differ diff --git a/frontend/src/assets/user4.png b/frontend/src/assets/user4.png deleted file mode 100644 index 7e2afc01..00000000 Binary files a/frontend/src/assets/user4.png and /dev/null differ diff --git a/frontend/src/assets/user5.png b/frontend/src/assets/user5.png deleted file mode 100644 index cfba57ba..00000000 Binary files a/frontend/src/assets/user5.png and /dev/null differ diff --git a/frontend/src/components/AppAppBar.jsx b/frontend/src/components/AppAppBar.jsx new file mode 100644 index 00000000..f990a128 --- /dev/null +++ b/frontend/src/components/AppAppBar.jsx @@ -0,0 +1,142 @@ +import * as React from "react"; +import { styled, alpha } from "@mui/material/styles"; +import Box from "@mui/material/Box"; +import AppBar from "@mui/material/AppBar"; +import Toolbar from "@mui/material/Toolbar"; +import Button from "@mui/material/Button"; +import IconButton from "@mui/material/IconButton"; +import Container from "@mui/material/Container"; +import Divider from "@mui/material/Divider"; +import MenuItem from "@mui/material/MenuItem"; +import Drawer from "@mui/material/Drawer"; +import MenuIcon from "@mui/icons-material/Menu"; +import CloseRoundedIcon from "@mui/icons-material/CloseRounded"; +import ColorModeIconDropdown from "../theme/ColorModeIconDropdown"; +import Sitemark from "./SitemarkIcon"; + +const StyledToolbar = styled(Toolbar)(({ theme }) => ({ + display: "flex", + alignItems: "center", + justifyContent: "space-between", + flexShrink: 0, + borderRadius: `calc(${theme.shape.borderRadius}px + 8px)`, + backdropFilter: "blur(24px)", + border: "1px solid", + borderColor: (theme.vars || theme).palette.divider, + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.background.defaultChannel} / 0.4)` + : alpha(theme.palette.background.default, 0.4), + boxShadow: (theme.vars || theme).shadows[1], + padding: "8px 12px", +})); + +export default function AppAppBar() { + const [open, setOpen] = React.useState(false); + + const toggleDrawer = (newOpen) => () => { + setOpen(newOpen); + }; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Features + FAQ + Watch Demo + + + + + + + + + + + + + + ); +} diff --git a/frontend/src/components/ChartLine.jsx b/frontend/src/components/ChartLine.jsx deleted file mode 100644 index d31e8ebf..00000000 --- a/frontend/src/components/ChartLine.jsx +++ /dev/null @@ -1,217 +0,0 @@ -import React, { useState } from 'react' -import { PieChart } from '@mui/x-charts/PieChart' - -const pieChartsData = { - userLogon: [ - { id: 0, label: 'Administrator', value: 30 }, - { id: 1, label: 'Orion11s', value: 25 }, - { id: 2, label: 'System', value: 20 }, - { id: 3, label: 'Local Users', value: 5 }, - { id: 4, label: 'Guest', value: 10 }, - { id: 5, label: 'Developer', value: 15 }, - { id: 6, label: 'Service Account', value: 8 }, - { id: 7, label: 'Others', value: 20 }, - ], - logonFailures: [ - { id: 0, label: 'Password Error', value: 40 }, - { id: 1, label: 'Account Locked', value: 30 }, - { id: 2, label: 'Unknown User', value: 30 }, - { id: 3, label: 'MFA Failure', value: 25 }, - { id: 4, label: 'Account Disabled', value: 15 }, - ], - issueType: [ - { id: 0, label: 'Hardware', value: 25 }, - { id: 1, label: 'Software', value: 50 }, - { id: 2, label: 'Hybrid', value: 25 }, - { id: 3, label: 'Other', value: 10 }, - ], - fixationStatus: [ - { id: 0, label: 'Fixed', value: 60 }, - { id: 1, label: 'Not Fixed', value: 40 }, - { id: 2, label: 'In Progress', value: 20 }, - ], - errorSeverity: [ - { id: 0, label: 'Critical', value: 35 }, - { id: 1, label: 'Major', value: 40 }, - { id: 2, label: 'Minor', value: 25 }, - ], - errorCategory: [ - { id: 0, label: 'Syntax Error', value: 30 }, - { id: 1, label: 'Runtime Error', value: 40 }, - { id: 2, label: 'Database Error', value: 20 }, - { id: 3, label: 'Network Error', value: 10 }, - { id: 4, label: 'Other', value: 15 }, - ], -} - -function LogonFailuresPieChart({ title, data }) { - const [selected, setSelected] = useState(null) - - const handleClick = (event, params) => { - setSelected(params.label) - } - - return ( -
- -

{title}

- {selected &&

Selected: {selected}

} -
- ); -} - - -function IssueTypePieChart({ title, data }) { - const [selected, setSelected] = useState(null) - - const handleClick = (event, params) => { - setSelected(params.label) - } - - return ( -
- -

{title}

- {selected &&

Selected: {selected}

} -
- ); -} - -function FixationStatusPieChart({ title, data }) { - const [selected, setSelected] = useState(null) - - const handleClick = (event, params) => { - setSelected(params.label) - } - - return ( -
- -

{title}

- {selected &&

Selected: {selected}

} -
- ); -} - -function ErrorCategoryPieChart({ title, data }) { - const [selected, setSelected] = useState(null) - - const handleClick = (event, params) => { - setSelected(params.label) - } - - return ( -
- -

{title}

- {selected &&

Selected: {selected}

} -
- ); -} - - -const ChartLine = () => { - return ( -
-

Chart Line

-
-
-
- -
-
-
- -
-
-
- -
-
- -
-
- ) -} - -export default ChartLine \ No newline at end of file diff --git a/frontend/src/components/ChatBox.jsx b/frontend/src/components/ChatBox.jsx deleted file mode 100644 index e14e8b1f..00000000 --- a/frontend/src/components/ChatBox.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, { useState } from 'react' - -function ChatBox() { - const [input, setInput] = useState('') - - const handleChat = () => { - // Here you can pass the input or log details to your chat interface. - // For now, we simulate opening a new tab with a chat window. - window.open('/chat', '_blank') - } - - return ( -
- setInput(e.target.value)} - className="border border-r-0 border-gray-400 px-2 py-1 flex-1" - /> - -
- ) -} - -export default ChatBox diff --git a/frontend/src/components/CheckLog.jsx b/frontend/src/components/CheckLog.jsx deleted file mode 100644 index 21f44d29..00000000 --- a/frontend/src/components/CheckLog.jsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useState, useEffect } from 'react' -import sampleHistory from '../data/sampleLogs.json' -import LogItem from './LogItem' - -function CheckLog() { - const [logs, setLogs] = useState([]) - const [sortOrder, setSortOrder] = useState("desc") - const [searchQuery, setSearchQuery] = useState("") - const [visibleCount, setVisibleCount] = useState(8) - - useEffect(() => { - // Load sample logs on mount. - setLogs(sampleHistory) - }, []) - - // Toggle sort order between ascending and descending. - const handleSort = () => { - setSortOrder((prev) => (prev === "desc" ? "asc" : "desc")) - } - - // Filter logs by checking if any relevant fields contain the search query. - const filteredLogs = logs.filter((log) => { - const combined = `${log.date} ${log.logMessage} ${log.eventIDs.join(" ")} ${log.issueType} ${log.fixableType} ${log.userType} ${log.riskPriority}`.toLowerCase() - return combined.includes(searchQuery.toLowerCase()) - }) - - // Sort logs based on the date field. - const sortedLogs = [...filteredLogs].sort((a, b) => { - const dateA = new Date(a.date) - const dateB = new Date(b.date) - return sortOrder === "asc" ? dateA - dateB : dateB - dateA - }) - - // Slice the sorted logs based on the visible count. - const visibleLogs = sortedLogs.slice(0, visibleCount) - - // Load more logs by increasing visible count. - const loadMore = () => { - setVisibleCount((prev) => prev + 5) - } - - return ( -
-

Check Previous Logs

-
- - setSearchQuery(e.target.value)} - /> -
- -
- {visibleLogs.map((log, idx) => ( - - ))} -
- {visibleCount < sortedLogs.length && ( -
- -
- )} -
- ) -} - -export default CheckLog diff --git a/frontend/src/components/Collab.jsx b/frontend/src/components/Collab.jsx deleted file mode 100644 index 63fa4dd9..00000000 --- a/frontend/src/components/Collab.jsx +++ /dev/null @@ -1,106 +0,0 @@ -import React, { useState } from 'react'; -import { FaRegEnvelope } from 'react-icons/fa'; -import { assets } from '../assets/assets'; - -const usersData = [ - { - id: 1, - name: "Alice Johnson", - bio: "Frontend Developer.", - profilePic: assets.user2, - active: true, - lastSeen: "Active now" - }, - { - id: 2, - name: "Bob Smith", - bio: "Backend Developer.", - profilePic: assets.user5, - active: false, - lastSeen: "1 hour ago" - }, - { - id: 3, - name: "Carol White", - bio: "Fullstack Engineer.", - profilePic: assets.user4, - active: true, - lastSeen: "Active now" - }, - { - id: 4, - name: "David Green", - bio: "Data Scientist.", - profilePic: assets.user1, - active: false, - lastSeen: "30 min ago" - }, - { - id: 5, - name: "Eve Black", - bio: "DevOps Engineer.", - profilePic: assets.user3, - active: true, - lastSeen: "Active now" - }, -]; - -const Collaboration = () => { - const [showActive, setShowActive] = useState(true); - - // Filter users based on the toggle state - const filteredUsers = usersData.filter(user => user.active === showActive); - - return ( -
- {/* Top toggle switch for Active/Inactive users */} -
-

Collaboration

-
- - {showActive ? 'Active Users' : 'Inactive Users'} - - -
-
- - {/* User cards */} -
- {filteredUsers.map(user => ( -
- {user.name} -
-

{user.name}

-

{user.bio}

-

- {user.active ? 'Active' : `Last seen: ${user.lastSeen}`} -

-
-
- -
- -
- ))} -
-
- ); -}; - -export default Collaboration; diff --git a/frontend/src/components/DailyLogsBarGraph.jsx b/frontend/src/components/DailyLogsBarGraph.jsx deleted file mode 100644 index 5582db59..00000000 --- a/frontend/src/components/DailyLogsBarGraph.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react' -import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'; - -const data = [ - { date: '2023-02-01', logs: 12 }, - { date: '2023-02-02', logs: 18 }, - { date: '2023-02-03', logs: 9 }, - { date: '2023-02-04', logs: 15 }, - { date: '2023-02-05', logs: 20 }, - { date: '2023-02-06', logs: 14 }, - { date: '2023-02-07', logs: 22 }, - { date: '2023-02-08', logs: 17 }, - { date: '2023-02-09', logs: 11 }, - { date: '2023-02-10', logs: 19 }, - { date: '2023-02-11', logs: 16 }, - { date: '2023-02-12', logs: 23 }, - { date: '2023-02-13', logs: 13 }, - { date: '2023-02-14', logs: 20 }, - { date: '2023-02-15', logs: 15 } -] - -// Function to convert date string to day of the week -const getDayName = (dateString) => { - const date = new Date(dateString); - const options = { weekday: 'short' }; - return date.toLocaleDateString('en-US', options); -}; - -// Map data to include day names -const formattedData = data.map(item => ({ - ...item, - day: getDayName(item.date), -})); - -// Custom tooltip component for the area chart -const CustomTooltip = ({ active, payload }) => { - if (active && payload && payload.length) { - return ( -
-

{`Logs: ${payload[0].value}`}

-
- ); - } - return null; -}; - -function DailyLogsAreaChart() { - return ( -
-

Overview

- - - - } /> - - - -
- ); -} - -export default DailyLogsAreaChart; \ No newline at end of file diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx deleted file mode 100644 index b1101ce4..00000000 --- a/frontend/src/components/Dashboard.jsx +++ /dev/null @@ -1,276 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react' -import DailyLogsBarGraph from './DailyLogsBarGraph' -import { PieChart } from '@mui/x-charts/PieChart' -import { MapContainer, TileLayer, CircleMarker, Tooltip } from 'react-leaflet'; -import 'leaflet/dist/leaflet.css'; -import * as d3 from 'd3'; - -const pieChartsData = { - userLogon: [ - { id: 0, label: 'Administrator', value: 30 }, - { id: 1, label: 'Orion11s', value: 25 }, - { id: 2, label: 'System', value: 20 }, - { id: 3, label: 'Local Users', value: 5 }, - { id: 4, label: 'Guest', value: 10 }, - { id: 5, label: 'Developer', value: 15 }, - { id: 6, label: 'Service Account', value: 8 }, - { id: 7, label: 'Others', value: 20 }, - ], - logonFailures: [ - { id: 0, label: 'Password Error', value: 40 }, - { id: 1, label: 'Account Locked', value: 30 }, - { id: 2, label: 'Unknown User', value: 30 }, - { id: 3, label: 'MFA Failure', value: 25 }, - { id: 4, label: 'Account Disabled', value: 15 }, - ], - issueType: [ - { id: 0, label: 'Hardware', value: 25 }, - { id: 1, label: 'Software', value: 50 }, - { id: 2, label: 'Hybrid', value: 25 }, - { id: 3, label: 'Other', value: 10 }, - ], - fixationStatus: [ - { id: 0, label: 'Fixed', value: 60 }, - { id: 1, label: 'Not Fixed', value: 40 }, - { id: 2, label: 'In Progress', value: 20 }, - ], - errorSeverity: [ - { id: 0, label: 'Critical', value: 35 }, - { id: 1, label: 'Major', value: 40 }, - { id: 2, label: 'Minor', value: 25 }, - ], - errorCategory: [ - { id: 0, label: 'Syntax Error', value: 30 }, - { id: 1, label: 'Runtime Error', value: 40 }, - { id: 2, label: 'Database Error', value: 20 }, - { id: 3, label: 'Network Error', value: 10 }, - { id: 4, label: 'Other', value: 15 }, - ], -} - -const heatmapData = [ - { x: 'Mon', y: '00:00', value: 5 }, - { x: 'Mon', y: '01:00', value: 8 }, - { x: 'Mon', y: '02:00', value: 6 }, - { x: 'Tue', y: '00:00', value: 7 }, - { x: 'Tue', y: '01:00', value: 3 }, - { x: 'Tue', y: '02:00', value: 4 }, - { x: 'Wed', y: '00:00', value: 9 }, - { x: 'Wed', y: '01:00', value: 2 }, - { x: 'Wed', y: '02:00', value: 5 }, - { x: 'Thu', y: '00:00', value: 6 }, - { x: 'Thu', y: '01:00', value: 7 }, - { x: 'Thu', y: '02:00', value: 8 }, - { x: 'Fri', y: '00:00', value: 4 }, - { x: 'Fri', y: '01:00', value: 5 }, - { x: 'Fri', y: '02:00', value: 6 }, - { x: 'Sat', y: '00:00', value: 3 }, - { x: 'Sat', y: '01:00', value: 4 }, - { x: 'Sat', y: '02:00', value: 2 }, - { x: 'Sun', y: '00:00', value: 8 }, - { x: 'Sun', y: '01:00', value: 7 }, - { x: 'Sun', y: '02:00', value: 9 }, -]; - -const userLocations = [ - { lat: 37.7749, lng: -122.4194, count: 150 }, // San Francisco - { lat: 34.0522, lng: -118.2437, count: 200 }, // Los Angeles - { lat: 40.7128, lng: -74.0060, count: 250 }, // New York - { lat: 51.5074, lng: -0.1278, count: 180 }, // London - { lat: 48.8566, lng: 2.3522, count: 220 }, // Paris - { lat: 35.6895, lng: 139.6917, count: 300 }, // Tokyo - { lat: -33.8688, lng: 151.2093, count: 170 }, // Sydney - { lat: 55.7558, lng: 37.6176, count: 190 }, // Moscow - { lat: 39.9042, lng: 116.4074, count: 280 }, // Beijing - { lat: -23.5505, lng: -46.6333, count: 160 }, // São Paulo - { lat: 19.0760, lng: 72.8777, count: 210 }, // Mumbai - { lat: 52.5200, lng: 13.4050, count: 200 }, // Berlin -]; - - -function LogUserPie({ title, data }) { - const [selected, setSelected] = useState(null) - - const handleClick = (event, params) => { - setSelected(params.label) - } - - return ( -
-

{title}

- - {selected &&

Selected: {selected}

} -
- ) -} - - -function GeographicalUserDistributionMap() { - return ( - - - {userLocations.map((location, index) => ( - - {`Users: ${location.count}`} - - ))} - - ); -} - - -function HeatMapChart({ data }) { - const svgRef = useRef(); - - useEffect(() => { - const margin = { top: 0, right: 0, bottom: 30, left: 40 }; - const width = 450 - margin.left - margin.right; - const height = 250 - margin.top - margin.bottom; - - // Clear previous SVG content if any - d3.select(svgRef.current).selectAll('*').remove(); - - const svg = d3.select(svgRef.current) - .attr('width', width + margin.left + margin.right) - .attr('height', height + margin.top + margin.bottom) - .append('g') - .attr('transform', `translate(${margin.left},${margin.top})`); - - const xLabels = Array.from(new Set(data.map(d => d.x))); - const yLabels = Array.from(new Set(data.map(d => d.y))); - - const xScale = d3.scaleBand() - .domain(xLabels) - .range([0, width]) - .padding(0.05); - - const yScale = d3.scaleBand() - .domain(yLabels) - .range([height, 0]) - .padding(0.05); - - svg.append('g') - .attr('transform', `translate(0, ${height})`) - .call(d3.axisBottom(xScale)); - - svg.append('g') - .call(d3.axisLeft(yScale)); - - const colorScale = d3.scaleSequential(d3.interpolatePurples) - .domain([0, d3.max(data, d => d.value)]); - - // Create a tooltip div that is hidden by default - const tooltip = d3.select('body').append('div') - .style('position', 'absolute') - .style('background-color', 'white') - .style('border', 'solid') - .style('border-width', '1px') - .style('border-radius', '5px') - .style('padding', '10px') - .style('opacity', 0); - - // Functions to handle mouse events - const handleMouseOver = function (event, d) { - tooltip.style('opacity', 1); - d3.select(this).style('stroke', 'black').style('opacity', 1); - }; - - const handleMouseMove = function (event, d) { - tooltip - .html(`No. of Users: ${d.value}`) - .style('left', (event.pageX + 10) + 'px') - .style('top', (event.pageY - 28) + 'px') - .style('font-size', '12px'); - }; - - const handleMouseLeave = function (event, d) { - tooltip.style('opacity', 0); - d3.select(this).style('stroke', 'none').style('opacity', 0.8); - }; - - svg.selectAll() - .data(data, d => `${d.x}:${d.y}`) - .enter() - .append('rect') - .attr('x', d => xScale(d.x)) - .attr('y', d => yScale(d.y)) - .attr('width', xScale.bandwidth()) - .attr('height', yScale.bandwidth()) - .style('fill', d => colorScale(d.value)) - .style('stroke-width', 0) - .style('stroke', 'none') - .style('opacity', 0.8) - .style('cursor', 'pointer') - .on('mouseover', handleMouseOver) - .on('mousemove', handleMouseMove) - .on('mouseleave', handleMouseLeave); - - // Cleanup function to remove the tooltip when the component unmounts - return () => { - tooltip.remove(); - }; - }, [data]); - - return ; -} - - -function Dashboard() { - return ( -
-
Primary
-

Dashboard

- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
- -
- ) -} - -export default Dashboard diff --git a/frontend/src/components/FAQ.jsx b/frontend/src/components/FAQ.jsx new file mode 100644 index 00000000..fab2bcbd --- /dev/null +++ b/frontend/src/components/FAQ.jsx @@ -0,0 +1,152 @@ +import * as React from "react"; +import Accordion from "@mui/material/Accordion"; +import AccordionDetails from "@mui/material/AccordionDetails"; +import AccordionSummary from "@mui/material/AccordionSummary"; +import Box from "@mui/material/Box"; +import Container from "@mui/material/Container"; +import Link from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; + +export default function FAQ() { + const [expanded, setExpanded] = React.useState([]); + + const handleChange = (panel) => (event, isExpanded) => { + setExpanded( + isExpanded + ? [...expanded, panel] + : expanded.filter((item) => item !== panel) + ); + }; + + return ( + + + ❓ Frequently Asked Questions (FAQ) + + + + } + aria-controls="panel1d-content" + id="panel1d-header" + > + + What is this log analyzer, and how does it work? + + + + + Our log analyzer helps you + detect errors, troubleshoot issues, and gain insights from + system logs. Simply upload your logs, and our AI-powered engine + will analyze them, highlighting potential problems and suggesting + fixes. + + + + + } + aria-controls="panel2d-content" + id="panel2d-header" + > + + What types of logs does it support? + + + + + We support system logs, server logs, application logs, network + logs, and more, including formats like CSV and log files from + Linux, Windows, and cloud platforms. + + + + + } + aria-controls="panel3d-content" + id="panel3d-header" + > + + Do I need technical expertise to use it? + + + + + Not at all! Our tool is designed for both developers and + non-technical users, providing an easy-to-use dashboard with + simple explanations and actionable insights. + + + + + } + aria-controls="panel4d-content" + id="panel4d-header" + > + + How do I get started? + + + + + Click "Get Started", sign up, and upload your first log to begin + analyzing errors and improving your system performance. It's that + simple! + + + + + + ); +} diff --git a/frontend/src/components/Features.jsx b/frontend/src/components/Features.jsx new file mode 100644 index 00000000..fd287a21 --- /dev/null +++ b/frontend/src/components/Features.jsx @@ -0,0 +1,264 @@ +import * as React from "react"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Card from "@mui/material/Card"; +import MuiChip from "@mui/material/Chip"; +import Container from "@mui/material/Container"; +import Typography from "@mui/material/Typography"; +import { styled } from "@mui/material/styles"; + +import DevicesRoundedIcon from "@mui/icons-material/DevicesRounded"; +import EdgesensorHighRoundedIcon from "@mui/icons-material/EdgesensorHighRounded"; +import ViewQuiltRoundedIcon from "@mui/icons-material/ViewQuiltRounded"; + +const items = [ + { + icon: , + title: "AI-Powered Insights", + description: + "Automatically analyzes logs and CSV files, providing instant error highlights and suggested solutions.", + imageLight: `url("${"https://mui.com"}/static/images/templates/templates-images/dash-light.png")`, + imageDark: `url("${"https://mui.com"}/static/images/templates/templates-images/dash-dark.png")`, + }, + { + icon: , + title: "Cross-Platform Compatibility", + description: + "Supports log files from any OS, ensuring seamless analysis across Windows, macOS, Linux, and more.", + imageLight: `url("${"https://mui.com"}/static/images/templates/templates-images/mobile-light.png")`, + imageDark: `url("${"https://mui.com"}/static/images/templates/templates-images/mobile-dark.png")`, + }, + { + icon: , + title: "Interactive Issue Resolution", + description: + "Chat with AI for detailed explanations and step-by-step guidance on fixing detected issues.", + imageLight: `url("${"https://mui.com"}/static/images/templates/templates-images/devices-light.png")`, + imageDark: `url("${"https://mui.com"}/static/images/templates/templates-images/devices-dark.png")`, + }, +]; + +const Chip = styled(MuiChip)(({ theme }) => ({ + variants: [ + { + props: ({ selected }) => selected, + style: { + background: + "linear-gradient(to bottom right, hsl(210, 98%, 48%), hsl(210, 98%, 35%))", + color: "hsl(0, 0%, 100%)", + borderColor: (theme.vars || theme).palette.primary.light, + "& .MuiChip-label": { + color: "hsl(0, 0%, 100%)", + }, + ...theme.applyStyles("dark", { + borderColor: (theme.vars || theme).palette.primary.dark, + }), + }, + }, + ], +})); + +export function MobileLayout({ + selectedItemIndex, + handleItemClick, + selectedFeature, +}) { + if (!items[selectedItemIndex]) { + return null; + } + + return ( + + + {items.map(({ title }, index) => ( + handleItemClick(index)} + selected={selectedItemIndex === index} + /> + ))} + + + ({ + mb: 2, + backgroundSize: "cover", + backgroundPosition: "center", + minHeight: 280, + backgroundImage: "var(--items-imageLight)", + ...theme.applyStyles("dark", { + backgroundImage: "var(--items-imageDark)", + }), + })} + style={ + items[selectedItemIndex] + ? { + "--items-imageLight": items[selectedItemIndex].imageLight, + "--items-imageDark": items[selectedItemIndex].imageDark, + } + : {} + } + /> + + + {selectedFeature.title} + + + {selectedFeature.description} + + + + + ); +} + +export default function Features() { + const [selectedItemIndex, setSelectedItemIndex] = React.useState(0); + + const handleItemClick = (index) => { + setSelectedItemIndex(index); + }; + + const selectedFeature = items[selectedItemIndex]; + + return ( + + + + Log Analyzer features + + + Discover the powerful features of our log analyzer, designed to help + you diagnose system issues faster and more efficiently. Our intuitive + platform combines AI capabilities with user-friendly interfaces to + transform complex log data into actionable insights. + + + +
+ + {items.map(({ icon, title, description }, index) => ( + handleItemClick(index)} + sx={[ + (theme) => ({ + p: 2, + height: "100%", + width: "100%", + "&:hover": { + backgroundColor: (theme.vars || theme).palette.action + .hover, + }, + }), + selectedItemIndex === index && { + backgroundColor: "action.selected", + }, + ]} + > + + {icon} + + {title} + {description} + + + ))} + + +
+ + + ({ + m: "auto", + width: 420, + height: 500, + backgroundSize: "contain", + backgroundImage: "var(--items-imageLight)", + ...theme.applyStyles("dark", { + backgroundImage: "var(--items-imageDark)", + }), + })} + style={ + items[selectedItemIndex] + ? { + "--items-imageLight": items[selectedItemIndex].imageLight, + "--items-imageDark": items[selectedItemIndex].imageDark, + } + : {} + } + /> + + +
+
+ ); +} diff --git a/frontend/src/components/Footer.jsx b/frontend/src/components/Footer.jsx new file mode 100644 index 00000000..cd0ac55e --- /dev/null +++ b/frontend/src/components/Footer.jsx @@ -0,0 +1,179 @@ +import * as React from "react"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Container from "@mui/material/Container"; +import IconButton from "@mui/material/IconButton"; +import Link from "@mui/material/Link"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import GitHubIcon from "@mui/icons-material/GitHub"; +import LinkedInIcon from "@mui/icons-material/LinkedIn"; +import TwitterIcon from "@mui/icons-material/X"; +import SitemarkIcon from "./SitemarkIcon"; + +function Copyright() { + return ( + + {"Copyright © "} + + Log Analyzer + +   + {new Date().getFullYear()} + + ); +} + +export default function Footer() { + return ( + + + + + + + Get started with our log analyzer today!{" "} + + + and fix the issues with your system. + + + + + + + Product + + + Features + + + FAQs + + + About us + + + + + Legal + + + Terms + + + Privacy + + + Contact + + + + +
+ + Privacy Policy + + +  •  + + + Terms of Service + + +
+ + + + + + + + + + + +
+
+ ); +} diff --git a/frontend/src/components/Hero.jsx b/frontend/src/components/Hero.jsx new file mode 100644 index 00000000..fc008790 --- /dev/null +++ b/frontend/src/components/Hero.jsx @@ -0,0 +1,149 @@ +import * as React from "react"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Container from "@mui/material/Container"; +import Link from "@mui/material/Link"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import { styled } from "@mui/material/styles"; +import dashboardImage from "../assets/dashboard.png"; + +const StyledBox = styled("div")(({ theme }) => ({ + alignSelf: "center", + width: "100%", + height: 400, + marginTop: theme.spacing(8), + borderRadius: (theme.vars || theme).shape.borderRadius, + outline: "6px solid", + outlineColor: "hsla(220, 25%, 80%, 0.2)", + border: "1px solid", + borderColor: (theme.vars || theme).palette.grey[200], + boxShadow: "0 0 12px 8px hsla(220, 25%, 80%, 0.2)", + backgroundImage: `url(${dashboardImage})`, + backgroundSize: "cover", + [theme.breakpoints.up("sm")]: { + marginTop: theme.spacing(10), + height: 700, + }, + ...theme.applyStyles("dark", { + boxShadow: "0 0 24px 12px hsla(210, 100%, 25%, 0.2)", + backgroundImage: `url(${dashboardImage})`, + outlineColor: "hsla(220, 20%, 42%, 0.1)", + borderColor: (theme.vars || theme).palette.grey[700], + }), +})); + +export default function Hero() { + return ( + ({ + width: "100%", + backgroundRepeat: "no-repeat", + display: "flex", + justifyContent: "center", + backgroundImage: + "radial-gradient(ellipse 80% 50% at 50% -20%, hsl(210, 100%, 90%), transparent)", + ...theme.applyStyles("dark", { + backgroundImage: + "radial-gradient(ellipse 80% 50% at 50% -20%, hsl(210, 100%, 16%), transparent)", + }), + })} + > + + + + Analyze & Fix Logs, + ({ + fontSize: "inherit", + color: "primary.main", + ...theme.applyStyles("dark", { + color: "primary.light", + }), + })} + > + Instantly! + + + + Fix errors fast with AI-powered log analysis. Get real-time insights + and instant issue detection—no more endless log sifting! 🚀 + + + + + + + + By clicking "Get Started" you agree to our  + + Terms & Conditions + + . + + + + + + ); +} diff --git a/frontend/src/components/LogItem.jsx b/frontend/src/components/LogItem.jsx deleted file mode 100644 index 9b6a5156..00000000 --- a/frontend/src/components/LogItem.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useState } from 'react' -import ChatBox from './ChatBox' - -function LogItem({ log }) { - const [expanded, setExpanded] = useState(false) - - // Return Tailwind color classes based on risk priority. - const getRiskColor = (risk) => { - switch (risk.toLowerCase()) { - case "high": - return "text-red-500" - case "medium": - return "text-yellow-500" - case "low": - return "text-green-500" - default: - return "" - } - } - - return ( -
-
-
-
{log.date}
-
{log.time}
-
{log.userType}
-
- {log.riskPriority} -
-
- -
- {expanded && ( -
-
- {log.logMessage} -
-
-

Event IDs:

{log.eventIDs.join(', ')} -
-
-

Type of Issue:

{log.issueType} -
-
-

Fixable Type:

{log.fixableType} -
-
- -
-
- )} -
- ) -} - -export default LogItem diff --git a/frontend/src/components/LogOut.jsx b/frontend/src/components/LogOut.jsx deleted file mode 100644 index afd86d09..00000000 --- a/frontend/src/components/LogOut.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -const LogOut = () => { - return ( -
LogOut
- ) -} - -export default LogOut; \ No newline at end of file diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx deleted file mode 100644 index 6dbf469e..00000000 --- a/frontend/src/components/Navbar.jsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faHouse, faFileLines, faChartLine, faRightFromBracket } from '@fortawesome/free-solid-svg-icons'; -import { faPiedPiperAlt } from '@fortawesome/free-brands-svg-icons'; - -function Navbar({ activeTab, setActiveTab }) { - return ( - - ); -} - -export default Navbar; diff --git a/frontend/src/components/Pricing.jsx b/frontend/src/components/Pricing.jsx new file mode 100644 index 00000000..3e18d42d --- /dev/null +++ b/frontend/src/components/Pricing.jsx @@ -0,0 +1,217 @@ +import * as React from "react"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Card from "@mui/material/Card"; +import Chip from "@mui/material/Chip"; +import CardActions from "@mui/material/CardActions"; +import CardContent from "@mui/material/CardContent"; +import Container from "@mui/material/Container"; +import Divider from "@mui/material/Divider"; +import Grid from "@mui/material/Grid"; +import Typography from "@mui/material/Typography"; +import AutoAwesomeIcon from "@mui/icons-material/AutoAwesome"; +import CheckCircleRoundedIcon from "@mui/icons-material/CheckCircleRounded"; + +const tiers = [ + { + title: "Free", + price: "0", + description: [ + "10 users included", + "2 GB of storage", + "Help center access", + "Email support", + ], + buttonText: "Sign up for free", + buttonVariant: "outlined", + buttonColor: "primary", + }, + { + title: "Professional", + subheader: "Recommended", + price: "15", + description: [ + "20 users included", + "10 GB of storage", + "Help center access", + "Priority email support", + "Dedicated team", + "Best deals", + ], + buttonText: "Start now", + buttonVariant: "contained", + buttonColor: "secondary", + }, + { + title: "Enterprise", + price: "30", + description: [ + "50 users included", + "30 GB of storage", + "Help center access", + "Phone & email support", + ], + buttonText: "Contact us", + buttonVariant: "outlined", + buttonColor: "primary", + }, +]; + +export default function Pricing() { + return ( + + + + Pricing + + + Quickly build an effective pricing table for your potential customers + with this layout.
+ It's built with default Material UI components with little + customization. +
+
+ + {tiers.map((tier) => ( + + ({ + border: "none", + background: + "radial-gradient(circle at 50% 0%, hsl(220, 20%, 35%), hsl(220, 30%, 6%))", + boxShadow: `0 8px 12px hsla(220, 20%, 42%, 0.2)`, + ...theme.applyStyles("dark", { + background: + "radial-gradient(circle at 50% 0%, hsl(220, 20%, 20%), hsl(220, 30%, 16%))", + boxShadow: `0 8px 12px hsla(0, 0%, 0%, 0.8)`, + }), + })), + ]} + > + + + + {tier.title} + + {tier.title === "Professional" && ( + } label={tier.subheader} /> + )} + + + + ${tier.price} + + +   per month + + + + {tier.description.map((line) => ( + + + + {line} + + + ))} + + + + + + + ))} + +
+ ); +} diff --git a/frontend/src/components/SitemarkIcon.jsx b/frontend/src/components/SitemarkIcon.jsx new file mode 100644 index 00000000..a1ec4935 --- /dev/null +++ b/frontend/src/components/SitemarkIcon.jsx @@ -0,0 +1,37 @@ +import * as React from "react"; +import SvgIcon from "@mui/material/SvgIcon"; + +export default function SitemarkIcon() { + return ( + + + + + + Log Analyzer + + + + ); +} diff --git a/frontend/src/data/sampleLogs.json b/frontend/src/data/sampleLogs.json deleted file mode 100644 index 3f819231..00000000 --- a/frontend/src/data/sampleLogs.json +++ /dev/null @@ -1,536 +0,0 @@ -[ - { - "date": "2023-03-01", - "time": "10:15:30", - "userType": "User Logon", - "riskPriority": "High", - "logMessage": "SyntaxError: Unexpected token '}' in config file at line 23", - "eventIDs": [ - "E101", - "E102" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-01", - "time": "11:20:45", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "ReferenceError: variable x is not defined in main.js", - "eventIDs": [ - "E103" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-02", - "time": "09:05:10", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Kernel panic: Fatal exception in interrupt handler", - "eventIDs": [ - "E104", - "E105" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-02", - "time": "14:40:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Warning: Login attempt failed due to incorrect password", - "eventIDs": [ - "E106" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-03", - "time": "08:30:25", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Unable to connect to database on port 5432", - "eventIDs": [ - "E107" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-03", - "time": "12:15:00", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Critical: Overheating detected in CPU, system halted", - "eventIDs": [ - "E108", - "E109" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-04", - "time": "16:05:55", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Notice: User login successful after multiple attempts", - "eventIDs": [ - "E110" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-04", - "time": "18:20:30", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Memory allocation failed in process scheduler", - "eventIDs": [ - "E111" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-05", - "time": "07:45:00", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Panic: Null pointer dereference in module xyz", - "eventIDs": [ - "E112" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-05", - "time": "13:30:15", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Info: User login from remote terminal established", - "eventIDs": [ - "E113" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-06", - "time": "09:10:00", - "userType": "User Logon", - "riskPriority": "Medium", - "logMessage": "SyntaxError: Unexpected end of input in script.js", - "eventIDs": [ - "E114" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-06", - "time": "10:22:15", - "userType": "System CTL", - "riskPriority": "High", - "logMessage": "ReferenceError: Cannot access variable before initialization", - "eventIDs": [ - "E115", - "E116" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-07", - "time": "11:35:20", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Kernel panic: Fatal error in module ABC", - "eventIDs": [ - "E117" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-07", - "time": "14:50:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Warning: Suspicious login activity detected", - "eventIDs": [ - "E118" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-08", - "time": "08:00:00", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Database connection timeout", - "eventIDs": [ - "E119" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-08", - "time": "12:30:45", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Critical: Overheating detected, shutting down", - "eventIDs": [ - "E120" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-09", - "time": "09:55:30", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Notice: Login successful from new device", - "eventIDs": [ - "E121" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-09", - "time": "15:45:10", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Failed to load configuration file", - "eventIDs": [ - "E122" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-10", - "time": "07:20:00", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Panic: System halt due to unknown error", - "eventIDs": [ - "E123" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-10", - "time": "11:11:11", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Info: User logged in successfully", - "eventIDs": [ - "E124" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-11", - "time": "10:05:00", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Unexpected null value in data stream", - "eventIDs": [ - "E125" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-11", - "time": "13:15:30", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Critical: Memory leak detected in module XYZ", - "eventIDs": [ - "E126", - "E127" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-12", - "time": "09:00:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Notice: User session expired after inactivity", - "eventIDs": [ - "E128" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-12", - "time": "14:10:20", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Failed to update system settings", - "eventIDs": [ - "E129" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-13", - "time": "08:40:00", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Panic: Critical error in kernel module", - "eventIDs": [ - "E130" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-13", - "time": "12:20:15", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Warning: Multiple failed login attempts detected", - "eventIDs": [ - "E131" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-14", - "time": "07:30:45", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Connection reset during data transmission", - "eventIDs": [ - "E132" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-14", - "time": "10:55:20", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Critical: CPU temperature exceeded safe threshold", - "eventIDs": [ - "E133" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-15", - "time": "08:05:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Notice: Successful login after password reset", - "eventIDs": [ - "E134" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-15", - "time": "11:30:45", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: File system read error detected", - "eventIDs": [ - "E135" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-16", - "time": "09:15:30", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Panic: System halted due to critical hardware failure", - "eventIDs": [ - "E136" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-16", - "time": "13:45:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Info: User login successful with MFA", - "eventIDs": [ - "E137" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-17", - "time": "08:20:15", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Unexpected response from authentication server", - "eventIDs": [ - "E138" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-17", - "time": "12:35:50", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Critical: Disk failure imminent", - "eventIDs": [ - "E139", - "E140" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-18", - "time": "09:50:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Notice: User logged out successfully", - "eventIDs": [ - "E141" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-18", - "time": "14:25:30", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Configuration file not found", - "eventIDs": [ - "E142" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-19", - "time": "08:10:00", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Panic: Memory corruption detected", - "eventIDs": [ - "E143" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-19", - "time": "12:00:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Warning: Login attempt from unrecognized device", - "eventIDs": [ - "E144" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-20", - "time": "09:05:30", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Timeout while connecting to server", - "eventIDs": [ - "E145" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-20", - "time": "13:40:00", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Critical: Hardware failure in RAID array", - "eventIDs": [ - "E146" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-21", - "time": "08:30:00", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Notice: User account unlocked after verification", - "eventIDs": [ - "E147" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - }, - { - "date": "2023-03-21", - "time": "11:50:20", - "userType": "System CTL", - "riskPriority": "Medium", - "logMessage": "Error: Failed to load user profile", - "eventIDs": [ - "E148" - ], - "issueType": "Software", - "fixableType": "Fixable" - }, - { - "date": "2023-03-22", - "time": "09:00:00", - "userType": "Kernel", - "riskPriority": "High", - "logMessage": "Panic: Critical error in firmware", - "eventIDs": [ - "E149" - ], - "issueType": "Hardware", - "fixableType": "Non-Fixable" - }, - { - "date": "2023-03-22", - "time": "14:15:30", - "userType": "User Logon", - "riskPriority": "Low", - "logMessage": "Warning: Multiple login attempts detected", - "eventIDs": [ - "E150" - ], - "issueType": "Hybrid", - "fixableType": "Fixable" - } -] \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 5fb72d83..a6156fea 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,22 +1,23 @@ -import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; -import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -import App from './App.jsx'; -import './index.css'; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import { StyledEngineProvider } from "@mui/material/styles"; +import App from "./App.jsx"; +import "./index.css"; -const Dashboard = () => ( -
-

Dashboard Coming Soon

-
-); +function Dashboard() { + return

Dashboard

; +} -createRoot(document.getElementById('root')).render( +createRoot(document.getElementById("root")).render( - - - } /> - } /> - - + + + + } /> + } /> + + + -) +); diff --git a/frontend/src/theme/AppTheme.jsx b/frontend/src/theme/AppTheme.jsx new file mode 100644 index 00000000..b3ad5a4d --- /dev/null +++ b/frontend/src/theme/AppTheme.jsx @@ -0,0 +1,43 @@ +import * as React from "react"; +import { ThemeProvider, createTheme } from "@mui/material/styles"; +import { inputsCustomizations } from "./customizations/inputs"; +import { dataDisplayCustomizations } from "./customizations/dataDisplay"; +import { feedbackCustomizations } from "./customizations/feedback"; +import { navigationCustomizations } from "./customizations/navigation"; +import { surfacesCustomizations } from "./customizations/surfaces"; +import { colorSchemes, typography, shadows, shape } from "./themePrimitives"; + +export default function AppTheme(props) { + const { children, disableCustomTheme, themeComponents } = props; + const theme = React.useMemo(() => { + return disableCustomTheme + ? {} + : createTheme({ + // For more details about CSS variables configuration, see https://mui.com/material-ui/customization/css-theme-variables/configuration/ + cssVariables: { + colorSchemeSelector: "data-mui-color-scheme", + cssVarPrefix: "template", + }, + colorSchemes, // Recently added in v6 for building light & dark mode app, see https://mui.com/material-ui/customization/palette/#color-schemes + typography, + shadows, + shape, + components: { + ...inputsCustomizations, + ...dataDisplayCustomizations, + ...feedbackCustomizations, + ...navigationCustomizations, + ...surfacesCustomizations, + ...themeComponents, + }, + }); + }, [disableCustomTheme, themeComponents]); + if (disableCustomTheme) { + return {children}; + } + return ( + + {children} + + ); +} diff --git a/frontend/src/theme/ColorModeIconDropdown.jsx b/frontend/src/theme/ColorModeIconDropdown.jsx new file mode 100644 index 00000000..08a2a13b --- /dev/null +++ b/frontend/src/theme/ColorModeIconDropdown.jsx @@ -0,0 +1,89 @@ +import * as React from "react"; +import DarkModeIcon from "@mui/icons-material/DarkModeRounded"; +import LightModeIcon from "@mui/icons-material/LightModeRounded"; +import Box from "@mui/material/Box"; +import IconButton from "@mui/material/IconButton"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import { useColorScheme } from "@mui/material/styles"; + +export default function ColorModeIconDropdown(props) { + const { mode, systemMode, setMode } = useColorScheme(); + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleMode = (targetMode) => () => { + setMode(targetMode); + handleClose(); + }; + if (!mode) { + return ( + ({ + verticalAlign: "bottom", + display: "inline-flex", + width: "2.25rem", + height: "2.25rem", + borderRadius: (theme.vars || theme).shape.borderRadius, + border: "1px solid", + borderColor: (theme.vars || theme).palette.divider, + })} + /> + ); + } + const resolvedMode = systemMode || mode; + const icon = { + light: , + dark: , + }[resolvedMode]; + return ( + + + {icon} + + + + System + + + Light + + + Dark + + + + ); +} diff --git a/frontend/src/theme/ColorModeSelect.jsx b/frontend/src/theme/ColorModeSelect.jsx new file mode 100644 index 00000000..b56bb8b5 --- /dev/null +++ b/frontend/src/theme/ColorModeSelect.jsx @@ -0,0 +1,25 @@ +import * as React from "react"; +import { useColorScheme } from "@mui/material/styles"; +import MenuItem from "@mui/material/MenuItem"; +import Select from "@mui/material/Select"; + +export default function ColorModeSelect(props) { + const { mode, setMode } = useColorScheme(); + if (!mode) { + return null; + } + return ( + + ); +} diff --git a/frontend/src/theme/customizations/charts.js b/frontend/src/theme/customizations/charts.js new file mode 100644 index 00000000..7e075bcb --- /dev/null +++ b/frontend/src/theme/customizations/charts.js @@ -0,0 +1,73 @@ +import { axisClasses, legendClasses, chartsGridClasses } from '@mui/x-charts'; +import { gray } from '../themePrimitives'; + +export const chartsCustomizations = { + MuiChartsAxis: { + styleOverrides: { + root: ({ theme }) => ({ + [`& .${axisClasses.line}`]: { + stroke: gray[300], + }, + [`& .${axisClasses.tick}`]: { stroke: gray[300] }, + [`& .${axisClasses.tickLabel}`]: { + fill: gray[500], + fontWeight: 500, + }, + ...theme.applyStyles('dark', { + [`& .${axisClasses.line}`]: { + stroke: gray[700], + }, + [`& .${axisClasses.tick}`]: { stroke: gray[700] }, + [`& .${axisClasses.tickLabel}`]: { + fill: gray[300], + fontWeight: 500, + }, + }), + }), + }, + }, + MuiChartsTooltip: { + styleOverrides: { + mark: ({ theme }) => ({ + ry: 6, + boxShadow: 'none', + border: `1px solid ${(theme.vars || theme).palette.divider}`, + }), + table: ({ theme }) => ({ + border: `1px solid ${(theme.vars || theme).palette.divider}`, + borderRadius: theme.shape.borderRadius, + background: 'hsl(0, 0%, 100%)', + ...theme.applyStyles('dark', { + background: gray[900], + }), + }), + }, + }, + MuiChartsLegend: { + styleOverrides: { + root: { + [`& .${legendClasses.mark}`]: { + ry: 6, + }, + }, + }, + }, + MuiChartsGrid: { + styleOverrides: { + root: ({ theme }) => ({ + [`& .${chartsGridClasses.line}`]: { + stroke: gray[200], + strokeDasharray: '4 2', + strokeWidth: 0.8, + }, + ...theme.applyStyles('dark', { + [`& .${chartsGridClasses.line}`]: { + stroke: gray[700], + strokeDasharray: '4 2', + strokeWidth: 0.8, + }, + }), + }), + }, + }, +}; diff --git a/frontend/src/theme/customizations/dataDisplay.jsx b/frontend/src/theme/customizations/dataDisplay.jsx new file mode 100644 index 00000000..7e0d71a8 --- /dev/null +++ b/frontend/src/theme/customizations/dataDisplay.jsx @@ -0,0 +1,232 @@ +import { alpha } from "@mui/material/styles"; +import { svgIconClasses } from "@mui/material/SvgIcon"; +import { typographyClasses } from "@mui/material/Typography"; +import { buttonBaseClasses } from "@mui/material/ButtonBase"; +import { chipClasses } from "@mui/material/Chip"; +import { iconButtonClasses } from "@mui/material/IconButton"; +import { gray, red, green } from "../themePrimitives"; + +export const dataDisplayCustomizations = { + MuiList: { + styleOverrides: { + root: { + padding: "8px", + display: "flex", + flexDirection: "column", + gap: 0, + }, + }, + }, + MuiListItem: { + styleOverrides: { + root: ({ theme }) => ({ + [`& .${svgIconClasses.root}`]: { + width: "1rem", + height: "1rem", + color: (theme.vars || theme).palette.text.secondary, + }, + [`& .${typographyClasses.root}`]: { + fontWeight: 500, + }, + [`& .${buttonBaseClasses.root}`]: { + display: "flex", + gap: 8, + padding: "2px 8px", + borderRadius: (theme.vars || theme).shape.borderRadius, + opacity: 0.7, + "&.Mui-selected": { + opacity: 1, + backgroundColor: alpha(theme.palette.action.selected, 0.3), + [`& .${svgIconClasses.root}`]: { + color: (theme.vars || theme).palette.text.primary, + }, + "&:focus-visible": { + backgroundColor: alpha(theme.palette.action.selected, 0.3), + }, + "&:hover": { + backgroundColor: alpha(theme.palette.action.selected, 0.5), + }, + }, + "&:focus-visible": { + backgroundColor: "transparent", + }, + }, + }), + }, + }, + MuiListItemText: { + styleOverrides: { + primary: ({ theme }) => ({ + fontSize: theme.typography.body2.fontSize, + fontWeight: 500, + lineHeight: theme.typography.body2.lineHeight, + }), + secondary: ({ theme }) => ({ + fontSize: theme.typography.caption.fontSize, + lineHeight: theme.typography.caption.lineHeight, + }), + }, + }, + MuiListSubheader: { + styleOverrides: { + root: ({ theme }) => ({ + backgroundColor: "transparent", + padding: "4px 8px", + fontSize: theme.typography.caption.fontSize, + fontWeight: 500, + lineHeight: theme.typography.caption.lineHeight, + }), + }, + }, + MuiListItemIcon: { + styleOverrides: { + root: { + minWidth: 0, + }, + }, + }, + MuiChip: { + defaultProps: { + size: "small", + }, + styleOverrides: { + root: ({ theme }) => ({ + border: "1px solid", + borderRadius: "999px", + [`& .${chipClasses.label}`]: { + fontWeight: 600, + }, + variants: [ + { + props: { + color: "default", + }, + style: { + borderColor: gray[200], + backgroundColor: gray[100], + [`& .${chipClasses.label}`]: { + color: gray[500], + }, + [`& .${chipClasses.icon}`]: { + color: gray[500], + }, + ...theme.applyStyles("dark", { + borderColor: gray[700], + backgroundColor: gray[800], + [`& .${chipClasses.label}`]: { + color: gray[300], + }, + [`& .${chipClasses.icon}`]: { + color: gray[300], + }, + }), + }, + }, + { + props: { + color: "success", + }, + style: { + borderColor: green[200], + backgroundColor: green[50], + [`& .${chipClasses.label}`]: { + color: green[500], + }, + [`& .${chipClasses.icon}`]: { + color: green[500], + }, + ...theme.applyStyles("dark", { + borderColor: green[800], + backgroundColor: green[900], + [`& .${chipClasses.label}`]: { + color: green[300], + }, + [`& .${chipClasses.icon}`]: { + color: green[300], + }, + }), + }, + }, + { + props: { + color: "error", + }, + style: { + borderColor: red[100], + backgroundColor: red[50], + [`& .${chipClasses.label}`]: { + color: red[500], + }, + [`& .${chipClasses.icon}`]: { + color: red[500], + }, + ...theme.applyStyles("dark", { + borderColor: red[800], + backgroundColor: red[900], + [`& .${chipClasses.label}`]: { + color: red[200], + }, + [`& .${chipClasses.icon}`]: { + color: red[300], + }, + }), + }, + }, + { + props: { size: "small" }, + style: { + maxHeight: 20, + [`& .${chipClasses.label}`]: { + fontSize: theme.typography.caption.fontSize, + }, + [`& .${svgIconClasses.root}`]: { + fontSize: theme.typography.caption.fontSize, + }, + }, + }, + { + props: { size: "medium" }, + style: { + [`& .${chipClasses.label}`]: { + fontSize: theme.typography.caption.fontSize, + }, + }, + }, + ], + }), + }, + }, + MuiTablePagination: { + styleOverrides: { + actions: { + display: "flex", + gap: 8, + marginRight: 6, + [`& .${iconButtonClasses.root}`]: { + minWidth: 0, + width: 36, + height: 36, + }, + }, + }, + }, + MuiIcon: { + defaultProps: { + fontSize: "small", + }, + styleOverrides: { + root: { + variants: [ + { + props: { + fontSize: "small", + }, + style: { + fontSize: "1rem", + }, + }, + ], + }, + }, + }, +}; diff --git a/frontend/src/theme/customizations/dataGrid.js b/frontend/src/theme/customizations/dataGrid.js new file mode 100644 index 00000000..6127b19b --- /dev/null +++ b/frontend/src/theme/customizations/dataGrid.js @@ -0,0 +1,130 @@ +import { paperClasses } from '@mui/material/Paper'; +import { alpha } from '@mui/material/styles'; +import { menuItemClasses } from '@mui/material/MenuItem'; +import { listItemIconClasses } from '@mui/material/ListItemIcon'; +import { iconButtonClasses } from '@mui/material/IconButton'; +import { checkboxClasses } from '@mui/material/Checkbox'; +import { listClasses } from '@mui/material/List'; +import { gridClasses } from '@mui/x-data-grid'; +import { tablePaginationClasses } from '@mui/material/TablePagination'; +import { gray } from '../themePrimitives'; + +export const dataGridCustomizations= { + MuiDataGrid: { + styleOverrides: { + root: ({ theme }) => ({ + '--DataGrid-overlayHeight': '300px', + overflow: 'clip', + borderColor: (theme.vars || theme).palette.divider, + backgroundColor: (theme.vars || theme).palette.background.default, + [`& .${gridClasses.columnHeader}`]: { + backgroundColor: (theme.vars || theme).palette.background.paper, + }, + [`& .${gridClasses.footerContainer}`]: { + backgroundColor: (theme.vars || theme).palette.background.paper, + }, + [`& .${checkboxClasses.root}`]: { + padding: theme.spacing(0.5), + '& > svg': { + fontSize: '1rem', + }, + }, + [`& .${tablePaginationClasses.root}`]: { + marginRight: theme.spacing(1), + '& .MuiIconButton-root': { + maxHeight: 32, + maxWidth: 32, + '& > svg': { + fontSize: '1rem', + }, + }, + }, + }), + cell: ({ theme }) => ({ borderTopColor: (theme.vars || theme).palette.divider }), + menu: ({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + backgroundImage: 'none', + [`& .${paperClasses.root}`]: { + border: `1px solid ${(theme.vars || theme).palette.divider}`, + }, + + [`& .${menuItemClasses.root}`]: { + margin: '0 4px', + }, + [`& .${listItemIconClasses.root}`]: { + marginRight: 0, + }, + [`& .${listClasses.root}`]: { + paddingLeft: 0, + paddingRight: 0, + }, + }), + + row: ({ theme }) => ({ + '&:last-of-type': { borderBottom: `1px solid ${(theme.vars || theme).palette.divider}` }, + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + '&.Mui-selected': { + background: (theme.vars || theme).palette.action.selected, + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + }, + }), + iconButtonContainer: ({ theme }) => ({ + [`& .${iconButtonClasses.root}`]: { + border: 'none', + backgroundColor: 'transparent', + '&:hover': { + backgroundColor: alpha(theme.palette.action.selected, 0.3), + }, + '&:active': { + backgroundColor: gray[200], + }, + ...theme.applyStyles('dark', { + color: gray[50], + '&:hover': { + backgroundColor: gray[800], + }, + '&:active': { + backgroundColor: gray[900], + }, + }), + }, + }), + menuIconButton: ({ theme }) => ({ + border: 'none', + backgroundColor: 'transparent', + '&:hover': { + backgroundColor: gray[100], + }, + '&:active': { + backgroundColor: gray[200], + }, + ...theme.applyStyles('dark', { + color: gray[50], + '&:hover': { + backgroundColor: gray[800], + }, + '&:active': { + backgroundColor: gray[900], + }, + }), + }), + filterForm: ({ theme }) => ({ + gap: theme.spacing(1), + alignItems: 'flex-end', + }), + columnsManagementHeader: ({ theme }) => ({ + paddingRight: theme.spacing(3), + paddingLeft: theme.spacing(3), + }), + columnHeaderTitleContainer: { + flexGrow: 1, + justifyContent: 'space-between', + }, + columnHeaderDraggableContainer: { paddingRight: 2 }, + }, + }, +}; diff --git a/frontend/src/theme/customizations/datePickers.js b/frontend/src/theme/customizations/datePickers.js new file mode 100644 index 00000000..58e5e08a --- /dev/null +++ b/frontend/src/theme/customizations/datePickers.js @@ -0,0 +1,170 @@ +import { alpha } from '@mui/material/styles'; +import { pickersYearClasses, pickersMonthClasses, pickersDayClasses } from '@mui/x-date-pickers'; +import { menuItemClasses } from '@mui/material/MenuItem'; +import { gray, brand } from '../themePrimitives'; + +export const datePickersCustomizations= { + MuiPickersPopper: { + styleOverrides: { + paper: ({ theme }) => ({ + marginTop: 4, + borderRadius: theme.shape.borderRadius, + border: `1px solid ${(theme.vars || theme).palette.divider}`, + backgroundImage: 'none', + background: 'hsl(0, 0%, 100%)', + boxShadow: + 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px', + [`& .${menuItemClasses.root}`]: { + borderRadius: 6, + margin: '0 6px', + }, + ...theme.applyStyles('dark', { + background: gray[900], + boxShadow: + 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px', + }), + }), + }, + }, + MuiPickersArrowSwitcher: { + styleOverrides: { + spacer: { width: 16 }, + button: ({ theme }) => ({ + backgroundColor: 'transparent', + color: (theme.vars || theme).palette.grey[500], + ...theme.applyStyles('dark', { + color: (theme.vars || theme).palette.grey[400], + }), + }), + }, + }, + MuiPickersCalendarHeader: { + styleOverrides: { + switchViewButton: { + padding: 0, + border: 'none', + }, + }, + }, + MuiPickersMonth: { + styleOverrides: { + monthButton: ({ theme }) => ({ + fontSize: theme.typography.body1.fontSize, + color: (theme.vars || theme).palette.grey[600], + padding: theme.spacing(0.5), + borderRadius: theme.shape.borderRadius, + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + [`&.${pickersMonthClasses.selected}`]: { + backgroundColor: gray[700], + fontWeight: theme.typography.fontWeightMedium, + }, + '&:focus': { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + backgroundColor: 'transparent', + [`&.${pickersMonthClasses.selected}`]: { backgroundColor: gray[700] }, + }, + ...theme.applyStyles('dark', { + color: (theme.vars || theme).palette.grey[300], + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + [`&.${pickersMonthClasses.selected}`]: { + color: (theme.vars || theme).palette.common.black, + fontWeight: theme.typography.fontWeightMedium, + backgroundColor: gray[300], + }, + '&:focus': { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + backgroundColor: 'transparent', + [`&.${pickersMonthClasses.selected}`]: { backgroundColor: gray[300] }, + }, + }), + }), + }, + }, + MuiPickersYear: { + styleOverrides: { + yearButton: ({ theme }) => ({ + fontSize: theme.typography.body1.fontSize, + color: (theme.vars || theme).palette.grey[600], + padding: theme.spacing(0.5), + borderRadius: theme.shape.borderRadius, + height: 'fit-content', + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + [`&.${pickersYearClasses.selected}`]: { + backgroundColor: gray[700], + fontWeight: theme.typography.fontWeightMedium, + }, + '&:focus': { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + backgroundColor: 'transparent', + [`&.${pickersYearClasses.selected}`]: { backgroundColor: gray[700] }, + }, + ...theme.applyStyles('dark', { + color: (theme.vars || theme).palette.grey[300], + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + [`&.${pickersYearClasses.selected}`]: { + color: (theme.vars || theme).palette.common.black, + fontWeight: theme.typography.fontWeightMedium, + backgroundColor: gray[300], + }, + '&:focus': { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + backgroundColor: 'transparent', + [`&.${pickersYearClasses.selected}`]: { backgroundColor: gray[300] }, + }, + }), + }), + }, + }, + MuiPickersDay: { + styleOverrides: { + root: ({ theme }) => ({ + fontSize: theme.typography.body1.fontSize, + color: (theme.vars || theme).palette.grey[600], + padding: theme.spacing(0.5), + borderRadius: theme.shape.borderRadius, + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + [`&.${pickersDayClasses.selected}`]: { + backgroundColor: gray[700], + fontWeight: theme.typography.fontWeightMedium, + }, + '&:focus': { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + backgroundColor: 'transparent', + [`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[700] }, + }, + ...theme.applyStyles('dark', { + color: (theme.vars || theme).palette.grey[300], + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + }, + [`&.${pickersDayClasses.selected}`]: { + color: (theme.vars || theme).palette.common.black, + fontWeight: theme.typography.fontWeightMedium, + backgroundColor: gray[300], + }, + '&:focus': { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + backgroundColor: 'transparent', + [`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[300] }, + }, + }), + }), + }, + }, +}; diff --git a/frontend/src/theme/customizations/feedback.jsx b/frontend/src/theme/customizations/feedback.jsx new file mode 100644 index 00000000..067aef40 --- /dev/null +++ b/frontend/src/theme/customizations/feedback.jsx @@ -0,0 +1,45 @@ +import { alpha } from "@mui/material/styles"; +import { gray, orange } from "../themePrimitives"; + +export const feedbackCustomizations = { + MuiAlert: { + styleOverrides: { + root: ({ theme }) => ({ + borderRadius: 10, + backgroundColor: orange[100], + color: (theme.vars || theme).palette.text.primary, + border: `1px solid ${alpha(orange[300], 0.5)}`, + "& .MuiAlert-icon": { + color: orange[500], + }, + ...theme.applyStyles("dark", { + backgroundColor: `${alpha(orange[900], 0.5)}`, + border: `1px solid ${alpha(orange[800], 0.5)}`, + }), + }), + }, + }, + MuiDialog: { + styleOverrides: { + root: ({ theme }) => ({ + "& .MuiDialog-paper": { + borderRadius: "10px", + border: "1px solid", + borderColor: (theme.vars || theme).palette.divider, + }, + }), + }, + }, + MuiLinearProgress: { + styleOverrides: { + root: ({ theme }) => ({ + height: 8, + borderRadius: 8, + backgroundColor: gray[200], + ...theme.applyStyles("dark", { + backgroundColor: gray[800], + }), + }), + }, + }, +}; diff --git a/frontend/src/theme/customizations/inputs.jsx b/frontend/src/theme/customizations/inputs.jsx new file mode 100644 index 00000000..07db6419 --- /dev/null +++ b/frontend/src/theme/customizations/inputs.jsx @@ -0,0 +1,452 @@ +import * as React from "react"; +import { alpha } from "@mui/material/styles"; +import { outlinedInputClasses } from "@mui/material/OutlinedInput"; +import { svgIconClasses } from "@mui/material/SvgIcon"; +import { toggleButtonGroupClasses } from "@mui/material/ToggleButtonGroup"; +import { toggleButtonClasses } from "@mui/material/ToggleButton"; +import CheckBoxOutlineBlankRoundedIcon from "@mui/icons-material/CheckBoxOutlineBlankRounded"; +import CheckRoundedIcon from "@mui/icons-material/CheckRounded"; +import RemoveRoundedIcon from "@mui/icons-material/RemoveRounded"; +import { gray, brand } from "../themePrimitives"; + +export const inputsCustomizations = { + MuiButtonBase: { + defaultProps: { + disableTouchRipple: true, + disableRipple: true, + }, + styleOverrides: { + root: ({ theme }) => ({ + boxSizing: "border-box", + transition: "all 100ms ease-in", + "&:focus-visible": { + outline: `3px solid ${alpha(theme.palette.primary.main, 0.5)}`, + outlineOffset: "2px", + }, + }), + }, + }, + MuiButton: { + styleOverrides: { + root: ({ theme }) => ({ + boxShadow: "none", + borderRadius: (theme.vars || theme).shape.borderRadius, + textTransform: "none", + variants: [ + { + props: { + size: "small", + }, + style: { + height: "2.25rem", + padding: "8px 12px", + }, + }, + { + props: { + size: "medium", + }, + style: { + height: "2.5rem", // 40px + }, + }, + { + props: { + color: "primary", + variant: "contained", + }, + style: { + color: "white", + backgroundColor: gray[900], + backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`, + boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`, + border: `1px solid ${gray[700]}`, + "&:hover": { + backgroundImage: "none", + backgroundColor: gray[700], + boxShadow: "none", + }, + "&:active": { + backgroundColor: gray[800], + }, + ...theme.applyStyles("dark", { + color: "black", + backgroundColor: gray[50], + backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`, + boxShadow: "inset 0 -1px 0 hsl(220, 30%, 80%)", + border: `1px solid ${gray[50]}`, + "&:hover": { + backgroundImage: "none", + backgroundColor: gray[300], + boxShadow: "none", + }, + "&:active": { + backgroundColor: gray[400], + }, + }), + }, + }, + { + props: { + color: "secondary", + variant: "contained", + }, + style: { + color: "white", + backgroundColor: brand[300], + backgroundImage: `linear-gradient(to bottom, ${alpha( + brand[400], + 0.8 + )}, ${brand[500]})`, + boxShadow: `inset 0 2px 0 ${alpha( + brand[200], + 0.2 + )}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`, + border: `1px solid ${brand[500]}`, + "&:hover": { + backgroundColor: brand[700], + boxShadow: "none", + }, + "&:active": { + backgroundColor: brand[700], + backgroundImage: "none", + }, + }, + }, + { + props: { + variant: "outlined", + }, + style: { + color: (theme.vars || theme).palette.text.primary, + border: "1px solid", + borderColor: gray[200], + backgroundColor: alpha(gray[50], 0.3), + "&:hover": { + backgroundColor: gray[100], + borderColor: gray[300], + }, + "&:active": { + backgroundColor: gray[200], + }, + ...theme.applyStyles("dark", { + backgroundColor: gray[800], + borderColor: gray[700], + + "&:hover": { + backgroundColor: gray[900], + borderColor: gray[600], + }, + "&:active": { + backgroundColor: gray[900], + }, + }), + }, + }, + { + props: { + color: "secondary", + variant: "outlined", + }, + style: { + color: brand[700], + border: "1px solid", + borderColor: brand[200], + backgroundColor: brand[50], + "&:hover": { + backgroundColor: brand[100], + borderColor: brand[400], + }, + "&:active": { + backgroundColor: alpha(brand[200], 0.7), + }, + ...theme.applyStyles("dark", { + color: brand[50], + border: "1px solid", + borderColor: brand[900], + backgroundColor: alpha(brand[900], 0.3), + "&:hover": { + borderColor: brand[700], + backgroundColor: alpha(brand[900], 0.6), + }, + "&:active": { + backgroundColor: alpha(brand[900], 0.5), + }, + }), + }, + }, + { + props: { + variant: "text", + }, + style: { + color: gray[600], + "&:hover": { + backgroundColor: gray[100], + }, + "&:active": { + backgroundColor: gray[200], + }, + ...theme.applyStyles("dark", { + color: gray[50], + "&:hover": { + backgroundColor: gray[700], + }, + "&:active": { + backgroundColor: alpha(gray[700], 0.7), + }, + }), + }, + }, + { + props: { + color: "secondary", + variant: "text", + }, + style: { + color: brand[700], + "&:hover": { + backgroundColor: alpha(brand[100], 0.5), + }, + "&:active": { + backgroundColor: alpha(brand[200], 0.7), + }, + ...theme.applyStyles("dark", { + color: brand[100], + "&:hover": { + backgroundColor: alpha(brand[900], 0.5), + }, + "&:active": { + backgroundColor: alpha(brand[900], 0.3), + }, + }), + }, + }, + ], + }), + }, + }, + MuiIconButton: { + styleOverrides: { + root: ({ theme }) => ({ + boxShadow: "none", + borderRadius: (theme.vars || theme).shape.borderRadius, + textTransform: "none", + fontWeight: theme.typography.fontWeightMedium, + letterSpacing: 0, + color: (theme.vars || theme).palette.text.primary, + border: "1px solid ", + borderColor: gray[200], + backgroundColor: alpha(gray[50], 0.3), + "&:hover": { + backgroundColor: gray[100], + borderColor: gray[300], + }, + "&:active": { + backgroundColor: gray[200], + }, + ...theme.applyStyles("dark", { + backgroundColor: gray[800], + borderColor: gray[700], + "&:hover": { + backgroundColor: gray[900], + borderColor: gray[600], + }, + "&:active": { + backgroundColor: gray[900], + }, + }), + variants: [ + { + props: { + size: "small", + }, + style: { + width: "2.25rem", + height: "2.25rem", + padding: "0.25rem", + [`& .${svgIconClasses.root}`]: { fontSize: "1rem" }, + }, + }, + { + props: { + size: "medium", + }, + style: { + width: "2.5rem", + height: "2.5rem", + }, + }, + ], + }), + }, + }, + MuiToggleButtonGroup: { + styleOverrides: { + root: ({ theme }) => ({ + borderRadius: "10px", + boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`, + [`& .${toggleButtonGroupClasses.selected}`]: { + color: brand[500], + }, + ...theme.applyStyles("dark", { + [`& .${toggleButtonGroupClasses.selected}`]: { + color: "#fff", + }, + boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`, + }), + }), + }, + }, + MuiToggleButton: { + styleOverrides: { + root: ({ theme }) => ({ + padding: "12px 16px", + textTransform: "none", + borderRadius: "10px", + fontWeight: 500, + ...theme.applyStyles("dark", { + color: gray[400], + boxShadow: "0 4px 16px rgba(0, 0, 0, 0.5)", + [`&.${toggleButtonClasses.selected}`]: { + color: brand[300], + }, + }), + }), + }, + }, + MuiCheckbox: { + defaultProps: { + disableRipple: true, + icon: ( + + ), + checkedIcon: , + indeterminateIcon: , + }, + styleOverrides: { + root: ({ theme }) => ({ + margin: 10, + height: 16, + width: 16, + borderRadius: 5, + border: "1px solid ", + borderColor: alpha(gray[300], 0.8), + boxShadow: "0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset", + backgroundColor: alpha(gray[100], 0.4), + transition: "border-color, background-color, 120ms ease-in", + "&:hover": { + borderColor: brand[300], + }, + "&.Mui-focusVisible": { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: "2px", + borderColor: brand[400], + }, + "&.Mui-checked": { + color: "white", + backgroundColor: brand[500], + borderColor: brand[500], + boxShadow: `none`, + "&:hover": { + backgroundColor: brand[600], + }, + }, + ...theme.applyStyles("dark", { + borderColor: alpha(gray[700], 0.8), + boxShadow: "0 0 0 1.5px hsl(210, 0%, 0%) inset", + backgroundColor: alpha(gray[900], 0.8), + "&:hover": { + borderColor: brand[300], + }, + "&.Mui-focusVisible": { + borderColor: brand[400], + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: "2px", + }, + }), + }), + }, + }, + MuiInputBase: { + styleOverrides: { + root: { + border: "none", + }, + input: { + "&::placeholder": { + opacity: 0.7, + color: gray[500], + }, + }, + }, + }, + MuiOutlinedInput: { + styleOverrides: { + input: { + padding: 0, + }, + root: ({ theme }) => ({ + padding: "8px 12px", + color: (theme.vars || theme).palette.text.primary, + borderRadius: (theme.vars || theme).shape.borderRadius, + border: `1px solid ${(theme.vars || theme).palette.divider}`, + backgroundColor: (theme.vars || theme).palette.background.default, + transition: "border 120ms ease-in", + "&:hover": { + borderColor: gray[400], + }, + [`&.${outlinedInputClasses.focused}`]: { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + borderColor: brand[400], + }, + ...theme.applyStyles("dark", { + "&:hover": { + borderColor: gray[500], + }, + }), + variants: [ + { + props: { + size: "small", + }, + style: { + height: "2.25rem", + }, + }, + { + props: { + size: "medium", + }, + style: { + height: "2.5rem", + }, + }, + ], + }), + notchedOutline: { + border: "none", + }, + }, + }, + MuiInputAdornment: { + styleOverrides: { + root: ({ theme }) => ({ + color: (theme.vars || theme).palette.grey[500], + ...theme.applyStyles("dark", { + color: (theme.vars || theme).palette.grey[400], + }), + }), + }, + }, + MuiFormLabel: { + styleOverrides: { + root: ({ theme }) => ({ + typography: theme.typography.caption, + marginBottom: 8, + }), + }, + }, +}; diff --git a/frontend/src/theme/customizations/navigation.jsx b/frontend/src/theme/customizations/navigation.jsx new file mode 100644 index 00000000..9e9ae493 --- /dev/null +++ b/frontend/src/theme/customizations/navigation.jsx @@ -0,0 +1,280 @@ +import * as React from "react"; +import { alpha } from "@mui/material/styles"; +import { buttonBaseClasses } from "@mui/material/ButtonBase"; +import { dividerClasses } from "@mui/material/Divider"; +import { menuItemClasses } from "@mui/material/MenuItem"; +import { selectClasses } from "@mui/material/Select"; +import { tabClasses } from "@mui/material/Tab"; +import UnfoldMoreRoundedIcon from "@mui/icons-material/UnfoldMoreRounded"; +import { gray, brand } from "../themePrimitives"; + +export const navigationCustomizations = { + MuiMenuItem: { + styleOverrides: { + root: ({ theme }) => ({ + borderRadius: (theme.vars || theme).shape.borderRadius, + padding: "6px 8px", + [`&.${menuItemClasses.focusVisible}`]: { + backgroundColor: "transparent", + }, + [`&.${menuItemClasses.selected}`]: { + [`&.${menuItemClasses.focusVisible}`]: { + backgroundColor: alpha(theme.palette.action.selected, 0.3), + }, + }, + }), + }, + }, + MuiMenu: { + styleOverrides: { + list: { + gap: "0px", + [`&.${dividerClasses.root}`]: { + margin: "0 -8px", + }, + }, + paper: ({ theme }) => ({ + marginTop: "4px", + borderRadius: (theme.vars || theme).shape.borderRadius, + border: `1px solid ${(theme.vars || theme).palette.divider}`, + backgroundImage: "none", + background: "hsl(0, 0%, 100%)", + boxShadow: + "hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px", + [`& .${buttonBaseClasses.root}`]: { + "&.Mui-selected": { + backgroundColor: alpha(theme.palette.action.selected, 0.3), + }, + }, + ...theme.applyStyles("dark", { + background: gray[900], + boxShadow: + "hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px", + }), + }), + }, + }, + MuiSelect: { + defaultProps: { + IconComponent: React.forwardRef((props, ref) => ( + + )), + }, + styleOverrides: { + root: ({ theme }) => ({ + borderRadius: (theme.vars || theme).shape.borderRadius, + border: "1px solid", + borderColor: gray[200], + backgroundColor: (theme.vars || theme).palette.background.paper, + boxShadow: `inset 0 1px 0 1px hsla(220, 0%, 100%, 0.6), inset 0 -1px 0 1px hsla(220, 35%, 90%, 0.5)`, + "&:hover": { + borderColor: gray[300], + backgroundColor: (theme.vars || theme).palette.background.paper, + boxShadow: "none", + }, + [`&.${selectClasses.focused}`]: { + outlineOffset: 0, + borderColor: gray[400], + }, + "&:before, &:after": { + display: "none", + }, + + ...theme.applyStyles("dark", { + borderRadius: (theme.vars || theme).shape.borderRadius, + borderColor: gray[700], + backgroundColor: (theme.vars || theme).palette.background.paper, + boxShadow: `inset 0 1px 0 1px ${alpha( + gray[700], + 0.15 + )}, inset 0 -1px 0 1px hsla(220, 0%, 0%, 0.7)`, + "&:hover": { + borderColor: alpha(gray[700], 0.7), + backgroundColor: (theme.vars || theme).palette.background.paper, + boxShadow: "none", + }, + [`&.${selectClasses.focused}`]: { + outlineOffset: 0, + borderColor: gray[900], + }, + "&:before, &:after": { + display: "none", + }, + }), + }), + select: ({ theme }) => ({ + display: "flex", + alignItems: "center", + ...theme.applyStyles("dark", { + display: "flex", + alignItems: "center", + "&:focus-visible": { + backgroundColor: gray[900], + }, + }), + }), + }, + }, + MuiLink: { + defaultProps: { + underline: "none", + }, + styleOverrides: { + root: ({ theme }) => ({ + color: (theme.vars || theme).palette.text.primary, + fontWeight: 500, + position: "relative", + textDecoration: "none", + width: "fit-content", + "&::before": { + content: '""', + position: "absolute", + width: "100%", + height: "1px", + bottom: 0, + left: 0, + backgroundColor: (theme.vars || theme).palette.text.secondary, + opacity: 0.3, + transition: "width 0.3s ease, opacity 0.3s ease", + }, + "&:hover::before": { + width: 0, + }, + "&:focus-visible": { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: "4px", + borderRadius: "2px", + }, + }), + }, + }, + MuiDrawer: { + styleOverrides: { + paper: ({ theme }) => ({ + backgroundColor: (theme.vars || theme).palette.background.default, + }), + }, + }, + MuiPaginationItem: { + styleOverrides: { + root: ({ theme }) => ({ + "&.Mui-selected": { + color: "white", + backgroundColor: (theme.vars || theme).palette.grey[900], + }, + ...theme.applyStyles("dark", { + "&.Mui-selected": { + color: "black", + backgroundColor: (theme.vars || theme).palette.grey[50], + }, + }), + }), + }, + }, + MuiTabs: { + styleOverrides: { + root: { minHeight: "fit-content" }, + indicator: ({ theme }) => ({ + backgroundColor: (theme.vars || theme).palette.grey[800], + ...theme.applyStyles("dark", { + backgroundColor: (theme.vars || theme).palette.grey[200], + }), + }), + }, + }, + MuiTab: { + styleOverrides: { + root: ({ theme }) => ({ + padding: "6px 8px", + marginBottom: "8px", + textTransform: "none", + minWidth: "fit-content", + minHeight: "fit-content", + color: (theme.vars || theme).palette.text.secondary, + borderRadius: (theme.vars || theme).shape.borderRadius, + border: "1px solid", + borderColor: "transparent", + ":hover": { + color: (theme.vars || theme).palette.text.primary, + backgroundColor: gray[100], + borderColor: gray[200], + }, + [`&.${tabClasses.selected}`]: { + color: gray[900], + }, + ...theme.applyStyles("dark", { + ":hover": { + color: (theme.vars || theme).palette.text.primary, + backgroundColor: gray[800], + borderColor: gray[700], + }, + [`&.${tabClasses.selected}`]: { + color: "#fff", + }, + }), + }), + }, + }, + MuiStepConnector: { + styleOverrides: { + line: ({ theme }) => ({ + borderTop: "1px solid", + borderColor: (theme.vars || theme).palette.divider, + flex: 1, + borderRadius: "99px", + }), + }, + }, + MuiStepIcon: { + styleOverrides: { + root: ({ theme }) => ({ + color: "transparent", + border: `1px solid ${gray[400]}`, + width: 12, + height: 12, + borderRadius: "50%", + "& text": { + display: "none", + }, + "&.Mui-active": { + border: "none", + color: (theme.vars || theme).palette.primary.main, + }, + "&.Mui-completed": { + border: "none", + color: (theme.vars || theme).palette.success.main, + }, + ...theme.applyStyles("dark", { + border: `1px solid ${gray[700]}`, + "&.Mui-active": { + border: "none", + color: (theme.vars || theme).palette.primary.light, + }, + "&.Mui-completed": { + border: "none", + color: (theme.vars || theme).palette.success.light, + }, + }), + variants: [ + { + props: { completed: true }, + style: { + width: 12, + height: 12, + }, + }, + ], + }), + }, + }, + MuiStepLabel: { + styleOverrides: { + label: ({ theme }) => ({ + "&.Mui-completed": { + opacity: 0.6, + ...theme.applyStyles("dark", { opacity: 0.5 }), + }, + }), + }, + }, +}; diff --git a/frontend/src/theme/customizations/surfaces.js b/frontend/src/theme/customizations/surfaces.js new file mode 100644 index 00000000..674e77ac --- /dev/null +++ b/frontend/src/theme/customizations/surfaces.js @@ -0,0 +1,112 @@ +import { alpha } from '@mui/material/styles'; +import { gray } from '../themePrimitives'; + +export const surfacesCustomizations = { + MuiAccordion: { + defaultProps: { + elevation: 0, + disableGutters: true, + }, + styleOverrides: { + root: ({ theme }) => ({ + padding: 4, + overflow: 'clip', + backgroundColor: (theme.vars || theme).palette.background.default, + border: '1px solid', + borderColor: (theme.vars || theme).palette.divider, + ':before': { + backgroundColor: 'transparent', + }, + '&:not(:last-of-type)': { + borderBottom: 'none', + }, + '&:first-of-type': { + borderTopLeftRadius: (theme.vars || theme).shape.borderRadius, + borderTopRightRadius: (theme.vars || theme).shape.borderRadius, + }, + '&:last-of-type': { + borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius, + borderBottomRightRadius: (theme.vars || theme).shape.borderRadius, + }, + }), + }, + }, + MuiAccordionSummary: { + styleOverrides: { + root: ({ theme }) => ({ + border: 'none', + borderRadius: 8, + '&:hover': { backgroundColor: gray[50] }, + '&:focus-visible': { backgroundColor: 'transparent' }, + ...theme.applyStyles('dark', { + '&:hover': { backgroundColor: gray[800] }, + }), + }), + }, + }, + MuiAccordionDetails: { + styleOverrides: { + root: { mb: 20, border: 'none' }, + }, + }, + MuiPaper: { + defaultProps: { + elevation: 0, + }, + }, + MuiCard: { + styleOverrides: { + root: ({ theme }) => { + return { + padding: 16, + gap: 16, + transition: 'all 100ms ease', + backgroundColor: gray[50], + borderRadius: (theme.vars || theme).shape.borderRadius, + border: `1px solid ${(theme.vars || theme).palette.divider}`, + boxShadow: 'none', + ...theme.applyStyles('dark', { + backgroundColor: gray[800], + }), + variants: [ + { + props: { + variant: 'outlined', + }, + style: { + border: `1px solid ${(theme.vars || theme).palette.divider}`, + boxShadow: 'none', + background: 'hsl(0, 0%, 100%)', + ...theme.applyStyles('dark', { + background: alpha(gray[900], 0.4), + }), + }, + }, + ], + }; + }, + }, + }, + MuiCardContent: { + styleOverrides: { + root: { + padding: 0, + '&:last-child': { paddingBottom: 0 }, + }, + }, + }, + MuiCardHeader: { + styleOverrides: { + root: { + padding: 0, + }, + }, + }, + MuiCardActions: { + styleOverrides: { + root: { + padding: 0, + }, + }, + }, +}; diff --git a/frontend/src/theme/customizations/treeView.js b/frontend/src/theme/customizations/treeView.js new file mode 100644 index 00000000..88d3c57b --- /dev/null +++ b/frontend/src/theme/customizations/treeView.js @@ -0,0 +1,60 @@ +import { alpha } from '@mui/material/styles'; +import { gray, brand } from '../themePrimitives'; + +export const treeViewCustomizations = { + MuiTreeItem2: { + styleOverrides: { + root: ({ theme }) => ({ + position: 'relative', + boxSizing: 'border-box', + padding: theme.spacing(0, 1), + '& .groupTransition': { + marginLeft: theme.spacing(2), + padding: theme.spacing(0), + borderLeft: '1px solid', + borderColor: (theme.vars || theme).palette.divider, + }, + '&:focus-visible .focused': { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + '&:hover': { + backgroundColor: alpha(gray[300], 0.2), + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: '2px', + }, + }, + }), + content: ({ theme }) => ({ + marginTop: theme.spacing(1), + padding: theme.spacing(0.5, 1), + overflow: 'clip', + '&:hover': { + backgroundColor: alpha(gray[300], 0.2), + }, + + '&.selected': { + backgroundColor: alpha(gray[300], 0.4), + '&:hover': { + backgroundColor: alpha(gray[300], 0.6), + }, + }, + ...theme.applyStyles('dark', { + '&:hover': { + backgroundColor: alpha(gray[500], 0.2), + }, + '&:focus-visible': { + '&:hover': { + backgroundColor: alpha(gray[500], 0.2), + }, + }, + '&.selected': { + backgroundColor: alpha(gray[500], 0.4), + '&:hover': { + backgroundColor: alpha(gray[500], 0.6), + }, + }, + }), + }), + }, + }, +}; diff --git a/frontend/src/theme/themePrimitives.js b/frontend/src/theme/themePrimitives.js new file mode 100644 index 00000000..9aa79fe2 --- /dev/null +++ b/frontend/src/theme/themePrimitives.js @@ -0,0 +1,376 @@ +import { createTheme, alpha } from '@mui/material/styles'; + +const defaultTheme = createTheme(); + +const customShadows = [...defaultTheme.shadows]; + +export const brand = { + 50: 'hsl(210, 100%, 95%)', + 100: 'hsl(210, 100%, 92%)', + 200: 'hsl(210, 100%, 80%)', + 300: 'hsl(210, 100%, 65%)', + 400: 'hsl(210, 98%, 48%)', + 500: 'hsl(210, 98%, 42%)', + 600: 'hsl(210, 98%, 55%)', + 700: 'hsl(210, 100%, 35%)', + 800: 'hsl(210, 100%, 16%)', + 900: 'hsl(210, 100%, 21%)', +}; + +export const gray = { + 50: 'hsl(220, 35%, 97%)', + 100: 'hsl(220, 30%, 94%)', + 200: 'hsl(220, 20%, 88%)', + 300: 'hsl(220, 20%, 80%)', + 400: 'hsl(220, 20%, 65%)', + 500: 'hsl(220, 20%, 42%)', + 600: 'hsl(220, 20%, 35%)', + 700: 'hsl(220, 20%, 25%)', + 800: 'hsl(220, 30%, 6%)', + 900: 'hsl(220, 35%, 3%)', +}; + +export const green = { + 50: 'hsl(120, 80%, 98%)', + 100: 'hsl(120, 75%, 94%)', + 200: 'hsl(120, 75%, 87%)', + 300: 'hsl(120, 61%, 77%)', + 400: 'hsl(120, 44%, 53%)', + 500: 'hsl(120, 59%, 30%)', + 600: 'hsl(120, 70%, 25%)', + 700: 'hsl(120, 75%, 16%)', + 800: 'hsl(120, 84%, 10%)', + 900: 'hsl(120, 87%, 6%)', +}; + +export const orange = { + 50: 'hsl(45, 100%, 97%)', + 100: 'hsl(45, 92%, 90%)', + 200: 'hsl(45, 94%, 80%)', + 300: 'hsl(45, 90%, 65%)', + 400: 'hsl(45, 90%, 40%)', + 500: 'hsl(45, 90%, 35%)', + 600: 'hsl(45, 91%, 25%)', + 700: 'hsl(45, 94%, 20%)', + 800: 'hsl(45, 95%, 16%)', + 900: 'hsl(45, 93%, 12%)', +}; + +export const red = { + 50: 'hsl(0, 100%, 97%)', + 100: 'hsl(0, 92%, 90%)', + 200: 'hsl(0, 94%, 80%)', + 300: 'hsl(0, 90%, 65%)', + 400: 'hsl(0, 90%, 40%)', + 500: 'hsl(0, 90%, 30%)', + 600: 'hsl(0, 91%, 25%)', + 700: 'hsl(0, 94%, 18%)', + 800: 'hsl(0, 95%, 12%)', + 900: 'hsl(0, 93%, 6%)', +}; + +export const getDesignTokens = (mode) => { + customShadows[1] = + mode === 'dark' + ? 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px' + : 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px'; + + return { + palette: { + mode, + primary: { + light: brand[200], + main: brand[400], + dark: brand[700], + contrastText: brand[50], + ...(mode === 'dark' && { + contrastText: brand[50], + light: brand[300], + main: brand[400], + dark: brand[700], + }), + }, + info: { + light: brand[100], + main: brand[300], + dark: brand[600], + contrastText: gray[50], + ...(mode === 'dark' && { + contrastText: brand[300], + light: brand[500], + main: brand[700], + dark: brand[900], + }), + }, + warning: { + light: orange[300], + main: orange[400], + dark: orange[800], + ...(mode === 'dark' && { + light: orange[400], + main: orange[500], + dark: orange[700], + }), + }, + error: { + light: red[300], + main: red[400], + dark: red[800], + ...(mode === 'dark' && { + light: red[400], + main: red[500], + dark: red[700], + }), + }, + success: { + light: green[300], + main: green[400], + dark: green[800], + ...(mode === 'dark' && { + light: green[400], + main: green[500], + dark: green[700], + }), + }, + grey: { + ...gray, + }, + divider: mode === 'dark' ? alpha(gray[700], 0.6) : alpha(gray[300], 0.4), + background: { + default: 'hsl(0, 0%, 99%)', + paper: 'hsl(220, 35%, 97%)', + ...(mode === 'dark' && { default: gray[900], paper: 'hsl(220, 30%, 7%)' }), + }, + text: { + primary: gray[800], + secondary: gray[600], + warning: orange[400], + ...(mode === 'dark' && { primary: 'hsl(0, 0%, 100%)', secondary: gray[400] }), + }, + action: { + hover: alpha(gray[200], 0.2), + selected: `${alpha(gray[200], 0.3)}`, + ...(mode === 'dark' && { + hover: alpha(gray[600], 0.2), + selected: alpha(gray[600], 0.3), + }), + }, + }, + typography: { + fontFamily: 'Inter, sans-serif', + h1: { + fontSize: defaultTheme.typography.pxToRem(48), + fontWeight: 600, + lineHeight: 1.2, + letterSpacing: -0.5, + }, + h2: { + fontSize: defaultTheme.typography.pxToRem(36), + fontWeight: 600, + lineHeight: 1.2, + }, + h3: { + fontSize: defaultTheme.typography.pxToRem(30), + lineHeight: 1.2, + }, + h4: { + fontSize: defaultTheme.typography.pxToRem(24), + fontWeight: 600, + lineHeight: 1.5, + }, + h5: { + fontSize: defaultTheme.typography.pxToRem(20), + fontWeight: 600, + }, + h6: { + fontSize: defaultTheme.typography.pxToRem(18), + fontWeight: 600, + }, + subtitle1: { + fontSize: defaultTheme.typography.pxToRem(18), + }, + subtitle2: { + fontSize: defaultTheme.typography.pxToRem(14), + fontWeight: 500, + }, + body1: { + fontSize: defaultTheme.typography.pxToRem(14), + }, + body2: { + fontSize: defaultTheme.typography.pxToRem(14), + fontWeight: 400, + }, + caption: { + fontSize: defaultTheme.typography.pxToRem(12), + fontWeight: 400, + }, + }, + shape: { + borderRadius: 8, + }, + shadows: customShadows, + }; +}; + +export const colorSchemes = { + light: { + palette: { + primary: { + light: brand[200], + main: brand[400], + dark: brand[700], + contrastText: brand[50], + }, + info: { + light: brand[100], + main: brand[300], + dark: brand[600], + contrastText: gray[50], + }, + warning: { + light: orange[300], + main: orange[400], + dark: orange[800], + }, + error: { + light: red[300], + main: red[400], + dark: red[800], + }, + success: { + light: green[300], + main: green[400], + dark: green[800], + }, + grey: { + ...gray, + }, + divider: alpha(gray[300], 0.4), + background: { + default: 'hsl(0, 0%, 99%)', + paper: 'hsl(220, 35%, 97%)', + }, + text: { + primary: gray[800], + secondary: gray[600], + warning: orange[400], + }, + action: { + hover: alpha(gray[200], 0.2), + selected: `${alpha(gray[200], 0.3)}`, + }, + baseShadow: + 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px', + }, + }, + dark: { + palette: { + primary: { + contrastText: brand[50], + light: brand[300], + main: brand[400], + dark: brand[700], + }, + info: { + contrastText: brand[300], + light: brand[500], + main: brand[700], + dark: brand[900], + }, + warning: { + light: orange[400], + main: orange[500], + dark: orange[700], + }, + error: { + light: red[400], + main: red[500], + dark: red[700], + }, + success: { + light: green[400], + main: green[500], + dark: green[700], + }, + grey: { + ...gray, + }, + divider: alpha(gray[700], 0.6), + background: { + default: gray[900], + paper: 'hsl(220, 30%, 7%)', + }, + text: { + primary: 'hsl(0, 0%, 100%)', + secondary: gray[400], + }, + action: { + hover: alpha(gray[600], 0.2), + selected: alpha(gray[600], 0.3), + }, + baseShadow: + 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px', + }, + }, +}; + +export const typography = { + fontFamily: 'Inter, sans-serif', + h1: { + fontSize: defaultTheme.typography.pxToRem(48), + fontWeight: 600, + lineHeight: 1.2, + letterSpacing: -0.5, + }, + h2: { + fontSize: defaultTheme.typography.pxToRem(36), + fontWeight: 600, + lineHeight: 1.2, + }, + h3: { + fontSize: defaultTheme.typography.pxToRem(30), + lineHeight: 1.2, + }, + h4: { + fontSize: defaultTheme.typography.pxToRem(24), + fontWeight: 600, + lineHeight: 1.5, + }, + h5: { + fontSize: defaultTheme.typography.pxToRem(20), + fontWeight: 600, + }, + h6: { + fontSize: defaultTheme.typography.pxToRem(18), + fontWeight: 600, + }, + subtitle1: { + fontSize: defaultTheme.typography.pxToRem(18), + }, + subtitle2: { + fontSize: defaultTheme.typography.pxToRem(14), + fontWeight: 500, + }, + body1: { + fontSize: defaultTheme.typography.pxToRem(14), + }, + body2: { + fontSize: defaultTheme.typography.pxToRem(14), + fontWeight: 400, + }, + caption: { + fontSize: defaultTheme.typography.pxToRem(12), + fontWeight: 400, + }, +}; + +export const shape = { + borderRadius: 8, +}; + +const defaultShadows = [ + 'none', + 'var(--template-palette-baseShadow)', + ...defaultTheme.shadows.slice(2), +]; +export const shadows = defaultShadows; diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js deleted file mode 100644 index ee174650..00000000 --- a/frontend/src/utils/api.js +++ /dev/null @@ -1,26 +0,0 @@ -export async function sendChatMessage(message) { - try { - const response = await fetch('https://api.gemini.com/v1/chat', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${import.meta.env.VITE_GEMINI_API_KEY}` - }, - body: JSON.stringify({ - // Adjust the payload according to the Gemini API documentation. - query: message, - // Optionally include additional parameters - }) - }) - - if (!response.ok) { - throw new Error(`API error: ${response.status}`) - } - const data = await response.json() - // Assuming the response has a property 'reply' - return data.reply || 'No reply from API.' - } catch (error) { - console.error('Error calling Gemini API:', error) - throw error - } -}