UI WIP
This commit is contained in:
parent
04bfa594cd
commit
843acfaa89
@ -28,15 +28,6 @@ function App() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const testButton = () => {
|
|
||||||
client?.publish({
|
|
||||||
"destination": "/app/items",
|
|
||||||
"body": "Potato"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Kjør din funksjon her når komponenten lastes inn for første gang
|
// Kjør din funksjon her når komponenten lastes inn for første gang
|
||||||
// Sjekk om cursor er null
|
// Sjekk om cursor er null
|
||||||
@ -56,16 +47,26 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<BrowserRouter>
|
<Box sx={{
|
||||||
<Box sx={{ marginTop: "70px", minHeight: "50vh", width: "100%", display: "block", overflow: "hidden" }}>
|
height: 70,
|
||||||
<button onClick={testButton}>Click me</button>
|
backgroundColor: theme.palette.action.selected
|
||||||
<Routes>
|
}}>
|
||||||
<Route path='/files' element={<ExplorePage />} />
|
</Box>
|
||||||
<Route path='/' element={<LaunchPage />} />
|
<Box sx={{
|
||||||
</Routes>
|
display: "block",
|
||||||
</Box>
|
maxHeight: window.screen.height - 70,
|
||||||
<Footer />
|
height: window.screen.height - 70,
|
||||||
</BrowserRouter>
|
width: "100vw",
|
||||||
|
maxWidth: "100vw"
|
||||||
|
}}>
|
||||||
|
<BrowserRouter>
|
||||||
|
<Routes>
|
||||||
|
<Route path='/files' element={<ExplorePage />} />
|
||||||
|
<Route path='/' element={<LaunchPage />} />
|
||||||
|
</Routes>
|
||||||
|
<Footer />
|
||||||
|
</BrowserRouter>
|
||||||
|
</Box>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
22
UI/web/src/app/features/UxTc.tsx
Normal file
22
UI/web/src/app/features/UxTc.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Typography } from "@mui/material";
|
||||||
|
|
||||||
|
|
||||||
|
export function UnixTimestamp({ timestamp }: { timestamp?: number }) {
|
||||||
|
if (!timestamp) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date(timestamp);
|
||||||
|
const day = date.getDate();
|
||||||
|
const month = date.toLocaleString('default', { month: 'short' });
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const hours = date.getHours().toString().padStart(2, '0');
|
||||||
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography variant='body1'>
|
||||||
|
{day}.{month}.{year} {hours}.{minutes}
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
133
UI/web/src/app/features/table.tsx
Normal file
133
UI/web/src/app/features/table.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../store";
|
||||||
|
import IconArrowUp from '@mui/icons-material/ArrowUpward';
|
||||||
|
import IconArrowDown from '@mui/icons-material/ArrowDownward';
|
||||||
|
import { Table, TableHead, TableRow, TableCell, TableBody, Typography, Box, useTheme, TableContainer } from "@mui/material";
|
||||||
|
|
||||||
|
export interface TablePropetyConfig {
|
||||||
|
label: string
|
||||||
|
accessor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableCellCustomizer<T> {
|
||||||
|
(accessor: string, data: T): JSX.Element | null
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullableTableRowActionEvents<T> = TableRowActionEvents<T> | null;
|
||||||
|
export interface TableRowActionEvents<T> {
|
||||||
|
click: (row: T) => void;
|
||||||
|
doubleClick: (row: T) => void;
|
||||||
|
contextMenu: (row: T) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function SimpleTable<T>({ items, columns, customizer, onRowClickedEvent }: { items: Array<T>, columns: Array<TablePropetyConfig>, customizer?: TableCellCustomizer<T>, onRowClickedEvent?: TableRowActionEvents<T> }) {
|
||||||
|
const muiTheme = useTheme();
|
||||||
|
|
||||||
|
const [contextMenuVisible, setContextMenuVisible] = useState(false);
|
||||||
|
const [contextMenuPosition, setContextMenuPosition] = useState({ top: 0, left: 0 });
|
||||||
|
|
||||||
|
const [order, setOrder] = useState<'asc' | 'desc'>('asc');
|
||||||
|
const [orderBy, setOrderBy] = useState<string>('');
|
||||||
|
const [selectedRow, setSelectedRow] = useState<T | null>(null);
|
||||||
|
|
||||||
|
const tableRowSingleClicked = (row: T | null) => {
|
||||||
|
setSelectedRow(row);
|
||||||
|
if (row && onRowClickedEvent) {
|
||||||
|
onRowClickedEvent.click(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tableRowDoubleClicked = (row: T | null) => {
|
||||||
|
setSelectedRow(row);
|
||||||
|
if (row && onRowClickedEvent) {
|
||||||
|
onRowClickedEvent.doubleClick(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSort = (property: string) => {
|
||||||
|
const isAsc = orderBy === property && order === 'asc';
|
||||||
|
setOrder(isAsc ? 'desc' : 'asc');
|
||||||
|
setOrderBy(property);
|
||||||
|
};
|
||||||
|
|
||||||
|
const compareValues = (a: any, b: any, orderBy: string) => {
|
||||||
|
if (typeof a[orderBy] === 'string') {
|
||||||
|
return a[orderBy].localeCompare(b[orderBy]);
|
||||||
|
} else if (typeof a[orderBy] === 'number') {
|
||||||
|
return a[orderBy] - b[orderBy];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedData = items.slice().sort((a, b) => {
|
||||||
|
if (order === 'asc') {
|
||||||
|
return compareValues(a, b, orderBy);
|
||||||
|
} else {
|
||||||
|
return compareValues(b, a, orderBy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleSort(columns[0].accessor)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column", // Bruk column-fleksretning
|
||||||
|
height: "100%",
|
||||||
|
overflow: "hidden"
|
||||||
|
}}>
|
||||||
|
<TableContainer sx={{
|
||||||
|
flex: 1,
|
||||||
|
overflowY: "auto",
|
||||||
|
position: "relative", // Legg til denne linjen for å justere layout
|
||||||
|
maxHeight: "100%" // Legg til denne linjen for å begrense høyden
|
||||||
|
}}>
|
||||||
|
<Table>
|
||||||
|
<TableHead sx={{
|
||||||
|
position: "sticky",
|
||||||
|
top: 0,
|
||||||
|
backgroundColor: muiTheme.palette.background.paper,
|
||||||
|
}}>
|
||||||
|
<TableRow>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<TableCell key={column.accessor} onClick={() => handleSort(column.accessor)} sx={{ cursor: "pointer" }}>
|
||||||
|
<Box display="flex">
|
||||||
|
{orderBy === column.accessor ?
|
||||||
|
(order === "asc" ? (<IconArrowDown />) : (<IconArrowUp />)) : (
|
||||||
|
<IconArrowDown sx={{ color: "transparent" }} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Typography>{column.label}</Typography>
|
||||||
|
</Box>
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody sx={{
|
||||||
|
overflowY: "scroll"
|
||||||
|
}}>
|
||||||
|
{sortedData?.map((row: T, rowIndex: number) => (
|
||||||
|
<TableRow key={rowIndex}
|
||||||
|
onClick={() => tableRowSingleClicked(row)}
|
||||||
|
onDoubleClick={() => tableRowDoubleClicked(row)}
|
||||||
|
style={{ cursor: "pointer", backgroundColor: selectedRow === row ? muiTheme.palette.action.selected : '' }}
|
||||||
|
>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<TableCell key={column.accessor}>
|
||||||
|
{customizer && customizer(column.accessor, row) !== null
|
||||||
|
? customizer(column.accessor, row)
|
||||||
|
: <Typography variant='body1'>{(row as any)[column.accessor]}</Typography>}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
180
UI/web/src/app/page/ExplorePage.tsx
Normal file
180
UI/web/src/app/page/ExplorePage.tsx
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { UnixTimestamp } from '../features/UxTc';
|
||||||
|
import { Box, Button, Typography, useTheme } from '@mui/material';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { RootState } from '../store';
|
||||||
|
import SimpleTable, { TableCellCustomizer, TablePropetyConfig, TableRowActionEvents } from '../features/table';
|
||||||
|
import { useStompClient } from 'react-stomp-hooks';
|
||||||
|
import { useWsSubscription } from '../ws/subscriptions';
|
||||||
|
import { updateItems } from '../store/explorer-slice';
|
||||||
|
import FolderIcon from '@mui/icons-material/Folder';
|
||||||
|
import IconForward from '@mui/icons-material/ArrowForwardIosRounded';
|
||||||
|
import IconHome from '@mui/icons-material/Home';
|
||||||
|
import { ExplorerItem, ExplorerCursor, ExplorerItemType } from '../../types';
|
||||||
|
|
||||||
|
|
||||||
|
const createTableCell: TableCellCustomizer<ExplorerItem> = (accessor, data) => {
|
||||||
|
switch (accessor) {
|
||||||
|
case "created": {
|
||||||
|
if (typeof data[accessor] === "number") {
|
||||||
|
const timestampObject = { timestamp: data[accessor] as number }; // Opprett et objekt med riktig struktur
|
||||||
|
return UnixTimestamp(timestampObject);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "extension": {
|
||||||
|
if (data[accessor] === null) {
|
||||||
|
return <FolderIcon sx={{ margin: 1 }} />
|
||||||
|
} else {
|
||||||
|
return <Typography>{data[accessor]}</Typography>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: Array<TablePropetyConfig> = [
|
||||||
|
{ label: "Name", accessor: "name" },
|
||||||
|
{ label: "Format", accessor: "extension" },
|
||||||
|
{ label: "Created", accessor: "created" },
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function getPartFor(path: string, index: number): string | null {
|
||||||
|
if (path.match(/\//)) {
|
||||||
|
return path.split(/\//, index + 1).join("/");
|
||||||
|
} else if (path.match(/\\/)) {
|
||||||
|
return path.split(/\\/, index + 1).join("\\");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSegmentedNaviagatablePath(navigateTo: (path: string | null) => void, path: string | null): JSX.Element {
|
||||||
|
console.log(path);
|
||||||
|
const parts: Array<string> = path?.split(/\\|\//).map((value: string, index: number) => value.replaceAll(":", "")) ?? [];
|
||||||
|
const segments = parts.map((name: string, index: number) => {
|
||||||
|
return (
|
||||||
|
<Box key={index} sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center"
|
||||||
|
}}>
|
||||||
|
<Button sx={{
|
||||||
|
borderRadius: 5
|
||||||
|
}} onClick={() => navigateTo(getPartFor(path!, index))}>
|
||||||
|
<Typography>{name}</Typography>
|
||||||
|
</Button>
|
||||||
|
{index < parts.length - 1 && <IconForward fontSize="small" />}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
console.log(parts)
|
||||||
|
return (
|
||||||
|
<Box display="flex">
|
||||||
|
{segments}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function ExplorePage() {
|
||||||
|
const muiTheme = useTheme();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const client = useStompClient();
|
||||||
|
const cursor = useSelector((state: RootState) => state.explorer)
|
||||||
|
|
||||||
|
const navigateTo = (path: string | null) => {
|
||||||
|
console.log(path)
|
||||||
|
if (path) {
|
||||||
|
client?.publish({
|
||||||
|
destination: "/app/explorer/navigate",
|
||||||
|
body: path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onItemSelectedEvent: TableRowActionEvents<ExplorerItem> = {
|
||||||
|
click: (row: ExplorerItem) => null,
|
||||||
|
doubleClick: (row: ExplorerItem) => {
|
||||||
|
console.log(row);
|
||||||
|
if (row.type === "FOLDER") {
|
||||||
|
navigateTo(row.path);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contextMenu: (row: ExplorerItem) => null
|
||||||
|
}
|
||||||
|
|
||||||
|
const onHomeClick = () => {
|
||||||
|
client?.publish({
|
||||||
|
destination: "/app/explorer/home"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useWsSubscription<ExplorerCursor>("/topic/explorer/go", (response) => {
|
||||||
|
dispatch(updateItems(response))
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (cursor)
|
||||||
|
|
||||||
|
|
||||||
|
// Kjør din funksjon her når komponenten lastes inn for første gang
|
||||||
|
// Sjekk om cursor er null
|
||||||
|
if (cursor.path === null && client !== null) {
|
||||||
|
console.log(cursor)
|
||||||
|
// Kjør din funksjon her når cursor er null og client ikke er null
|
||||||
|
client?.publish({
|
||||||
|
destination: "/app/explorer/home"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Alternativt, du kan dispatche en Redux handling her
|
||||||
|
// dispatch(fetchDataAction()); // Eksempel på å dispatche en handling
|
||||||
|
}
|
||||||
|
}, [cursor, client, dispatch]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box display="block">
|
||||||
|
<Box sx={{
|
||||||
|
height: 50,
|
||||||
|
width: "100%",
|
||||||
|
maxHeight: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
backgroundColor: muiTheme.palette.background.paper
|
||||||
|
}}>
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
}}>
|
||||||
|
<Button onClick={onHomeClick} sx={{
|
||||||
|
borderRadius: 5
|
||||||
|
}}>
|
||||||
|
<IconHome />
|
||||||
|
</Button>
|
||||||
|
<Box sx={{
|
||||||
|
borderRadius: 5,
|
||||||
|
backgroundColor: muiTheme.palette.divider
|
||||||
|
}}>
|
||||||
|
{getSegmentedNaviagatablePath(navigateTo, cursor?.path)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{
|
||||||
|
display: "block",
|
||||||
|
height: "calc(100% - 120px)",
|
||||||
|
overflow: "hidden",
|
||||||
|
position: "absolute",
|
||||||
|
width: "100%"
|
||||||
|
}}>
|
||||||
|
<SimpleTable items={cursor?.items ?? []} columns={columns} customizer={createTableCell} onRowClickedEvent={onItemSelectedEvent} />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
75
UI/web/src/app/page/LaunchPage.tsx
Normal file
75
UI/web/src/app/page/LaunchPage.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import SimpleTable, { TableCellCustomizer, TablePropetyConfig } from "../features/table"
|
||||||
|
import { RootState } from "../store";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useStompClient } from "react-stomp-hooks";
|
||||||
|
import { Box, Button, useTheme } from "@mui/material";
|
||||||
|
import IconRefresh from '@mui/icons-material/Refresh'
|
||||||
|
|
||||||
|
|
||||||
|
const columns: Array<TablePropetyConfig> = [
|
||||||
|
{ label: "Title", accessor: "givenTitle" },
|
||||||
|
{ label: "Type", accessor: "determinedType" },
|
||||||
|
{ label: "Collection", accessor: "givenCollection" },
|
||||||
|
{ label: "Encoded", accessor: "eventEncoded" }
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function LaunchPage() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const muiTheme = useTheme();
|
||||||
|
const client = useStompClient();
|
||||||
|
const simpleList = useSelector((state: RootState) => state.kafkaComposedFlat)
|
||||||
|
useEffect(() => {
|
||||||
|
if (simpleList.items.filter((item) => item.encodingTimeLeft !== null).length > 0) {
|
||||||
|
columns.push({
|
||||||
|
label: "Completion",
|
||||||
|
accessor: "encodingTimeLeft"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [simpleList, dispatch])
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
client?.publish({
|
||||||
|
"destination": "/app/items",
|
||||||
|
"body": "Potato"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box display="block">
|
||||||
|
<Box sx={{
|
||||||
|
height: 50,
|
||||||
|
width: "100%",
|
||||||
|
maxHeight: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
backgroundColor: muiTheme.palette.background.paper
|
||||||
|
}}>
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
}}>
|
||||||
|
<Button onClick={onRefresh} sx={{
|
||||||
|
borderRadius: 5
|
||||||
|
}}>
|
||||||
|
<IconRefresh />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{
|
||||||
|
display: "block",
|
||||||
|
height: "calc(100% - 120px)",
|
||||||
|
overflow: "hidden",
|
||||||
|
position: "absolute",
|
||||||
|
width: "100%"
|
||||||
|
}}>
|
||||||
|
<SimpleTable items={simpleList.items} columns={columns} />
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
22
UI/web/src/app/store/composed-slice.ts
Normal file
22
UI/web/src/app/store/composed-slice.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
|
|
||||||
|
interface ComposedState {
|
||||||
|
items: Array<EventDataObject>
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: ComposedState = {
|
||||||
|
items: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const composedSlice = createSlice({
|
||||||
|
name: "Composed",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
updateItems(state, action: PayloadAction<Array<EventDataObject>>) {
|
||||||
|
state.items = action.payload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const { updateItems } = composedSlice.actions;
|
||||||
|
export default composedSlice.reducer;
|
||||||
29
UI/web/src/app/store/explorer-slice.ts
Normal file
29
UI/web/src/app/store/explorer-slice.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
|
import { ExplorerItem, ExplorerCursor } from "../../types"
|
||||||
|
|
||||||
|
interface ExplorerState {
|
||||||
|
name: string | null
|
||||||
|
path: string | null
|
||||||
|
items: Array<ExplorerItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: ExplorerState = {
|
||||||
|
name: null,
|
||||||
|
path: null,
|
||||||
|
items: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const composedSlice = createSlice({
|
||||||
|
name: "Explorer",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
updateItems(state, action: PayloadAction<ExplorerCursor>) {
|
||||||
|
state.items = action.payload.items;
|
||||||
|
state.name = action.payload.name;
|
||||||
|
state.path = action.payload.path
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const { updateItems } = composedSlice.actions;
|
||||||
|
export default composedSlice.reducer;
|
||||||
22
UI/web/src/app/store/kafka-items-flat-slice.ts
Normal file
22
UI/web/src/app/store/kafka-items-flat-slice.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
|
|
||||||
|
interface ComposedState {
|
||||||
|
items: Array<SimpleEventDataObject>
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: ComposedState = {
|
||||||
|
items: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const kafkaComposedFlat = createSlice({
|
||||||
|
name: "Composed",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
simpleEventsUpdate(state, action: PayloadAction<Array<SimpleEventDataObject>>) {
|
||||||
|
state.items = action.payload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const { simpleEventsUpdate } = kafkaComposedFlat.actions;
|
||||||
|
export default kafkaComposedFlat.reducer;
|
||||||
@ -6,6 +6,8 @@ body {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
max-height: 100vh;
|
||||||
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
|||||||
16
UI/web/src/theme.d.ts
vendored
Normal file
16
UI/web/src/theme.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Theme, ThemeOptions } from '@mui/material/styles';
|
||||||
|
|
||||||
|
declare module '@mui/material/styles' {
|
||||||
|
interface CustomTheme extends Theme {
|
||||||
|
status: {
|
||||||
|
danger: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// allow configuration using `createTheme`
|
||||||
|
interface CustomThemeOptions extends ThemeOptions {
|
||||||
|
status?: {
|
||||||
|
danger?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function createTheme(options?: CustomThemeOptions): CustomTheme;
|
||||||
|
}
|
||||||
23
UI/web/src/theme.ts
Normal file
23
UI/web/src/theme.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { createTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
export const theme = createTheme({
|
||||||
|
palette: {
|
||||||
|
mode: "dark",
|
||||||
|
primary: {
|
||||||
|
main: '#8800da',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#004dbb',
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
default: '#181818',
|
||||||
|
paper: '#080808',
|
||||||
|
},
|
||||||
|
divider: '#000000',
|
||||||
|
action: {
|
||||||
|
selected: "rgb(67 0 107)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default theme;
|
||||||
74
UI/web/src/types.d.ts
vendored
Normal file
74
UI/web/src/types.d.ts
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { type } from "os";
|
||||||
|
|
||||||
|
|
||||||
|
interface ExplorerCursor {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
items: Array<ExplorerItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type ExplorerItemType = "FILE" | "FOLDER";
|
||||||
|
|
||||||
|
interface ExplorerItem {
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
extension: string|null;
|
||||||
|
created: number,
|
||||||
|
type: ExplorerItemType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface EventDataObject {
|
||||||
|
id: string;
|
||||||
|
details: Details
|
||||||
|
encode: Encode
|
||||||
|
io: IO
|
||||||
|
events: Array<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Details {
|
||||||
|
title: string;
|
||||||
|
file: string;
|
||||||
|
sanitizedName: string
|
||||||
|
collection: string|null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Metadata {
|
||||||
|
source: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Encode {
|
||||||
|
state: string;
|
||||||
|
progress: number = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IO {
|
||||||
|
inputFile: string;
|
||||||
|
outputFile: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum SimpleEventDataState {
|
||||||
|
NA,
|
||||||
|
QUEUED,
|
||||||
|
STARTED,
|
||||||
|
ENDED,
|
||||||
|
FAILED,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SimpleEventDataObject {
|
||||||
|
id: string;
|
||||||
|
name?: string | null;
|
||||||
|
path?: string | null;
|
||||||
|
givenTitle?: string | null;
|
||||||
|
givenSanitizedName?: string | null;
|
||||||
|
givenCollection?: string | null;
|
||||||
|
determinedType?: string | null;
|
||||||
|
eventEncoded: SimpleEventDataState;
|
||||||
|
eventExtracted: SimpleEventDataState;
|
||||||
|
eventConverted: SimpleEventDataState;
|
||||||
|
eventCollected: SimpleEventDataState;
|
||||||
|
encodingProgress?: number | null;
|
||||||
|
encodingTimeLeft?: number | null;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user