feat: initialize frontend project structure with Material UI and DataGrid implementation
This commit is contained in:
@@ -5,9 +5,6 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Mariavel - Premium Database Manager</title>
|
<title>Mariavel - Premium Database Manager</title>
|
||||||
<!-- DevExtreme CDN -->
|
|
||||||
<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.1.5/css/dx.light.css">
|
|
||||||
<script type="text/javascript" src="https://cdn3.devexpress.com/jslib/23.1.5/js/dx.all.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
Generated
+98
@@ -13,6 +13,7 @@
|
|||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@mui/icons-material": "^9.0.0",
|
"@mui/icons-material": "^9.0.0",
|
||||||
"@mui/material": "^9.0.0",
|
"@mui/material": "^9.0.0",
|
||||||
|
"@mui/x-data-grid": "^9.0.2",
|
||||||
"axios": "^1.15.2",
|
"axios": "^1.15.2",
|
||||||
"devextreme": "^25.2.6",
|
"devextreme": "^25.2.6",
|
||||||
"devextreme-react": "^25.2.6",
|
"devextreme-react": "^25.2.6",
|
||||||
@@ -990,6 +991,88 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-data-grid": {
|
||||||
|
"version": "9.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-9.0.2.tgz",
|
||||||
|
"integrity": "sha512-9hkBS73x3G5MniOpkCh54iH5iwBr55obchF5IS1eybURZEgPxSXFizNMrDbyM2EGaYG9DQ87MvC5IoV7g0F2Vw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"@mui/utils": "9.0.0",
|
||||||
|
"@mui/x-internals": "^9.0.0",
|
||||||
|
"@mui/x-virtualizer": "9.0.0-alpha.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"use-sync-external-store": "^1.6.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": "^7.3.0 || ^9.0.0",
|
||||||
|
"@mui/system": "^7.3.0 || ^9.0.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/x-internals": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-E/4rdg69JjhyybpPGypCjAKSKLLnSdCFM+O6P/nkUg47+qt3uftxQEhjQO53rcn6ahHl6du/uNZ9BLgeY6kYxQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"@mui/utils": "9.0.0",
|
||||||
|
"reselect": "^5.1.1",
|
||||||
|
"use-sync-external-store": "^1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/x-virtualizer": {
|
||||||
|
"version": "9.0.0-alpha.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-virtualizer/-/x-virtualizer-9.0.0-alpha.0.tgz",
|
||||||
|
"integrity": "sha512-K52TKCuWlkMEWOeB2nPfhIAHaWsYEb9h1ME9Wb+gmw4FloMA03VvKsrqvn8o6l8hYUi4/5F8NfYOIfPwqW3EhA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"@mui/utils": "9.0.0",
|
||||||
|
"@mui/x-internals": "^9.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
||||||
@@ -3712,6 +3795,12 @@
|
|||||||
"util-deprecate": "~1.0.1"
|
"util-deprecate": "~1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.12",
|
"version": "1.22.12",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
|
||||||
@@ -4050,6 +4139,15 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@mui/icons-material": "^9.0.0",
|
"@mui/icons-material": "^9.0.0",
|
||||||
"@mui/material": "^9.0.0",
|
"@mui/material": "^9.0.0",
|
||||||
|
"@mui/x-data-grid": "^9.0.2",
|
||||||
"axios": "^1.15.2",
|
"axios": "^1.15.2",
|
||||||
"devextreme": "^25.2.6",
|
"devextreme": "^25.2.6",
|
||||||
"devextreme-react": "^25.2.6",
|
"devextreme-react": "^25.2.6",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const Header: React.FC = () => {
|
|||||||
borderBottom: 1,
|
borderBottom: 1,
|
||||||
borderColor: 'divider',
|
borderColor: 'divider',
|
||||||
bgcolor: darkMode ? 'rgba(16, 24, 48, 0.95)' : 'rgba(255, 255, 255, 0.95)',
|
bgcolor: darkMode ? 'rgba(16, 24, 48, 0.95)' : 'rgba(255, 255, 255, 0.95)',
|
||||||
|
color: darkMode ? '#f1f5f9' : '#1e293b',
|
||||||
backdropFilter: 'blur(10px)',
|
backdropFilter: 'blur(10px)',
|
||||||
zIndex: (theme) => theme.zIndex.drawer + 1
|
zIndex: (theme) => theme.zIndex.drawer + 1
|
||||||
}}
|
}}
|
||||||
@@ -23,10 +24,10 @@ const Header: React.FC = () => {
|
|||||||
MARIAVEL
|
MARIAVEL
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<IconButton onClick={toggleDarkMode} color="inherit">
|
<IconButton onClick={toggleDarkMode} color="inherit" sx={{ opacity: 0.8, '&:hover': { opacity: 1 } }}>
|
||||||
{darkMode ? <Brightness7 /> : <Brightness4 />}
|
{darkMode ? <Brightness7 /> : <Brightness4 />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton color="inherit">
|
<IconButton color="inherit" sx={{ opacity: 0.8, '&:hover': { opacity: 1 } }}>
|
||||||
<Settings />
|
<Settings />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,53 +1,33 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react';
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
import { Box, Paper, Typography, CircularProgress } from '@mui/material';
|
import { Box, Paper, Typography, CircularProgress } from '@mui/material';
|
||||||
import DataGrid, {
|
import { DataGrid } from '@mui/x-data-grid';
|
||||||
Column,
|
import type { GridColDef, GridPaginationModel } from '@mui/x-data-grid';
|
||||||
Scrolling,
|
|
||||||
FilterRow,
|
|
||||||
HeaderFilter,
|
|
||||||
SearchPanel,
|
|
||||||
GroupPanel,
|
|
||||||
Export
|
|
||||||
} from 'devextreme-react/data-grid';
|
|
||||||
import { useAppStore } from '../store/useAppStore';
|
import { useAppStore } from '../store/useAppStore';
|
||||||
import api, { SchemaService } from '../services/api';
|
import { SchemaService } from '../services/api';
|
||||||
|
|
||||||
import CustomStore from 'devextreme/data/custom_store';
|
|
||||||
|
|
||||||
const MainContent: React.FC = () => {
|
const MainContent: React.FC = () => {
|
||||||
const { activeTable, activeDatabase } = useAppStore();
|
const { activeTable, activeDatabase } = useAppStore();
|
||||||
const [columns, setColumns] = useState<any[]>([]);
|
const [columns, setColumns] = useState<GridColDef[]>([]);
|
||||||
|
const [rows, setRows] = useState<any[]>([]);
|
||||||
|
const [rowCount, setRowCount] = useState(0);
|
||||||
const [loadingSchema, setLoadingSchema] = useState(false);
|
const [loadingSchema, setLoadingSchema] = useState(false);
|
||||||
|
const [loadingData, setLoadingData] = useState(false);
|
||||||
// Define data source with CustomStore for Remote Operations
|
const [paginationModel, setPaginationModel] = useState<GridPaginationModel>({
|
||||||
const dataSource = useMemo(() => {
|
page: 0,
|
||||||
if (!activeTable || !activeDatabase) return null;
|
pageSize: 100,
|
||||||
|
|
||||||
return new CustomStore({
|
|
||||||
key: 'id', // Ideally this should be the primary key from schema
|
|
||||||
load: async (loadOptions: any) => {
|
|
||||||
try {
|
|
||||||
const params = {
|
|
||||||
skip: loadOptions.skip || 0,
|
|
||||||
take: loadOptions.take || 100,
|
|
||||||
requireTotalCount: loadOptions.requireTotalCount,
|
|
||||||
database: activeDatabase,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await SchemaService.getTableData(activeTable, params);
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: response.data.data,
|
|
||||||
totalCount: response.data.totalCount,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Data loading error', error);
|
|
||||||
throw 'Data Loading Error';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, [activeTable, activeDatabase]);
|
|
||||||
|
|
||||||
|
// Helper to map SQL types to MUI X Data Grid types
|
||||||
|
const mapSqlTypeToMuiType = (sqlType: string): 'string' | 'number' | 'date' | 'dateTime' | 'boolean' => {
|
||||||
|
sqlType = sqlType.toLowerCase();
|
||||||
|
if (sqlType.includes('int') || sqlType.includes('decimal') || sqlType.includes('float') || sqlType.includes('double')) return 'number';
|
||||||
|
if (sqlType.includes('datetime') || sqlType.includes('timestamp')) return 'dateTime';
|
||||||
|
if (sqlType.includes('date')) return 'date';
|
||||||
|
if (sqlType.includes('bool') || sqlType.includes('tinyint(1)')) return 'boolean';
|
||||||
|
return 'string';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch Schema
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchSchema = async () => {
|
const fetchSchema = async () => {
|
||||||
if (!activeTable || !activeDatabase) return;
|
if (!activeTable || !activeDatabase) return;
|
||||||
@@ -55,12 +35,26 @@ const MainContent: React.FC = () => {
|
|||||||
setLoadingSchema(true);
|
setLoadingSchema(true);
|
||||||
try {
|
try {
|
||||||
const schemaRes = await SchemaService.getTableSchema(activeTable, activeDatabase);
|
const schemaRes = await SchemaService.getTableSchema(activeTable, activeDatabase);
|
||||||
const cols = schemaRes.data.map((col: any) => ({
|
const cols: GridColDef[] = schemaRes.data.map((col: any) => {
|
||||||
dataField: col.Field,
|
const type = mapSqlTypeToMuiType(col.Type);
|
||||||
caption: col.Field,
|
return {
|
||||||
dataType: mapSqlTypeToDxType(col.Type)
|
field: col.Field,
|
||||||
}));
|
headerName: col.Field,
|
||||||
|
type: type,
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 150,
|
||||||
|
valueGetter: (value: any) => {
|
||||||
|
if ((type === 'date' || type === 'dateTime') && value && typeof value === 'string') {
|
||||||
|
return new Date(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
setColumns(cols);
|
setColumns(cols);
|
||||||
|
|
||||||
|
// Reset pagination when table changes
|
||||||
|
setPaginationModel({ page: 0, pageSize: 100 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch table schema', error);
|
console.error('Failed to fetch table schema', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -71,15 +65,41 @@ const MainContent: React.FC = () => {
|
|||||||
fetchSchema();
|
fetchSchema();
|
||||||
}, [activeTable, activeDatabase]);
|
}, [activeTable, activeDatabase]);
|
||||||
|
|
||||||
// Helper to map SQL types to DevExtreme types
|
// Fetch Data
|
||||||
const mapSqlTypeToDxType = (sqlType: string) => {
|
const fetchData = useCallback(async () => {
|
||||||
sqlType = sqlType.toLowerCase();
|
if (!activeTable || !activeDatabase) return;
|
||||||
if (sqlType.includes('int') || sqlType.includes('decimal') || sqlType.includes('float')) return 'number';
|
|
||||||
if (sqlType.includes('date') || sqlType.includes('time')) return 'date';
|
setLoadingData(true);
|
||||||
if (sqlType.includes('bool')) return 'boolean';
|
try {
|
||||||
return 'string';
|
const params = {
|
||||||
|
skip: paginationModel.page * paginationModel.pageSize,
|
||||||
|
take: paginationModel.pageSize,
|
||||||
|
requireTotalCount: true,
|
||||||
|
database: activeDatabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const response = await SchemaService.getTableData(activeTable, params);
|
||||||
|
|
||||||
|
// MUI X Data Grid needs a unique id for each row
|
||||||
|
// If the table doesn't have an 'id' column, we might need to generate one
|
||||||
|
const dataWithIds = response.data.data.map((row: any, index: number) => ({
|
||||||
|
id: row.id || row.ID || `row-${index}-${paginationModel.page}`,
|
||||||
|
...row,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setRows(dataWithIds);
|
||||||
|
setRowCount(response.data.totalCount || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Data loading error', error);
|
||||||
|
} finally {
|
||||||
|
setLoadingData(false);
|
||||||
|
}
|
||||||
|
}, [activeTable, activeDatabase, paginationModel]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [fetchData]);
|
||||||
|
|
||||||
if (!activeTable) {
|
if (!activeTable) {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ flexGrow: 1, p: 3, display: 'flex', alignItems: 'center', justifyContent: 'center', bgcolor: 'background.default' }}>
|
<Box sx={{ flexGrow: 1, p: 3, display: 'flex', alignItems: 'center', justifyContent: 'center', bgcolor: 'background.default' }}>
|
||||||
@@ -89,40 +109,45 @@ const MainContent: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ flexGrow: 1, p: 3, bgcolor: 'background.default', overflow: 'hidden' }}>
|
<Box sx={{ flexGrow: 1, p: 3, bgcolor: 'background.default', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Box sx={{ mb: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
<Box sx={{ mb: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<Typography variant="h5" sx={{ fontWeight: 700 }}>
|
<Typography variant="h5" sx={{ fontWeight: 700 }}>
|
||||||
{activeDatabase}.{activeTable}
|
{activeDatabase}.{activeTable}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Paper sx={{ height: 'calc(100vh - 180px)', borderRadius: 2, overflow: 'hidden' }}>
|
<Paper sx={{ flexGrow: 1, borderRadius: 2, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
||||||
{loadingSchema ? (
|
{loadingSchema ? (
|
||||||
<Box sx={{ display: 'flex', height: '100%', alignItems: 'center', justifyContent: 'center' }}>
|
<Box sx={{ display: 'flex', height: '100%', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<DataGrid
|
<DataGrid
|
||||||
dataSource={dataSource}
|
rows={rows}
|
||||||
remoteOperations={true}
|
columns={columns}
|
||||||
showBorders={true}
|
rowCount={rowCount}
|
||||||
focusedRowEnabled={true}
|
loading={loadingData}
|
||||||
columnAutoWidth={true}
|
paginationMode="server"
|
||||||
allowColumnReordering={true}
|
paginationModel={paginationModel}
|
||||||
rowAlternationEnabled={true}
|
onPaginationModelChange={setPaginationModel}
|
||||||
height="100%"
|
pageSizeOptions={[25, 50, 100]}
|
||||||
>
|
sx={{
|
||||||
<Scrolling mode="virtual" rowRenderingMode="virtual" />
|
border: 'none',
|
||||||
<FilterRow visible={true} />
|
'& .MuiDataGrid-cell:focus': {
|
||||||
<HeaderFilter visible={true} />
|
outline: 'none',
|
||||||
<SearchPanel visible={true} width={240} placeholder="Search..." />
|
},
|
||||||
<GroupPanel visible={true} />
|
'& .MuiDataGrid-columnHeader:focus': {
|
||||||
<Export enabled={true} allowExportSelectedData={true} />
|
outline: 'none',
|
||||||
|
},
|
||||||
{columns.map(col => (
|
height: '100%',
|
||||||
<Column key={col.dataField} {...col} />
|
}}
|
||||||
))}
|
slotProps={{
|
||||||
</DataGrid>
|
loadingOverlay: {
|
||||||
|
variant: 'linear-progress',
|
||||||
|
noRowsVariant: 'linear-progress',
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
+1
-73
@@ -13,15 +13,7 @@
|
|||||||
--text-dark: #f1f5f9;
|
--text-dark: #f1f5f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Suppress DevExtreme License Watermark */
|
/* Global Styles */
|
||||||
dx-license, [class*="dx-license"] {
|
|
||||||
display: none !important;
|
|
||||||
visibility: hidden !important;
|
|
||||||
height: 0 !important;
|
|
||||||
opacity: 0 !important;
|
|
||||||
pointer-events: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -55,70 +47,6 @@ body.dark {
|
|||||||
border: 1px solid var(--glass-border-dark);
|
border: 1px solid var(--glass-border-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DevExtreme Custom Styling - Modern Dark Theme Integration */
|
|
||||||
.dx-datagrid {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border-radius: 12px !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
border: none !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .dx-datagrid {
|
|
||||||
color: var(--text-dark) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dx-datagrid-headers {
|
|
||||||
background-color: rgba(255, 255, 255, 0.05) !important;
|
|
||||||
border-bottom: 1px solid var(--glass-border) !important;
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .dx-datagrid-headers {
|
|
||||||
background-color: rgba(0, 0, 0, 0.2) !important;
|
|
||||||
border-bottom: 1px solid var(--glass-border-dark) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dx-datagrid-rowsview .dx-row {
|
|
||||||
background-color: transparent !important;
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .dx-datagrid-rowsview .dx-row {
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dx-datagrid-content .dx-datagrid-table .dx-row > td {
|
|
||||||
padding: 12px 16px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fix for Search and Group Panels in Dark Mode */
|
|
||||||
.dark .dx-datagrid-search-panel,
|
|
||||||
.dark .dx-datagrid-group-panel,
|
|
||||||
.dark .dx-datagrid-filter-row {
|
|
||||||
background-color: rgba(0, 0, 0, 0.2) !important;
|
|
||||||
color: var(--text-dark) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .dx-textbox-input {
|
|
||||||
color: var(--text-dark) !important;
|
|
||||||
background-color: rgba(255, 255, 255, 0.05) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Column Header text */
|
|
||||||
.dark .dx-datagrid-text-content {
|
|
||||||
color: rgba(255, 255, 255, 0.7) !important;
|
|
||||||
font-weight: 600 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Selection and Hover */
|
|
||||||
.dark .dx-data-row.dx-state-hover:not(.dx-selection):not(.dx-row-inserted):not(.dx-row-removed):not(.dx-edit-row) > td {
|
|
||||||
background-color: rgba(255, 255, 255, 0.05) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .dx-selection.dx-row > td {
|
|
||||||
background-color: rgba(0, 97, 255, 0.2) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbar Styling */
|
/* Scrollbar Styling */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
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'
|
import App from './App.tsx'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
|||||||
Reference in New Issue
Block a user