From 49cf557f41c773272ec4f9cde5c71b45e39a8de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Fri, 24 Apr 2026 07:14:41 +0300 Subject: [PATCH] feat: initialize Mariavel project with Laravel backend API and React dashboard using DevExtreme DataGrid --- .../Http/Controllers/Api/SchemaController.php | 63 ++++++++ backend/routes/api.php | 7 + frontend/src/App.tsx | 149 ++++-------------- frontend/src/components/Header.tsx | 28 ++++ frontend/src/components/MainContent.tsx | 62 ++++++++ frontend/src/components/Sidebar.tsx | 44 ++++++ frontend/src/main.tsx | 2 + 7 files changed, 237 insertions(+), 118 deletions(-) create mode 100644 backend/app/Http/Controllers/Api/SchemaController.php create mode 100644 frontend/src/components/Header.tsx create mode 100644 frontend/src/components/MainContent.tsx create mode 100644 frontend/src/components/Sidebar.tsx diff --git a/backend/app/Http/Controllers/Api/SchemaController.php b/backend/app/Http/Controllers/Api/SchemaController.php new file mode 100644 index 0000000..d67eef2 --- /dev/null +++ b/backend/app/Http/Controllers/Api/SchemaController.php @@ -0,0 +1,63 @@ +databaseService = $databaseService; + } + + protected function initializeDriver(Request $request) + { + // In a real app, these would come from encrypted session or token + $config = $request->only(['host', 'username', 'password', 'database', 'port']); + $driver = new MySqlDriver(); + + if (!$driver->connect($config)) { + throw new \Exception("Could not connect to database."); + } + + $this->databaseService->setDriver($driver); + } + + public function databases(Request $request) + { + try { + $this->initializeDriver($request); + return Response::json($this->databaseService->getDatabases()); + } catch (\Exception $e) { + return Response::json(['error' => $e->getMessage()], 400); + } + } + + public function tables(Request $request, $database) + { + try { + $request->merge(['database' => $database]); + $this->initializeDriver($request); + return Response::json($this->databaseService->getTables()); + } catch (\Exception $e) { + return Response::json(['error' => $e->getMessage()], 400); + } + } + + public function schema(Request $request, $table) + { + try { + $this->initializeDriver($request); + return Response::json($this->databaseService->getTableSchema($table)); + } catch (\Exception $e) { + return Response::json(['error' => $e->getMessage()], 400); + } + } +} diff --git a/backend/routes/api.php b/backend/routes/api.php index ccc387f..d1c00a0 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -2,7 +2,14 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; +use App\Http\Controllers\Api\SchemaController; Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:sanctum'); + +Route::prefix('schema')->group(function () { + Route::get('/databases', [SchemaController::class, 'databases']); + Route::get('/tables/{database}', [SchemaController::class, 'tables']); + Route::get('/{table}', [SchemaController::class, 'schema']); +}); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a66b5ef..1e2559f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,122 +1,35 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import heroImg from './assets/hero.png' -import './App.css' +import React, { useMemo, useEffect } from 'react'; +import { ThemeProvider, CssBaseline, Box } from '@mui/material'; +import { getTheme } from './theme/theme'; +import { useAppStore } from './store/useAppStore'; +import Sidebar from './components/Sidebar'; +import MainContent from './components/MainContent'; +import Header from './components/Header'; -function App() { - const [count, setCount] = useState(0) +const App: React.FC = () => { + const darkMode = useAppStore((state) => state.darkMode); + const theme = useMemo(() => getTheme(darkMode ? 'dark' : 'light'), [darkMode]); + + useEffect(() => { + if (darkMode) { + document.body.classList.add('dark'); + } else { + document.body.classList.remove('dark'); + } + }, [darkMode]); return ( - <> -
-
- - React logo - Vite logo -
-
-

Get started

-

- Edit src/App.tsx and save to test HMR -

-
- -
+ + + + + +
+ + + + + ); +}; -
- -
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
- - ) -} - -export default App +export default App; diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx new file mode 100644 index 0000000..00c681b --- /dev/null +++ b/frontend/src/components/Header.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { AppBar, Toolbar, Typography, IconButton, Box } from '@mui/material'; +import { Brightness4, Brightness7, Settings } from '@mui/icons-material'; +import { useAppStore } from '../store/useAppStore'; + +const Header: React.FC = () => { + const { darkMode, toggleDarkMode } = useAppStore(); + + return ( + + + + MARIAVEL + + + + {darkMode ? : } + + + + + + + + ); +}; + +export default Header; diff --git a/frontend/src/components/MainContent.tsx b/frontend/src/components/MainContent.tsx new file mode 100644 index 0000000..47caae8 --- /dev/null +++ b/frontend/src/components/MainContent.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { Box, Paper, Typography } from '@mui/material'; +import DataGrid, { + Column, + Editing, + Scrolling, + Paging, + FilterRow, + HeaderFilter +} from 'devextreme-react/data-grid'; +import { useAppStore } from '../store/useAppStore'; + +const MainContent: React.FC = () => { + const { activeDatabase } = useAppStore(); + + // Mock data for initial UI + const dummyData = [ + { id: 1, name: 'Users', type: 'BASE TABLE', engine: 'InnoDB', rows: 1500, size: '256 KB' }, + { id: 2, name: 'Products', type: 'BASE TABLE', engine: 'InnoDB', rows: 54200, size: '12 MB' }, + { id: 3, name: 'Orders', type: 'BASE TABLE', engine: 'InnoDB', rows: 120400, size: '45 MB' }, + { id: 4, name: 'Order_Items', type: 'BASE TABLE', engine: 'InnoDB', rows: 450000, size: '120 MB' }, + ]; + + return ( + + + + {activeDatabase ? `Tables in ${activeDatabase}` : 'Welcome to Mariavel'} + + + Manage your database tables with high-performance tools. + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default MainContent; diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx new file mode 100644 index 0000000..40b8ace --- /dev/null +++ b/frontend/src/components/Sidebar.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Box, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography, Divider } from '@mui/material'; +import { Storage, TableChart, Folder } from '@mui/icons-material'; +import { useAppStore } from '../store/useAppStore'; + +const Sidebar: React.FC = () => { + const { activeDatabase, setActiveDatabase } = useAppStore(); + + // Mock data for initial UI + const databases = ['information_schema', 'mysql', 'performance_schema', 'sys', 'mariavel_db']; + + return ( + + + + Databases + + + + {databases.map((db) => ( + + setActiveDatabase(db)} + > + + + + + + + ))} + + + + + Connected to: 127.0.0.1 + + + + ); +}; + +export default Sidebar; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index bef5202..63bb10c 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,6 +1,8 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' +import 'devextreme/dist/css/dx.common.css'; +import 'devextreme/dist/css/dx.material.blue.dark.compact.css'; import App from './App.tsx' createRoot(document.getElementById('root')!).render(