1st Version

This commit is contained in:
2024-03-19 17:06:47 +01:00
commit d79d2aefee
1948 changed files with 361921 additions and 0 deletions

2
vrpmdvfrontend/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/node_modules/
/tmpDir/

46
vrpmdvfrontend/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,46 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch firefox",
"type": "firefox",
"request": "launch",
"url": "http://localhost:5001",
"webRoot": "${workspaceFolder}",
},
{
"name": "Launch Firfox new",
"type": "firefox",
"request": "launch",
"url": "http://localhost/index.html",
"webRoot": "${workspaceFolder}"
}
{
"name": "Launch Firefox new2",
"type": "firefox",
"request": "launch",
"port": 5173,
"url": "http://localhost/index.html",
"webRoot": "${workspaceFolder}"
}
{
"name": "Launch index.html",
"type": "firefox",
"request": "launch",
"port": 5173,
"file": "${workspaceFolder}/index.html"
},
{
"type": "firefox",
"request": "launch",
"name": "Launch My Firefox",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}",
"timeout": 90000,
"tmpDir": "/home/markus/git/vrpmdvweb/vrpmdvfrontend/tmpDir"
},
]
}

37
vrpmdvfrontend/Dockerfile Normal file
View File

@@ -0,0 +1,37 @@
# This Dockerfile uses `serve` npm package to serve the static files with node process.
# You can find the Dockerfile for nginx in the following link:
# https://github.com/refinedev/dockerfiles/blob/main/vite/Dockerfile.nginx
FROM refinedev/node:18 AS base
FROM base as deps
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base as builder
ENV NODE_ENV production
COPY --from=deps /app/refine/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base as runner
ENV NODE_ENV production
RUN npm install -g serve
COPY --from=builder /app/refine/dist ./
USER refine
CMD ["serve"]

49
vrpmdvfrontend/README.MD Normal file
View File

@@ -0,0 +1,49 @@
# refine-project
<div align="center" style="margin: 30px;">
<a href="https://refine.dev">
<img alt="refine logo" src="https://refine.ams3.cdn.digitaloceanspaces.com/readme/refine-readme-banner.png">
</a>
</div>
<br/>
This [Refine](https://github.com/refinedev/refine) project was generated with [create refine-app](https://github.com/refinedev/refine/tree/master/packages/create-refine-app).
## Getting Started
A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility ✨
Refine's hooks and components simplifies the development process and eliminates the repetitive tasks by providing industry-standard solutions for crucial aspects of a project, including authentication, access control, routing, networking, state management, and i18n.
## Available Scripts
### Running the development server.
```bash
npm run dev
```
### Building for production.
```bash
npm run build
```
### Running the production server.
```bash
npm run start
```
## Learn More
To learn more about **Refine**, please check out the [Documentation](https://refine.dev/docs)
- **REST Data Provider** [Docs](https://refine.dev/docs/core/providers/data-provider/#overview)
- **Material UI** [Docs](https://refine.dev/docs/ui-frameworks/mui/tutorial/)
- **Custom Auth Provider** [Docs](https://refine.dev/docs/core/providers/auth-provider/)
- **React Router** [Docs](https://refine.dev/docs/core/providers/router-provider/)
## License
MIT

2
vrpmdvfrontend/dist/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/favicon.ico
/index.html

1
vrpmdvfrontend/dist/assets/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/index-a25c81c8.js

41
vrpmdvfrontend/index.html Normal file
View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Markus Lehr | VR Predictive Maintenance Device."
/>
<meta
data-rh="true"
property="og:image"
content="https://refine.dev/img/refine_social.png"
/>
<meta
data-rh="true"
name="twitter:image"
content="https://refine.dev/img/refine_social.png"
/>
<title>
Markus Lehr | VR Predictive Maintenance Device.
</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm dev` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

11266
vrpmdvfrontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
{
"name": "vrpmdv",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.8.3",
"@mui/lab": "^5.0.0-alpha.85",
"@mui/material": "^5.8.6",
"@mui/x-data-grid": "^6.6.0",
"@refinedev/cli": "^2.16.1",
"@refinedev/core": "^4.47.1",
"@refinedev/devtools": "^1.1.35",
"@refinedev/kbar": "^1.3.6",
"@refinedev/mui": "^5.14.4",
"@refinedev/react-hook-form": "^4.8.14",
"@refinedev/react-router-v6": "^4.5.5",
"@refinedev/simple-rest": "^5.0.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-hook-form": "^7.30.0",
"react-router-dom": "^6.8.1"
},
"devDependencies": {
"@types/node": "^18.16.2",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@vitejs/plugin-react": "^4.0.0",
"eslint": "^8.38.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"typescript": "^4.7.4",
"vite": "^4.3.1"
},
"scripts": {
"dev": "refine dev",
"build": "tsc && refine build",
"preview": "refine start",
"refine": "refine"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"refine": {
"projectId": "4LRbGi-1874iA-m2rbd1"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

184
vrpmdvfrontend/src/App.tsx Normal file
View File

@@ -0,0 +1,184 @@
import { Authenticated, GitHubBanner, Refine } from "@refinedev/core";
import { DevtoolsPanel, DevtoolsProvider } from "@refinedev/devtools";
import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar";
import {
ErrorComponent,
notificationProvider,
RefineSnackbarProvider,
ThemedLayoutV2,
ThemedTitleV2,
} from "@refinedev/mui";
import CssBaseline from "@mui/material/CssBaseline";
import GlobalStyles from "@mui/material/GlobalStyles";
import routerBindings, {
CatchAllNavigate,
DocumentTitleHandler,
NavigateToResource,
UnsavedChangesNotifier,
} from "@refinedev/react-router-v6";
// import dataProvider from "@refinedev/simple-rest";
import { dataProvider } from './dataprovider/dataprovider'
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom";
import { authProvider } from "./authProvider";
import { AppIcon } from "./components/app-icon";
import { Header } from "./components/header";
import { ColorModeContextProvider } from "./contexts/color-mode";
import {
BlogPostCreate,
BlogPostEdit,
BlogPostList,
BlogPostShow,
} from "./pages/blog-posts";
import {
CategoryCreate,
CategoryEdit,
CategoryList,
CategoryShow,
} from "./pages/categories";
import { ForgotPassword } from "./pages/forgotPassword";
import { Login } from "./pages/login";
import { Register } from "./pages/register";
import { MonitoringList } from "./pages/monitorings/list";
import { MonitoringCreate } from "./pages/monitorings/create";
import { MonitoringEdit } from "./pages/monitorings/edit";
import { MonitoringShow } from "./pages/monitorings/show";
const API_URL = "https://api.fake-rest.refine.dev";
const MONITORINGS_API_URL = "http://127.0.0.1:5000//vrpmdvapi/1_0";
function App() {
return (
<BrowserRouter>
<RefineKbarProvider>
<ColorModeContextProvider>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
<RefineSnackbarProvider>
<DevtoolsProvider>
<Refine
dataProvider={{
default: dataProvider(API_URL),
monitorings: dataProvider(MONITORINGS_API_URL),
}}
notificationProvider={notificationProvider}
authProvider={authProvider}
routerProvider={routerBindings}
resources={[
{
name: "monitorings",
list: "/monitorings",
create: "/monitorings/create",
edit: "/monitorings/edit/:id",
meta: {
canDelete: true,
dataProviderName: "monitorings"
},
},
{
name: "blog_posts",
list: "/blog-posts",
create: "/blog-posts/create",
edit: "/blog-posts/edit/:id",
show: "/blog-posts/show/:id",
meta: {
canDelete: true,
},
},
{
name: "categories",
list: "/categories",
create: "/categories/create",
edit: "/categories/edit/:id",
show: "/categories/show/:id",
meta: {
canDelete: true,
},
},
]}
options={{
syncWithLocation: true,
warnWhenUnsavedChanges: true,
useNewQueryKeys: true,
projectId: "4LRbGi-1874iA-m2rbd1",
}}
>
<Routes>
<Route
element={
<Authenticated
key="authenticated-inner"
fallback={<CatchAllNavigate to="/login" />}
>
<ThemedLayoutV2
Header={() => <Header sticky />}
Title={({ collapsed }) => (
<ThemedTitleV2
collapsed={collapsed}
text="Refine Project"
icon={<AppIcon />}
/>
)}
>
<Outlet />
</ThemedLayoutV2>
</Authenticated>
}
>
<Route
index
element={<NavigateToResource resource="monitorings" />}
/>
<Route path="/monitorings">
<Route index element={<MonitoringList />} />
<Route path="create" element={<MonitoringCreate />} />
<Route path="edit/:id" element={<MonitoringEdit />} />
</Route>
<Route path="/blog-posts">
<Route index element={<BlogPostList />} />
<Route path="create" element={<BlogPostCreate />} />
<Route path="edit/:id" element={<BlogPostEdit />} />
<Route path="show/:id" element={<BlogPostShow />} />
</Route>
<Route path="/categories">
<Route index element={<CategoryList />} />
<Route path="create" element={<CategoryCreate />} />
<Route path="edit/:id" element={<CategoryEdit />} />
<Route path="show/:id" element={<CategoryShow />} />
</Route>
<Route path="*" element={<ErrorComponent />} />
</Route>
<Route
element={
<Authenticated
key="authenticated-outer"
fallback={<Outlet />}
>
<NavigateToResource />
</Authenticated>
}
>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route
path="/forgot-password"
element={<ForgotPassword />}
/>
</Route>
</Routes>
<RefineKbar />
<UnsavedChangesNotifier />
<DocumentTitleHandler />
</Refine>
<DevtoolsPanel />
</DevtoolsProvider>
</RefineSnackbarProvider>
</ColorModeContextProvider>
</RefineKbarProvider>
</BrowserRouter>
);
}
export default App;

View File

@@ -0,0 +1,59 @@
import { AuthBindings } from "@refinedev/core";
export const TOKEN_KEY = "refine-auth";
export const authProvider: AuthBindings = {
login: async ({ username, email, password }) => {
if ((username || email) && password) {
localStorage.setItem(TOKEN_KEY, username);
return {
success: true,
redirectTo: "/",
};
}
return {
success: false,
error: {
name: "LoginError",
message: "Invalid username or password",
},
};
},
logout: async () => {
localStorage.removeItem(TOKEN_KEY);
return {
success: true,
redirectTo: "/login",
};
},
check: async () => {
const token = localStorage.getItem(TOKEN_KEY);
if (token) {
return {
authenticated: true,
};
}
return {
authenticated: false,
redirectTo: "/login",
};
},
getPermissions: async () => null,
getIdentity: async () => {
const token = localStorage.getItem(TOKEN_KEY);
if (token) {
return {
id: 1,
name: "John Doe",
avatar: "https://i.pravatar.cc/300",
};
}
return null;
},
onError: async (error) => {
console.error(error);
return { error };
},
};

View File

@@ -0,0 +1,22 @@
import React from "react";
export const AppIcon: React.FC = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 16 16"
fill="none"
>
<g fill="currentColor">
<path d="M8 6a2 2 0 1 0 0-4 2 2 0 0 0 0 4z" />
<path
fill-rule="evenodd"
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.333 4a2.667 2.667 0 0 1 5.334 0v8a2.667 2.667 0 1 1-5.334 0z"
clip-rule="evenodd"
/>
</g>
</svg>
);
};

View File

@@ -0,0 +1,80 @@
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
import AppBar from "@mui/material/AppBar";
import Avatar from "@mui/material/Avatar";
import IconButton from "@mui/material/IconButton";
import Stack from "@mui/material/Stack";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import { useGetIdentity } from "@refinedev/core";
import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from "@refinedev/mui";
import React, { useContext } from "react";
import { ColorModeContext } from "../../contexts/color-mode";
type IUser = {
id: number;
name: string;
avatar: string;
};
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
sticky = true,
}) => {
const { mode, setMode } = useContext(ColorModeContext);
const { data: user } = useGetIdentity<IUser>();
return (
<AppBar position={sticky ? "sticky" : "relative"}>
<Toolbar>
<Stack
direction="row"
width="100%"
justifyContent="flex-end"
alignItems="center"
>
<HamburgerMenu />
<Stack
direction="row"
width="100%"
justifyContent="flex-end"
alignItems="center"
>
<IconButton
color="inherit"
onClick={() => {
setMode();
}}
>
{mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />}
</IconButton>
{(user?.avatar || user?.name) && (
<Stack
direction="row"
gap="16px"
alignItems="center"
justifyContent="center"
>
{user?.name && (
<Typography
sx={{
display: {
xs: "none",
sm: "inline-block",
},
}}
variant="subtitle2"
>
{user?.name}
</Typography>
)}
<Avatar src={user?.avatar} alt={user?.name} />
</Stack>
)}
</Stack>
</Stack>
</Toolbar>
</AppBar>
);
};

View File

@@ -0,0 +1 @@
export { Header } from "./header";

View File

@@ -0,0 +1,51 @@
import TextField from "@mui/material/TextField";
interface NumericInputProps {
label: string;
value: string;
}
export const NumericInput = ({label:string, value}) : NumericInputProps => {
return (
<TextField
type="number"
label={label}
value={value}
//defaultValue="10"
// error={!!(errors as any)?.samplerate}
// helperText={(errors as any)?.samplerate?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
/>
);
}
// function NumericInput() {
// return (
// <TextField
// type="number"
// label="Enter a number"
// defaultValue="10"
// // Other props
// />
// );
// }
// {...register("samplerate", {
// required: "The samplerate is required",
// })}
// error={!!(errors as any)?.samplerate}
// helperText={(errors as any)?.samplerate?.message}
// margin="normal"
// fullWidth
// InputLabelProps={{ shrink: true }}
// type="number"
// label={"Samplerate"}
// name="samplerate"

View File

@@ -0,0 +1,59 @@
import { ThemeProvider } from "@mui/material/styles";
import { RefineThemes } from "@refinedev/mui";
import React, {
createContext,
PropsWithChildren,
useEffect,
useState,
} from "react";
type ColorModeContextType = {
mode: string;
setMode: () => void;
};
export const ColorModeContext = createContext<ColorModeContextType>(
{} as ColorModeContextType
);
export const ColorModeContextProvider: React.FC<PropsWithChildren> = ({
children,
}) => {
const colorModeFromLocalStorage = localStorage.getItem("colorMode");
const isSystemPreferenceDark = window?.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
const systemPreference = isSystemPreferenceDark ? "dark" : "light";
const [mode, setMode] = useState(
colorModeFromLocalStorage || systemPreference
);
useEffect(() => {
window.localStorage.setItem("colorMode", mode);
}, [mode]);
const setColorMode = () => {
if (mode === "light") {
setMode("dark");
} else {
setMode("light");
}
};
return (
<ColorModeContext.Provider
value={{
setMode: setColorMode,
mode,
}}
>
<ThemeProvider
// you can change the theme colors here. example: mode === "light" ? RefineThemes.Magenta : RefineThemes.MagentaDark
theme={mode === "light" ? RefineThemes.Blue : RefineThemes.BlueDark}
>
{children}
</ThemeProvider>
</ColorModeContext.Provider>
);
};

View File

@@ -0,0 +1,197 @@
import { AxiosInstance } from "axios";
import { stringify } from "query-string";
import { DataProvider } from "@refinedev/core";
import { axiosInstance, generateSort, generateFilter } from "./utils";
type MethodTypes = "get" | "delete" | "head" | "options";
type MethodTypesWithBody = "post" | "put" | "patch";
export const dataProvider = (
apiUrl: string,
httpClient: AxiosInstance = axiosInstance,
): Omit<
Required<DataProvider>,
"createMany" | "updateMany" | "deleteMany"
> => ({
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const url = `${apiUrl}/${resource}`;
const { current = 1, pageSize = 10, mode = "server" } = pagination ?? {};
const { headers: headersFromMeta, method } = meta ?? {};
const requestMethod = (method as MethodTypes) ?? "get";
const queryFilters = generateFilter(filters);
const query: {
_start?: number;
_end?: number;
_sort?: string;
_order?: string;
} = {};
if (mode === "server") {
query._start = (current - 1) * pageSize;
query._end = current * pageSize;
}
const generatedSort = generateSort(sorters);
if (generatedSort) {
const { _sort, _order } = generatedSort;
query._sort = _sort.join(",");
query._order = _order.join(",");
}
const combinedQuery = { ...query, ...queryFilters };
const urlWithQuery = Object.keys(combinedQuery).length
? `${url}?${stringify(combinedQuery)}`
: url;
const { data, headers } = await httpClient[requestMethod](urlWithQuery, {
headers: headersFromMeta,
});
const total = +headers["x-total-count"];
return {
data,
total: total || data.length,
};
},
getMany: async ({ resource, ids, meta }) => {
const { headers, method } = meta ?? {};
const requestMethod = (method as MethodTypes) ?? "get";
const { data } = await httpClient[requestMethod](
`${apiUrl}/${resource}?${stringify({ id: ids })}`,
{ headers },
);
return {
data,
};
},
create: async ({ resource, variables, meta }) => {
const url = `${apiUrl}/${resource}`;
const { headers, method } = meta ?? {};
const requestMethod = (method as MethodTypesWithBody) ?? "post";
const { data } = await httpClient[requestMethod](url, variables, {
headers,
});
return {
data,
};
},
update: async ({ resource, id, variables, meta }) => {
const url = `${apiUrl}/${resource}/${id}`;
const { headers, method } = meta ?? {};
const requestMethod = (method as MethodTypesWithBody) ?? "patch";
const { data } = await httpClient[requestMethod](url, variables, {
headers,
});
return {
data,
};
},
getOne: async ({ resource, id, meta }) => {
const url = `${apiUrl}/${resource}/${id}`;
const { headers, method } = meta ?? {};
const requestMethod = (method as MethodTypes) ?? "get";
const { data } = await httpClient[requestMethod](url, { headers });
return {
data,
};
},
deleteOne: async ({ resource, id, variables, meta }) => {
const url = `${apiUrl}/${resource}/${id}`;
const { headers, method } = meta ?? {};
const requestMethod = (method as MethodTypesWithBody) ?? "delete";
const { data } = await httpClient[requestMethod](url, {
data: variables,
headers,
});
return {
data,
};
},
getApiUrl: () => {
return apiUrl;
},
custom: async ({
url,
method,
filters,
sorters,
payload,
query,
headers,
}) => {
let requestUrl = `${url}?`;
if (sorters) {
const generatedSort = generateSort(sorters);
if (generatedSort) {
const { _sort, _order } = generatedSort;
const sortQuery = {
_sort: _sort.join(","),
_order: _order.join(","),
};
requestUrl = `${requestUrl}&${stringify(sortQuery)}`;
}
}
if (filters) {
const filterQuery = generateFilter(filters);
requestUrl = `${requestUrl}&${stringify(filterQuery)}`;
}
if (query) {
requestUrl = `${requestUrl}&${stringify(query)}`;
}
let axiosResponse;
switch (method) {
case "put":
case "post":
case "patch":
axiosResponse = await httpClient[method](url, payload, {
headers,
});
break;
case "delete":
axiosResponse = await httpClient.delete(url, {
data: payload,
headers: headers,
});
break;
default:
axiosResponse = await httpClient.get(requestUrl, {
headers,
});
break;
}
const { data } = axiosResponse;
return Promise.resolve({ data });
},
});

View File

@@ -0,0 +1,21 @@
import { HttpError } from "@refinedev/core";
import axios from "axios";
const axiosInstance = axios.create();
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const customError: HttpError = {
...error,
message: error.response?.data?.message,
statusCode: error.response?.status,
};
return Promise.reject(customError);
},
);
export { axiosInstance };

View File

@@ -0,0 +1,30 @@
import { CrudFilters } from "@refinedev/core";
import { mapOperator } from "./mapOperator";
export const generateFilter = (filters?: CrudFilters) => {
const queryFilters: { [key: string]: string } = {};
if (filters) {
filters.map((filter) => {
if (filter.operator === "or" || filter.operator === "and") {
throw new Error(
`[@refinedev/simple-rest]: \`operator: ${filter.operator}\` is not supported. You can create custom data provider. https://refine.dev/docs/api-reference/core/providers/data-provider/#creating-a-data-provider`,
);
}
if ("field" in filter) {
const { field, operator, value } = filter;
if (field === "q") {
queryFilters[field] = value;
return;
}
const mappedOperator = mapOperator(operator);
queryFilters[`${field}${mappedOperator}`] = value;
}
});
}
return queryFilters;
};

View File

@@ -0,0 +1,20 @@
import { CrudSorting } from "@refinedev/core";
export const generateSort = (sorters?: CrudSorting) => {
if (sorters && sorters.length > 0) {
const _sort: string[] = [];
const _order: string[] = [];
sorters.map((item) => {
_sort.push(item.field);
_order.push(item.order);
});
return {
_sort,
_order,
};
}
return;
};

View File

@@ -0,0 +1,4 @@
export { mapOperator } from "./mapOperator";
export { generateSort } from "./generateSort";
export { generateFilter } from "./generateFilter";
export { axiosInstance } from "./axios";

View File

@@ -0,0 +1,14 @@
import { CrudOperators } from "@refinedev/core";
export const mapOperator = (operator: CrudOperators): string => {
switch (operator) {
case "ne":
case "gte":
case "lte":
return `_${operator}`;
case "contains":
return "_like";
default:
return "";
}
};

View File

@@ -0,0 +1,13 @@
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
const container = document.getElementById("root") as HTMLElement;
const root = createRoot(container);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

View File

@@ -0,0 +1,119 @@
import { Autocomplete, Box, MenuItem, Select, TextField } from "@mui/material";
import { Create, useAutocomplete } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller } from "react-hook-form";
export const BlogPostCreate = () => {
const {
saveButtonProps,
refineCore: { formLoading },
register,
control,
formState: { errors },
} = useForm({});
const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
resource: "categories",
});
return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("title", {
required: "This field is required",
})}
error={!!(errors as any)?.title}
helperText={(errors as any)?.title?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label={"Title"}
name="title"
/>
<TextField
{...register("content", {
required: "This field is required",
})}
error={!!(errors as any)?.content}
helperText={(errors as any)?.content?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
multiline
label={"Content"}
name="content"
/>
<Controller
control={control}
name={"category.id"}
rules={{ required: "This field is required" }}
// eslint-disable-next-line
defaultValue={null as any}
render={({ field }) => (
<Autocomplete
{...categoryAutocompleteProps}
{...field}
onChange={(_, value) => {
field.onChange(value.id);
}}
getOptionLabel={(item) => {
return (
categoryAutocompleteProps?.options?.find((p) => {
const itemId =
typeof item === "object"
? item?.id?.toString()
: item?.toString();
const pId = p?.id?.toString();
return itemId === pId;
})?.title ?? ""
);
}}
isOptionEqualToValue={(option, value) => {
const optionId = option?.id?.toString();
const valueId =
typeof value === "object"
? value?.id?.toString()
: value?.toString();
return value === undefined || optionId === valueId;
}}
renderInput={(params) => (
<TextField
{...params}
label={"Category"}
margin="normal"
variant="outlined"
error={!!(errors as any)?.category?.id}
helperText={(errors as any)?.category?.id?.message}
required
/>
)}
/>
)}
/>
<Controller
name="status"
control={control}
render={({ field }) => {
return (
<Select
{...field}
value={field?.value || "draft"}
label={"Status"}
>
<MenuItem value="draft">Draft</MenuItem>
<MenuItem value="published">Published</MenuItem>
<MenuItem value="rejected">Rejected</MenuItem>
</Select>
);
}}
/>
</Box>
</Create>
);
};

View File

@@ -0,0 +1,124 @@
import { Autocomplete, Box, Select, TextField } from "@mui/material";
import MenuItem from "@mui/material/MenuItem";
import { Edit, useAutocomplete } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller } from "react-hook-form";
export const BlogPostEdit = () => {
const {
saveButtonProps,
refineCore: { queryResult, formLoading },
register,
control,
formState: { errors },
} = useForm({});
const blogPostsData = queryResult?.data?.data;
const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
resource: "categories",
defaultValue: blogPostsData?.category?.id,
});
return (
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("title", {
required: "This field is required",
})}
error={!!(errors as any)?.title}
helperText={(errors as any)?.title?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label={"Title"}
name="title"
/>
<TextField
{...register("content", {
required: "This field is required",
})}
error={!!(errors as any)?.content}
helperText={(errors as any)?.content?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
multiline
label={"Content"}
name="content"
rows={4}
/>
<Controller
control={control}
name={"category.id"}
rules={{ required: "This field is required" }}
// eslint-disable-next-line
defaultValue={null as any}
render={({ field }) => (
<Autocomplete
{...categoryAutocompleteProps}
{...field}
onChange={(_, value) => {
field.onChange(value.id);
}}
getOptionLabel={(item) => {
return (
categoryAutocompleteProps?.options?.find((p) => {
const itemId =
typeof item === "object"
? item?.id?.toString()
: item?.toString();
const pId = p?.id?.toString();
return itemId === pId;
})?.title ?? ""
);
}}
isOptionEqualToValue={(option, value) => {
const optionId = option?.id?.toString();
const valueId =
typeof value === "object"
? value?.id?.toString()
: value?.toString();
return value === undefined || optionId === valueId;
}}
renderInput={(params) => (
<TextField
{...params}
label={"Category"}
margin="normal"
variant="outlined"
error={!!(errors as any)?.category?.id}
helperText={(errors as any)?.category?.id?.message}
required
/>
)}
/>
)}
/>
<Controller
name="status"
control={control}
render={({ field }) => {
return (
<Select
{...field}
value={field?.value || "draft"}
label={"Status"}
>
<MenuItem value="draft">Draft</MenuItem>
<MenuItem value="published">Published</MenuItem>
<MenuItem value="rejected">Rejected</MenuItem>
</Select>
);
}}
/>
</Box>
</Edit>
);
};

View File

@@ -0,0 +1,4 @@
export * from "./create";
export * from "./edit";
export * from "./list";
export * from "./show";

View File

@@ -0,0 +1,112 @@
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import { useMany } from "@refinedev/core";
import {
DateField,
DeleteButton,
EditButton,
List,
MarkdownField,
ShowButton,
useDataGrid,
} from "@refinedev/mui";
import React from "react";
export const BlogPostList = () => {
const { dataGridProps } = useDataGrid({
syncWithLocation: true,
});
const { data: categoryData, isLoading: categoryIsLoading } = useMany({
resource: "categories",
ids:
dataGridProps?.rows
?.map((item: any) => item?.category?.id)
.filter(Boolean) ?? [],
queryOptions: {
enabled: !!dataGridProps?.rows,
},
});
const columns = React.useMemo<GridColDef[]>(
() => [
{
field: "id",
headerName: "ID",
type: "number",
minWidth: 50,
},
{
field: "title",
flex: 1,
headerName: "Title",
minWidth: 200,
},
{
field: "content",
flex: 1,
headerName: "content",
minWidth: 250,
renderCell: function render({ value }) {
if (!value) return "-";
return <MarkdownField value={value?.slice(0, 80) + "..." || ""} />;
},
},
{
field: "category",
flex: 1,
headerName: "Category",
minWidth: 300,
valueGetter: ({ row }) => {
const value = row?.category;
return value;
},
renderCell: function render({ value }) {
return categoryIsLoading ? (
<>Loading...</>
) : (
categoryData?.data?.find((item) => item.id === value?.id)?.title
);
},
},
{
field: "status",
flex: 1,
headerName: "Status",
minWidth: 200,
},
{
field: "createdAt",
flex: 1,
headerName: "Created at",
minWidth: 250,
renderCell: function render({ value }) {
return <DateField value={value} />;
},
},
{
field: "actions",
headerName: "Actions",
sortable: false,
renderCell: function render({ row }) {
return (
<>
<EditButton hideText recordItemId={row.id} />
<ShowButton hideText recordItemId={row.id} />
<DeleteButton hideText recordItemId={row.id} />
</>
);
},
align: "center",
headerAlign: "center",
minWidth: 80,
},
],
[categoryData]
);
return (
<List>
<DataGrid {...dataGridProps} columns={columns} autoHeight />
</List>
);
};

View File

@@ -0,0 +1,59 @@
import { Stack, Typography } from "@mui/material";
import { useOne, useShow } from "@refinedev/core";
import {
DateField,
MarkdownField,
NumberField,
Show,
TextFieldComponent as TextField,
} from "@refinedev/mui";
export const BlogPostShow = () => {
const { queryResult } = useShow({});
const { data, isLoading } = queryResult;
const record = data?.data;
const { data: categoryData, isLoading: categoryIsLoading } = useOne({
resource: "categories",
id: record?.category?.id || "",
queryOptions: {
enabled: !!record,
},
});
return (
<Show isLoading={isLoading}>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
{"ID"}
</Typography>
<NumberField value={record?.id ?? ""} />
<Typography variant="body1" fontWeight="bold">
{"Title"}
</Typography>
<TextField value={record?.title} />
<Typography variant="body1" fontWeight="bold">
{"Content"}
</Typography>
<MarkdownField value={record?.content} />
<Typography variant="body1" fontWeight="bold">
{"Category"}
</Typography>
{categoryIsLoading ? <>Loading...</> : <>{categoryData?.data?.title}</>}
<Typography variant="body1" fontWeight="bold">
{"Status"}
</Typography>
<TextField value={record?.status} />
<Typography variant="body1" fontWeight="bold">
{"CreatedAt"}
</Typography>
<DateField value={record?.createdAt} />
</Stack>
</Show>
);
};

View File

@@ -0,0 +1,36 @@
import { Box, TextField } from "@mui/material";
import { Create } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
export const CategoryCreate = () => {
const {
saveButtonProps,
refineCore: { formLoading },
register,
formState: { errors },
} = useForm({});
return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("title", {
required: "This field is required",
})}
error={!!(errors as any)?.title}
helperText={(errors as any)?.title?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label={"Title"}
name="title"
/>
</Box>
</Create>
);
};

View File

@@ -0,0 +1,35 @@
import { Box, TextField } from "@mui/material";
import { Edit } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
export const CategoryEdit = () => {
const {
saveButtonProps,
register,
formState: { errors },
} = useForm({});
return (
<Edit saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("title", {
required: "This field is required",
})}
error={!!(errors as any)?.title}
helperText={(errors as any)?.title?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label={"Title"}
name="title"
/>
</Box>
</Edit>
);
};

View File

@@ -0,0 +1,4 @@
export * from "./create";
export * from "./edit";
export * from "./list";
export * from "./show";

View File

@@ -0,0 +1,58 @@
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import {
DeleteButton,
EditButton,
List,
ShowButton,
useDataGrid,
} from "@refinedev/mui";
import React from "react";
export const CategoryList = () => {
const { dataGridProps } = useDataGrid({
pagination: {
mode: "client",
},
});
const columns = React.useMemo<GridColDef[]>(
() => [
{
field: "id",
headerName: "ID",
type: "number",
minWidth: 50,
},
{
field: "title",
flex: 1,
headerName: "Title",
minWidth: 200,
},
{
field: "actions",
headerName: "Actions",
sortable: false,
renderCell: function render({ row }) {
return (
<>
<EditButton hideText recordItemId={row.id} />
<ShowButton hideText recordItemId={row.id} />
<DeleteButton hideText recordItemId={row.id} />
</>
);
},
align: "center",
headerAlign: "center",
minWidth: 80,
},
],
[]
);
return (
<List>
<DataGrid {...dataGridProps} columns={columns} autoHeight />
</List>
);
};

View File

@@ -0,0 +1,29 @@
import { Stack, Typography } from "@mui/material";
import { useShow } from "@refinedev/core";
import {
NumberField,
Show,
TextFieldComponent as TextField,
} from "@refinedev/mui";
export const CategoryShow = () => {
const { queryResult } = useShow({});
const { data, isLoading } = queryResult;
const record = data?.data;
return (
<Show isLoading={isLoading}>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
{"ID"}
</Typography>
<NumberField value={record?.id ?? ""} />
<Typography variant="body1" fontWeight="bold">
{"Title"}
</Typography>
<TextField value={record?.title} />
</Stack>
</Show>
);
};

View File

@@ -0,0 +1,17 @@
import { AuthPage, ThemedTitleV2 } from "@refinedev/mui";
import { AppIcon } from "../../components/app-icon";
export const ForgotPassword = () => {
return (
<AuthPage
type="forgotPassword"
title={
<ThemedTitleV2
collapsed={false}
text="Refine Project"
icon={<AppIcon />}
/>
}
/>
);
};

View File

@@ -0,0 +1,20 @@
import { AuthPage, ThemedTitleV2 } from "@refinedev/mui";
import { AppIcon } from "../../components/app-icon";
export const Login = () => {
return (
<AuthPage
type="login"
title={
<ThemedTitleV2
collapsed={false}
text="Refine Project"
icon={<AppIcon />}
/>
}
formProps={{
defaultValues: { email: "demo@refine.dev", password: "demodemo" },
}}
/>
);
};

View File

@@ -0,0 +1,179 @@
import { Autocomplete, Box, InputAdornment, MenuItem, Select, TextField } from "@mui/material";
import { Create, NumberField, useAutocomplete } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller } from "react-hook-form";
import { IMonitoring } from "./monitorings.types";
export const MonitoringCreate = () => {
const {
saveButtonProps,
refineCore: { formLoading },
register,
control,
formState: { errors },
} = useForm<IMonitoring>({});
// const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
// resource: "categories",
// });
return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("name", {
required: "The name is required",
})}
error={!!(errors as any)?.name}
helperText={(errors as any)?.name?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label={"Name"}
name="name"
/>
<TextField
{...register("samplerate", {
required: "The samplerate is required",
})}
error={!!(errors as any)?.samplerate}
helperText={(errors as any)?.samplerate?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label={"Samplerate"}
name="samplerate"
InputProps={{
endAdornment:
<InputAdornment position="end">hz</InputAdornment>
}}
/>
<TextField
{...register("sampleperiod", {
required: "The sampleperiod is required",
})}
error={!!(errors as any)?.sampleperiod}
helperText={(errors as any)?.sampleperiod?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label={"Sampleperiod"}
name="sampleperiod"
InputProps={{
endAdornment:
<InputAdornment position="end">s</InputAdornment>
}}
/>
<TextField
{...register("downtime", {
required: "The downtime is required",
})}
error={!!(errors as any)?.downtime}
helperText={(errors as any)?.downtime?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label={"Downtime"}
name="downtime"
InputProps={{
endAdornment:
<InputAdornment position="end">s</InputAdornment>
}}
/>
<TextField
{...register("owner", {
required: "The downtime is required",
})}
error={!!(errors as any)?.downtime}
helperText={(errors as any)?.downtime?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
multiline
label={"Owner"}
name="owner"
/>
</Box>
</Create>
);
};
// <Controller
// name="status"
// control={control}
// render={({ field }) => {
// return (
// <Select
// {...field}
// value={field?.value || "stopped"}
// label={"Status"}
// >
// <MenuItem value="started">Draft</MenuItem>
// <MenuItem value="stoppedd">Published</MenuItem>
// </Select>
// );
// }}
// />
// <Controller
// control={control}
// name={"category.id"}
// rules={{ required: "This field is required" }}
// // eslint-disable-next-line
// defaultValue={null as any}
// render={({ field }) => (
// <Autocomplete
// {...categoryAutocompleteProps}
// {...field}
// onChange={(_, value) => {
// field.onChange(value.id);
// }}
// getOptionLabel={(item) => {
// return (
// categoryAutocompleteProps?.options?.find((p) => {
// const itemId =
// typeof item === "object"
// ? item?.id?.toString()
// : item?.toString();
// const pId = p?.id?.toString();
// return itemId === pId;
// })?.title ?? ""
// );
// }}
// isOptionEqualToValue={(option, value) => {
// const optionId = option?.id?.toString();
// const valueId =
// typeof value === "object"
// ? value?.id?.toString()
// : value?.toString();
// return value === undefined || optionId === valueId;
// }}
// renderInput={(params) => (
// <TextField
// {...params}
// label={"Category"}
// margin="normal"
// variant="outlined"
// error={!!(errors as any)?.category?.id}
// helperText={(errors as any)?.category?.id?.message}
// required
// />
// )}
// />
// )}
// />

View File

@@ -0,0 +1,163 @@
import { Autocomplete, Box, InputAdornment, Select, TextField } from "@mui/material";
import MenuItem from "@mui/material/MenuItem";
import { Edit, useAutocomplete } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller } from "react-hook-form";
import { IMonitoring } from "./monitorings.types";
export const MonitoringEdit = () => {
const {
saveButtonProps,
refineCore: { queryResult, formLoading },
register,
control,
formState: { errors },
} = useForm<IMonitoring>({});
const blogPostsData = queryResult?.data?.data;
// const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
// resource: "categories",
// defaultValue: blogPostsData?.category?.id,
// });
return (
<Edit dataProviderName="monitorings" isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("name", {
required: "The name is required",
})}
error={!!(errors as any)?.name}
helperText={(errors as any)?.name?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="text"
label={"Name"}
name="name"
/>
<TextField
{...register("samplerate", {
required: "The samplerate is required",
})}
error={!!(errors as any)?.samplerate}
helperText={(errors as any)?.samplerate?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label={"Samplerate"}
name="samplerate"
InputProps={{
endAdornment:
<InputAdornment position="end">hz</InputAdornment>
}}
/>
<TextField
{...register("sampleperiod", {
required: "The sampleperiod is required",
})}
error={!!(errors as any)?.sampleperiod}
helperText={(errors as any)?.sampleperiod?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label={"Sampleperiod"}
name="sampleperiod"
InputProps={{
endAdornment:
<InputAdornment position="end">s</InputAdornment>
}}
/>
<TextField
{...register("downtime", {
required: "The downtime is required",
})}
error={!!(errors as any)?.downtime}
helperText={(errors as any)?.downtime?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label={"Downtime"}
name="downtime"
InputProps={{
endAdornment:
<InputAdornment position="end">s</InputAdornment>
}}
/>
<TextField
{...register("owner", {
required: "The downtime is required",
})}
error={!!(errors as any)?.downtime}
helperText={(errors as any)?.downtime?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
multiline
label={"Owner"}
name="owner"
/>
</Box>
</Edit>
);
};
/*
{ <Controller
control={control}
name={"category.id"}
rules={{ required: "This field is required" }}
// eslint-disable-next-line
defaultValue={null as any}
render={({ field }) => (
<Autocomplete
{...categoryAutocompleteProps}
{...field}
onChange={(_, value) => {
field.onChange(value.id);
}}
getOptionLabel={(item) => {
return (
categoryAutocompleteProps?.options?.find((p) => {
const itemId =
typeof item === "object"
? item?.id?.toString()
: item?.toString();
const pId = p?.id?.toString();
return itemId === pId;
})?.title ?? ""
);
}}
isOptionEqualToValue={(option, value) => {
const optionId = option?.id?.toString();
const valueId =
typeof value === "object"
? value?.id?.toString()
: value?.toString();
return value === undefined || optionId === valueId;
}}
renderInput={(params) => (
<TextField
{...params}
label={"Category"}
margin="normal"
variant="outlined"
error={!!(errors as any)?.category?.id}
helperText={(errors as any)?.category?.id?.message}
required
/>
)}
/>
)}
/> }
*/

View File

@@ -0,0 +1,4 @@
export * from "./create";
export * from "./edit";
export * from "./list";
export * from "./show";

View File

@@ -0,0 +1,147 @@
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import {
DateField,
DeleteButton,
EditButton,
List,
MarkdownField,
NumberField,
ShowButton,
useDataGrid,
} from "@refinedev/mui";
import React from "react";
import { IMonitoring } from "./monitorings.types";
export const MonitoringList = () => {
const { dataGridProps } = useDataGrid<IMonitoring>({
syncWithLocation: true,
dataProviderName: "monitorings",
pagination: {
mode: "client",
pageSize: 10,
},
});
// const { data: categoryData, isLoading: categoryIsLoading } = useMany({
// resource: "categories",
// ids:
// dataGridProps?.rows
// ?.map((item: any) => item?.category?.id)
// .filter(Boolean) ?? [],
// queryOptions: {
// enabled: !!dataGridProps?.rows,
// },
// });
const columns = React.useMemo<GridColDef<IMonitoring>[]>(
() => [
{
field: "name",
flex: 1,
headerName: "Name",
type:"string",
minWidth: 250,
},
{
field: "id",
headerName: "ID",
type: "string",
minWidth: 300,
},
{
field: "created_at",
flex: 1,
headerName: "Created at",
minWidth: 30,
renderCell: function render({ value }) {
return <DateField value={value} />;
},
},
{
field: "samplerate",
flex: 0.3,
headerName: "Samplerate in Hz",
renderCell: function render({ row }) {
return (
<NumberField
value={row.samplerate}
options={{
minimumIntegerDigits: 1,
minimumFractionDigits:0,
maximumFractionDigits: 0,
}}
/>
);
},
minWidth: 120,
},
{
field: "sampleperiod",
flex: 0.3,
headerName: "Period in s",
renderCell: function render({ row }) {
return (
<NumberField
value={row.sampleperiod}
options={{
minimumIntegerDigits: 1,
minimumFractionDigits:1,
maximumFractionDigits: 3,
}}
/>
);
},
minWidth: 120,
},
{
field: "downtime",
flex: 0.3,
headerName: "Downtime in s",
renderCell: function render({ row }) {
return (
<NumberField
value={row.sampleperiod}
options={{
minimumIntegerDigits: 1,
minimumFractionDigits:1,
maximumFractionDigits: 3,
}}
/>
);
},
minWidth: 120,
},
{
field: "owner",
flex: 1,
headerName: "Owner",
type:"string",
minWidth: 80,
},
{
field: "actions",
headerName: "Actions",
sortable: false,
renderCell: function render({ row }) {
return (
<>
<EditButton hideText recordItemId={row.id} />
<DeleteButton hideText recordItemId={row.id} />
</>
);
},
align: "center",
headerAlign: "center",
minWidth: 80,
},
],
[]
);
return (
<List>
<DataGrid {...dataGridProps} columns={columns} autoHeight />
</List>
);
};

View File

@@ -0,0 +1,12 @@
export interface IMonitoring {
id: string;
created_at: string;
name: string;
samplerate: number;
sampleperiod: number;
downtime: number;
owner: string;
}

View File

@@ -0,0 +1,59 @@
import { Stack, Typography } from "@mui/material";
import { useOne, useShow } from "@refinedev/core";
import {
DateField,
MarkdownField,
NumberField,
Show,
TextFieldComponent as TextField,
} from "@refinedev/mui";
export const MonitoringShow = () => {
const { queryResult } = useShow({});
const { data, isLoading } = queryResult;
const record = data?.data;
const { data: categoryData, isLoading: categoryIsLoading } = useOne({
resource: "categories",
id: record?.category?.id || "",
queryOptions: {
enabled: !!record,
},
});
return (
<Show isLoading={isLoading}>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
{"ID"}
</Typography>
<NumberField value={record?.id ?? ""} />
<Typography variant="body1" fontWeight="bold">
{"Title"}
</Typography>
<TextField value={record?.title} />
<Typography variant="body1" fontWeight="bold">
{"Content"}
</Typography>
<MarkdownField value={record?.content} />
<Typography variant="body1" fontWeight="bold">
{"Category"}
</Typography>
{categoryIsLoading ? <>Loading...</> : <>{categoryData?.data?.title}</>}
<Typography variant="body1" fontWeight="bold">
{"Status"}
</Typography>
<TextField value={record?.status} />
<Typography variant="body1" fontWeight="bold">
{"CreatedAt"}
</Typography>
<DateField value={record?.createdAt} />
</Stack>
</Show>
);
};

View File

@@ -0,0 +1,17 @@
import { AuthPage, ThemedTitleV2 } from "@refinedev/mui";
import { AppIcon } from "../../components/app-icon";
export const Register = () => {
return (
<AuthPage
type="register"
title={
<ThemedTitleV2
collapsed={false}
text="Refine Project"
icon={<AppIcon />}
/>
}
/>
);
};

1
vrpmdvfrontend/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,494 @@
import {
__commonJS
} from "/node_modules/.vite/deps/chunk-AUZ3RYOM.js?v=e565559a";
// node_modules/strict-uri-encode/index.js
var require_strict_uri_encode = __commonJS({
"node_modules/strict-uri-encode/index.js"(exports, module) {
"use strict";
module.exports = (str) => encodeURIComponent(str).replace(/[!'()*]/g, (x) => `%${x.charCodeAt(0).toString(16).toUpperCase()}`);
}
});
// node_modules/decode-uri-component/index.js
var require_decode_uri_component = __commonJS({
"node_modules/decode-uri-component/index.js"(exports, module) {
"use strict";
var token = "%[a-f0-9]{2}";
var singleMatcher = new RegExp("(" + token + ")|([^%]+?)", "gi");
var multiMatcher = new RegExp("(" + token + ")+", "gi");
function decodeComponents(components, split) {
try {
return [decodeURIComponent(components.join(""))];
} catch (err) {
}
if (components.length === 1) {
return components;
}
split = split || 1;
var left = components.slice(0, split);
var right = components.slice(split);
return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right));
}
function decode(input) {
try {
return decodeURIComponent(input);
} catch (err) {
var tokens = input.match(singleMatcher) || [];
for (var i = 1; i < tokens.length; i++) {
input = decodeComponents(tokens, i).join("");
tokens = input.match(singleMatcher) || [];
}
return input;
}
}
function customDecodeURIComponent(input) {
var replaceMap = {
"%FE%FF": "<22><>",
"%FF%FE": "<22><>"
};
var match = multiMatcher.exec(input);
while (match) {
try {
replaceMap[match[0]] = decodeURIComponent(match[0]);
} catch (err) {
var result = decode(match[0]);
if (result !== match[0]) {
replaceMap[match[0]] = result;
}
}
match = multiMatcher.exec(input);
}
replaceMap["%C2"] = "<22>";
var entries = Object.keys(replaceMap);
for (var i = 0; i < entries.length; i++) {
var key = entries[i];
input = input.replace(new RegExp(key, "g"), replaceMap[key]);
}
return input;
}
module.exports = function(encodedURI) {
if (typeof encodedURI !== "string") {
throw new TypeError("Expected `encodedURI` to be of type `string`, got `" + typeof encodedURI + "`");
}
try {
encodedURI = encodedURI.replace(/\+/g, " ");
return decodeURIComponent(encodedURI);
} catch (err) {
return customDecodeURIComponent(encodedURI);
}
};
}
});
// node_modules/split-on-first/index.js
var require_split_on_first = __commonJS({
"node_modules/split-on-first/index.js"(exports, module) {
"use strict";
module.exports = (string, separator) => {
if (!(typeof string === "string" && typeof separator === "string")) {
throw new TypeError("Expected the arguments to be of type `string`");
}
if (separator === "") {
return [string];
}
const separatorIndex = string.indexOf(separator);
if (separatorIndex === -1) {
return [string];
}
return [
string.slice(0, separatorIndex),
string.slice(separatorIndex + separator.length)
];
};
}
});
// node_modules/filter-obj/index.js
var require_filter_obj = __commonJS({
"node_modules/filter-obj/index.js"(exports, module) {
"use strict";
module.exports = function(obj, predicate) {
var ret = {};
var keys = Object.keys(obj);
var isArr = Array.isArray(predicate);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var val = obj[key];
if (isArr ? predicate.indexOf(key) !== -1 : predicate(key, val, obj)) {
ret[key] = val;
}
}
return ret;
};
}
});
// node_modules/query-string/index.js
var require_query_string = __commonJS({
"node_modules/query-string/index.js"(exports) {
var strictUriEncode = require_strict_uri_encode();
var decodeComponent = require_decode_uri_component();
var splitOnFirst = require_split_on_first();
var filterObject = require_filter_obj();
var isNullOrUndefined = (value) => value === null || value === void 0;
var encodeFragmentIdentifier = Symbol("encodeFragmentIdentifier");
function encoderForArrayFormat(options) {
switch (options.arrayFormat) {
case "index":
return (key) => (result, value) => {
const index = result.length;
if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") {
return result;
}
if (value === null) {
return [...result, [encode(key, options), "[", index, "]"].join("")];
}
return [
...result,
[encode(key, options), "[", encode(index, options), "]=", encode(value, options)].join("")
];
};
case "bracket":
return (key) => (result, value) => {
if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") {
return result;
}
if (value === null) {
return [...result, [encode(key, options), "[]"].join("")];
}
return [...result, [encode(key, options), "[]=", encode(value, options)].join("")];
};
case "colon-list-separator":
return (key) => (result, value) => {
if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") {
return result;
}
if (value === null) {
return [...result, [encode(key, options), ":list="].join("")];
}
return [...result, [encode(key, options), ":list=", encode(value, options)].join("")];
};
case "comma":
case "separator":
case "bracket-separator": {
const keyValueSep = options.arrayFormat === "bracket-separator" ? "[]=" : "=";
return (key) => (result, value) => {
if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") {
return result;
}
value = value === null ? "" : value;
if (result.length === 0) {
return [[encode(key, options), keyValueSep, encode(value, options)].join("")];
}
return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
};
}
default:
return (key) => (result, value) => {
if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") {
return result;
}
if (value === null) {
return [...result, encode(key, options)];
}
return [...result, [encode(key, options), "=", encode(value, options)].join("")];
};
}
}
function parserForArrayFormat(options) {
let result;
switch (options.arrayFormat) {
case "index":
return (key, value, accumulator) => {
result = /\[(\d*)\]$/.exec(key);
key = key.replace(/\[\d*\]$/, "");
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === void 0) {
accumulator[key] = {};
}
accumulator[key][result[1]] = value;
};
case "bracket":
return (key, value, accumulator) => {
result = /(\[\])$/.exec(key);
key = key.replace(/\[\]$/, "");
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === void 0) {
accumulator[key] = [value];
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
case "colon-list-separator":
return (key, value, accumulator) => {
result = /(:list)$/.exec(key);
key = key.replace(/:list$/, "");
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === void 0) {
accumulator[key] = [value];
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
case "comma":
case "separator":
return (key, value, accumulator) => {
const isArray = typeof value === "string" && value.includes(options.arrayFormatSeparator);
const isEncodedArray = typeof value === "string" && !isArray && decode(value, options).includes(options.arrayFormatSeparator);
value = isEncodedArray ? decode(value, options) : value;
const newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map((item) => decode(item, options)) : value === null ? value : decode(value, options);
accumulator[key] = newValue;
};
case "bracket-separator":
return (key, value, accumulator) => {
const isArray = /(\[\])$/.test(key);
key = key.replace(/\[\]$/, "");
if (!isArray) {
accumulator[key] = value ? decode(value, options) : value;
return;
}
const arrayValue = value === null ? [] : value.split(options.arrayFormatSeparator).map((item) => decode(item, options));
if (accumulator[key] === void 0) {
accumulator[key] = arrayValue;
return;
}
accumulator[key] = [].concat(accumulator[key], arrayValue);
};
default:
return (key, value, accumulator) => {
if (accumulator[key] === void 0) {
accumulator[key] = value;
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
}
}
function validateArrayFormatSeparator(value) {
if (typeof value !== "string" || value.length !== 1) {
throw new TypeError("arrayFormatSeparator must be single character string");
}
}
function encode(value, options) {
if (options.encode) {
return options.strict ? strictUriEncode(value) : encodeURIComponent(value);
}
return value;
}
function decode(value, options) {
if (options.decode) {
return decodeComponent(value);
}
return value;
}
function keysSorter(input) {
if (Array.isArray(input)) {
return input.sort();
}
if (typeof input === "object") {
return keysSorter(Object.keys(input)).sort((a, b) => Number(a) - Number(b)).map((key) => input[key]);
}
return input;
}
function removeHash(input) {
const hashStart = input.indexOf("#");
if (hashStart !== -1) {
input = input.slice(0, hashStart);
}
return input;
}
function getHash(url) {
let hash = "";
const hashStart = url.indexOf("#");
if (hashStart !== -1) {
hash = url.slice(hashStart);
}
return hash;
}
function extract(input) {
input = removeHash(input);
const queryStart = input.indexOf("?");
if (queryStart === -1) {
return "";
}
return input.slice(queryStart + 1);
}
function parseValue(value, options) {
if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === "string" && value.trim() !== "")) {
value = Number(value);
} else if (options.parseBooleans && value !== null && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) {
value = value.toLowerCase() === "true";
}
return value;
}
function parse(query, options) {
options = Object.assign({
decode: true,
sort: true,
arrayFormat: "none",
arrayFormatSeparator: ",",
parseNumbers: false,
parseBooleans: false
}, options);
validateArrayFormatSeparator(options.arrayFormatSeparator);
const formatter = parserForArrayFormat(options);
const ret = /* @__PURE__ */ Object.create(null);
if (typeof query !== "string") {
return ret;
}
query = query.trim().replace(/^[?#&]/, "");
if (!query) {
return ret;
}
for (const param of query.split("&")) {
if (param === "") {
continue;
}
let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, " ") : param, "=");
value = value === void 0 ? null : ["comma", "separator", "bracket-separator"].includes(options.arrayFormat) ? value : decode(value, options);
formatter(decode(key, options), value, ret);
}
for (const key of Object.keys(ret)) {
const value = ret[key];
if (typeof value === "object" && value !== null) {
for (const k of Object.keys(value)) {
value[k] = parseValue(value[k], options);
}
} else {
ret[key] = parseValue(value, options);
}
}
if (options.sort === false) {
return ret;
}
return (options.sort === true ? Object.keys(ret).sort() : Object.keys(ret).sort(options.sort)).reduce((result, key) => {
const value = ret[key];
if (Boolean(value) && typeof value === "object" && !Array.isArray(value)) {
result[key] = keysSorter(value);
} else {
result[key] = value;
}
return result;
}, /* @__PURE__ */ Object.create(null));
}
exports.extract = extract;
exports.parse = parse;
exports.stringify = (object, options) => {
if (!object) {
return "";
}
options = Object.assign({
encode: true,
strict: true,
arrayFormat: "none",
arrayFormatSeparator: ","
}, options);
validateArrayFormatSeparator(options.arrayFormatSeparator);
const shouldFilter = (key) => options.skipNull && isNullOrUndefined(object[key]) || options.skipEmptyString && object[key] === "";
const formatter = encoderForArrayFormat(options);
const objectCopy = {};
for (const key of Object.keys(object)) {
if (!shouldFilter(key)) {
objectCopy[key] = object[key];
}
}
const keys = Object.keys(objectCopy);
if (options.sort !== false) {
keys.sort(options.sort);
}
return keys.map((key) => {
const value = object[key];
if (value === void 0) {
return "";
}
if (value === null) {
return encode(key, options);
}
if (Array.isArray(value)) {
if (value.length === 0 && options.arrayFormat === "bracket-separator") {
return encode(key, options) + "[]";
}
return value.reduce(formatter(key), []).join("&");
}
return encode(key, options) + "=" + encode(value, options);
}).filter((x) => x.length > 0).join("&");
};
exports.parseUrl = (url, options) => {
options = Object.assign({
decode: true
}, options);
const [url_, hash] = splitOnFirst(url, "#");
return Object.assign(
{
url: url_.split("?")[0] || "",
query: parse(extract(url), options)
},
options && options.parseFragmentIdentifier && hash ? { fragmentIdentifier: decode(hash, options) } : {}
);
};
exports.stringifyUrl = (object, options) => {
options = Object.assign({
encode: true,
strict: true,
[encodeFragmentIdentifier]: true
}, options);
const url = removeHash(object.url).split("?")[0] || "";
const queryFromUrl = exports.extract(object.url);
const parsedQueryFromUrl = exports.parse(queryFromUrl, { sort: false });
const query = Object.assign(parsedQueryFromUrl, object.query);
let queryString = exports.stringify(query, options);
if (queryString) {
queryString = `?${queryString}`;
}
let hash = getHash(object.url);
if (object.fragmentIdentifier) {
hash = `#${options[encodeFragmentIdentifier] ? encode(object.fragmentIdentifier, options) : object.fragmentIdentifier}`;
}
return `${url}${queryString}${hash}`;
};
exports.pick = (input, filter, options) => {
options = Object.assign({
parseFragmentIdentifier: true,
[encodeFragmentIdentifier]: false
}, options);
const { url, query, fragmentIdentifier } = exports.parseUrl(input, options);
return exports.stringifyUrl({
url,
query: filterObject(query, filter),
fragmentIdentifier
}, options);
};
exports.exclude = (input, filter, options) => {
const exclusionFilter = Array.isArray(filter) ? (key) => !filter.includes(key) : (key, value) => !filter(key, value);
return exports.pick(input, exclusionFilter, options);
};
}
});
export default require_query_string();
//# sourceMappingURL=query-string.js.map
3ɂte<>^<5E>e<EFBFBD>^<5E>F<EFBFBD>O1gՐ<67>vO^partitionKey=%28http%2Clocalhost%2C5173%29,:http://localhost:5173/node_modules/.vite/deps/query-string.js?v=e565559astrongly-framed1request-methodGETresponse-headHTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/javascript
Cache-Control: max-age=31536000,immutable
Etag: W/"43c4-MxI2NUTkbbNKbRJdi5zAv8LrAOQ"
Date: Fri, 15 Mar 2024 14:37:23 GMT
Content-Length: 17348
original-response-headersAccess-Control-Allow-Origin: *
Content-Type: application/javascript
Cache-Control: max-age=31536000,immutable
Etag: W/"43c4-MxI2NUTkbbNKbRJdi5zAv8LrAOQ"
Date: Fri, 15 Mar 2024 14:37:23 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 17348
ctid2uncompressed-len0net-response-time-onstart38net-response-time-onstop38C<>

Some files were not shown because too many files have changed in this diff Show More