1st Version
This commit is contained in:
119
vrpmdvfrontend/src/pages/blog-posts/create.tsx
Normal file
119
vrpmdvfrontend/src/pages/blog-posts/create.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
124
vrpmdvfrontend/src/pages/blog-posts/edit.tsx
Normal file
124
vrpmdvfrontend/src/pages/blog-posts/edit.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
4
vrpmdvfrontend/src/pages/blog-posts/index.ts
Normal file
4
vrpmdvfrontend/src/pages/blog-posts/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./create";
|
||||
export * from "./edit";
|
||||
export * from "./list";
|
||||
export * from "./show";
|
||||
112
vrpmdvfrontend/src/pages/blog-posts/list.tsx
Normal file
112
vrpmdvfrontend/src/pages/blog-posts/list.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
59
vrpmdvfrontend/src/pages/blog-posts/show.tsx
Normal file
59
vrpmdvfrontend/src/pages/blog-posts/show.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
36
vrpmdvfrontend/src/pages/categories/create.tsx
Normal file
36
vrpmdvfrontend/src/pages/categories/create.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
35
vrpmdvfrontend/src/pages/categories/edit.tsx
Normal file
35
vrpmdvfrontend/src/pages/categories/edit.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
4
vrpmdvfrontend/src/pages/categories/index.ts
Normal file
4
vrpmdvfrontend/src/pages/categories/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./create";
|
||||
export * from "./edit";
|
||||
export * from "./list";
|
||||
export * from "./show";
|
||||
58
vrpmdvfrontend/src/pages/categories/list.tsx
Normal file
58
vrpmdvfrontend/src/pages/categories/list.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
29
vrpmdvfrontend/src/pages/categories/show.tsx
Normal file
29
vrpmdvfrontend/src/pages/categories/show.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
17
vrpmdvfrontend/src/pages/forgotPassword/index.tsx
Normal file
17
vrpmdvfrontend/src/pages/forgotPassword/index.tsx
Normal 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 />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
20
vrpmdvfrontend/src/pages/login/index.tsx
Normal file
20
vrpmdvfrontend/src/pages/login/index.tsx
Normal 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" },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
179
vrpmdvfrontend/src/pages/monitorings/create.tsx
Normal file
179
vrpmdvfrontend/src/pages/monitorings/create.tsx
Normal 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
|
||||
// />
|
||||
// )}
|
||||
// />
|
||||
// )}
|
||||
// />
|
||||
163
vrpmdvfrontend/src/pages/monitorings/edit.tsx
Normal file
163
vrpmdvfrontend/src/pages/monitorings/edit.tsx
Normal 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
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/> }
|
||||
*/
|
||||
4
vrpmdvfrontend/src/pages/monitorings/index.ts
Normal file
4
vrpmdvfrontend/src/pages/monitorings/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./create";
|
||||
export * from "./edit";
|
||||
export * from "./list";
|
||||
export * from "./show";
|
||||
147
vrpmdvfrontend/src/pages/monitorings/list.tsx
Normal file
147
vrpmdvfrontend/src/pages/monitorings/list.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
12
vrpmdvfrontend/src/pages/monitorings/monitorings.types.ts
Normal file
12
vrpmdvfrontend/src/pages/monitorings/monitorings.types.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
export interface IMonitoring {
|
||||
id: string;
|
||||
created_at: string;
|
||||
name: string;
|
||||
samplerate: number;
|
||||
sampleperiod: number;
|
||||
downtime: number;
|
||||
owner: string;
|
||||
}
|
||||
|
||||
59
vrpmdvfrontend/src/pages/monitorings/show.tsx
Normal file
59
vrpmdvfrontend/src/pages/monitorings/show.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
17
vrpmdvfrontend/src/pages/register/index.tsx
Normal file
17
vrpmdvfrontend/src/pages/register/index.tsx
Normal 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 />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user