Added a basic web UI
This commit is contained in:
145
client/src/App.tsx
Normal file
145
client/src/App.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { Refine } from "@refinedev/core";
|
||||
import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar";
|
||||
|
||||
import {
|
||||
ErrorComponent,
|
||||
notificationProvider,
|
||||
ThemedLayoutV2,
|
||||
ThemedSiderV2,
|
||||
ThemedTitleV2,
|
||||
} from "@refinedev/antd";
|
||||
import "@refinedev/antd/dist/reset.css";
|
||||
|
||||
import routerBindings, {
|
||||
NavigateToResource,
|
||||
UnsavedChangesNotifier,
|
||||
} from "@refinedev/react-router-v6";
|
||||
import dataProvider from "@refinedev/simple-rest";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom";
|
||||
import { AppIcon } from "./components/app-icon";
|
||||
import { Header } from "./components/header";
|
||||
import { ColorModeContextProvider } from "./contexts/color-mode";
|
||||
import { SpoolCreate, SpoolEdit, SpoolList, SpoolShow } from "./pages/spools";
|
||||
import {
|
||||
FilamentCreate,
|
||||
FilamentEdit,
|
||||
FilamentList,
|
||||
FilamentShow,
|
||||
} from "./pages/filaments";
|
||||
import {
|
||||
VendorCreate,
|
||||
VendorEdit,
|
||||
VendorList,
|
||||
VendorShow,
|
||||
} from "./pages/vendors";
|
||||
|
||||
function App() {
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const i18nProvider = {
|
||||
translate: (key: string, params: object) => t(key, params),
|
||||
changeLocale: (lang: string) => i18n.changeLanguage(lang),
|
||||
getLocale: () => i18n.language,
|
||||
};
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<RefineKbarProvider>
|
||||
<ColorModeContextProvider>
|
||||
<Refine
|
||||
dataProvider={dataProvider("/api/v1")}
|
||||
notificationProvider={notificationProvider}
|
||||
i18nProvider={i18nProvider}
|
||||
routerProvider={routerBindings}
|
||||
resources={[
|
||||
{
|
||||
name: "spool",
|
||||
list: "/spool",
|
||||
create: "/spool/create",
|
||||
edit: "/spool/edit/:id",
|
||||
show: "/spool/show/:id",
|
||||
meta: {
|
||||
canDelete: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filament",
|
||||
list: "/filament",
|
||||
create: "/filament/create",
|
||||
edit: "/filament/edit/:id",
|
||||
show: "/filament/show/:id",
|
||||
meta: {
|
||||
canDelete: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vendor",
|
||||
list: "/vendor",
|
||||
create: "/vendor/create",
|
||||
edit: "/vendor/edit/:id",
|
||||
show: "/vendor/show/:id",
|
||||
meta: {
|
||||
canDelete: true,
|
||||
},
|
||||
},
|
||||
]}
|
||||
options={{
|
||||
syncWithLocation: true,
|
||||
warnWhenUnsavedChanges: true,
|
||||
}}
|
||||
>
|
||||
<Routes>
|
||||
<Route
|
||||
element={
|
||||
<ThemedLayoutV2
|
||||
Header={() => <Header sticky />}
|
||||
Sider={() => <ThemedSiderV2 fixed />}
|
||||
Title={({ collapsed }) => (
|
||||
<ThemedTitleV2
|
||||
collapsed={collapsed}
|
||||
text="Spoolman"
|
||||
icon={<AppIcon />}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<Outlet />
|
||||
</ThemedLayoutV2>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
index
|
||||
element={<NavigateToResource resource="spool" />}
|
||||
/>
|
||||
<Route path="/spool">
|
||||
<Route index element={<SpoolList />} />
|
||||
<Route path="create" element={<SpoolCreate />} />
|
||||
<Route path="edit/:id" element={<SpoolEdit />} />
|
||||
<Route path="show/:id" element={<SpoolShow />} />
|
||||
</Route>
|
||||
<Route path="/filament">
|
||||
<Route index element={<FilamentList />} />
|
||||
<Route path="create" element={<FilamentCreate />} />
|
||||
<Route path="edit/:id" element={<FilamentEdit />} />
|
||||
<Route path="show/:id" element={<FilamentShow />} />
|
||||
</Route>
|
||||
<Route path="/vendor">
|
||||
<Route index element={<VendorList />} />
|
||||
<Route path="create" element={<VendorCreate />} />
|
||||
<Route path="edit/:id" element={<VendorEdit />} />
|
||||
<Route path="show/:id" element={<VendorShow />} />
|
||||
</Route>
|
||||
<Route path="*" element={<ErrorComponent />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
<RefineKbar />
|
||||
<UnsavedChangesNotifier />
|
||||
</Refine>
|
||||
</ColorModeContextProvider>
|
||||
</RefineKbarProvider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
19
client/src/components/app-icon/index.tsx
Normal file
19
client/src/components/app-icon/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
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 6c4.418 0 8-1.343 8-3s-3.582-3-8-3-8 1.343-8 3 3.582 3 8 3z" />
|
||||
<path d="M8 8c4.418 0 8-1.343 8-3v3c0 1.657-3.582 3-8 3S0 9.657 0 8V5c0 1.657 3.582 3 8 3z" />
|
||||
<path d="M8 13c4.418 0 8-1.343 8-3v3c0 1.657-3.582 3-8 3s-8-1.343-8-3v-3c0 1.657 3.582 3 8 3z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
98
client/src/components/header/index.tsx
Normal file
98
client/src/components/header/index.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import { DownOutlined } from "@ant-design/icons";
|
||||
import type { RefineThemedLayoutV2HeaderProps } from "@refinedev/antd";
|
||||
import { useGetIdentity, useGetLocale, useSetLocale } from "@refinedev/core";
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Dropdown,
|
||||
Layout as AntdLayout,
|
||||
MenuProps,
|
||||
Space,
|
||||
Switch,
|
||||
theme,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import React, { useContext } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ColorModeContext } from "../../contexts/color-mode";
|
||||
|
||||
const { Text } = Typography;
|
||||
const { useToken } = theme;
|
||||
|
||||
type IUser = {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
||||
sticky,
|
||||
}) => {
|
||||
const { token } = useToken();
|
||||
const { i18n } = useTranslation();
|
||||
const locale = useGetLocale();
|
||||
const changeLanguage = useSetLocale();
|
||||
const { data: user } = useGetIdentity<IUser>();
|
||||
const { mode, setMode } = useContext(ColorModeContext);
|
||||
|
||||
const currentLocale = locale();
|
||||
|
||||
const menuItems: MenuProps["items"] = [...(i18n.languages || [])]
|
||||
.sort()
|
||||
.map((lang: string) => ({
|
||||
key: lang,
|
||||
onClick: () => changeLanguage(lang),
|
||||
icon: (
|
||||
<span style={{ marginRight: 8 }}>
|
||||
<Avatar size={16} src={`/images/flags/${lang}.svg`} />
|
||||
</span>
|
||||
),
|
||||
label: lang === "en" ? "English" : "German",
|
||||
}));
|
||||
|
||||
const headerStyles: React.CSSProperties = {
|
||||
backgroundColor: token.colorBgElevated,
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
padding: "0px 24px",
|
||||
height: "64px",
|
||||
};
|
||||
|
||||
if (sticky) {
|
||||
headerStyles.position = "sticky";
|
||||
headerStyles.top = 0;
|
||||
headerStyles.zIndex = 1;
|
||||
}
|
||||
|
||||
return (
|
||||
<AntdLayout.Header style={headerStyles}>
|
||||
<Space>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: menuItems,
|
||||
selectedKeys: currentLocale ? [currentLocale] : [],
|
||||
}}
|
||||
>
|
||||
<Button type="text">
|
||||
<Space>
|
||||
<Avatar size={16} src={`/images/flags/${currentLocale}.svg`} />
|
||||
{currentLocale === "en" ? "English" : "German"}
|
||||
<DownOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Switch
|
||||
checkedChildren="🌛"
|
||||
unCheckedChildren="🔆"
|
||||
onChange={() => setMode(mode === "light" ? "dark" : "light")}
|
||||
defaultChecked={mode === "dark"}
|
||||
/>
|
||||
<Space style={{ marginLeft: "8px" }} size="middle">
|
||||
{user?.name && <Text strong>{user.name}</Text>}
|
||||
{user?.avatar && <Avatar src={user?.avatar} alt={user?.name} />}
|
||||
</Space>
|
||||
</Space>
|
||||
</AntdLayout.Header>
|
||||
);
|
||||
};
|
||||
1
client/src/components/index.ts
Normal file
1
client/src/components/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { Header } from "./header";
|
||||
59
client/src/contexts/color-mode/index.tsx
Normal file
59
client/src/contexts/color-mode/index.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { RefineThemes } from "@refinedev/antd";
|
||||
import { ConfigProvider, theme } from "antd";
|
||||
import { createContext, PropsWithChildren, useEffect, useState } from "react";
|
||||
|
||||
type ColorModeContextType = {
|
||||
mode: string;
|
||||
setMode: (mode: string) => 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");
|
||||
}
|
||||
};
|
||||
|
||||
const { darkAlgorithm, defaultAlgorithm } = theme;
|
||||
|
||||
return (
|
||||
<ColorModeContext.Provider
|
||||
value={{
|
||||
setMode: setColorMode,
|
||||
mode,
|
||||
}}
|
||||
>
|
||||
<ConfigProvider
|
||||
// you can change the theme colors here. example: ...RefineThemes.Magenta,
|
||||
theme={{
|
||||
...RefineThemes.Yellow,
|
||||
algorithm: mode === "light" ? defaultAlgorithm : darkAlgorithm,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ConfigProvider>
|
||||
</ColorModeContext.Provider>
|
||||
);
|
||||
};
|
||||
19
client/src/i18n.ts
Normal file
19
client/src/i18n.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import i18n from "i18next";
|
||||
import detector from "i18next-browser-languagedetector";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(detector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
supportedLngs: ["en"],
|
||||
backend: {
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
||||
},
|
||||
defaultNS: "common",
|
||||
fallbackLng: ["en"],
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
16
client/src/index.tsx
Normal file
16
client/src/index.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
import App from "./App";
|
||||
import "./i18n";
|
||||
|
||||
const container = document.getElementById("root") as HTMLElement;
|
||||
const root = createRoot(container);
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<React.Suspense fallback="loading">
|
||||
<App />
|
||||
</React.Suspense>
|
||||
</React.StrictMode>
|
||||
);
|
||||
133
client/src/pages/filaments/create.tsx
Normal file
133
client/src/pages/filaments/create.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps } from "@refinedev/core";
|
||||
import { Create, useForm } from "@refinedev/antd";
|
||||
import { Form, Input, DatePicker, Select, InputNumber } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
export const FilamentCreate: React.FC<IResourceComponentsProps> = () => {
|
||||
const { formProps, saveButtonProps, queryResult } = useForm();
|
||||
|
||||
return (
|
||||
<Create saveButtonProps={saveButtonProps}>
|
||||
<Form {...formProps} layout="vertical">
|
||||
<Form.Item
|
||||
label="Name"
|
||||
help="Filament name, to distinguish this filament type among others from the same vendor. Should contain the color for example."
|
||||
name={["name"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Vendor"
|
||||
name={["vendor", "name"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{/* <Select {...vendorSelectProps} /> */}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Material"
|
||||
help="E.g. PLA, ABS, PETG, etc."
|
||||
name={["material"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Price"
|
||||
help="Price of a full spool in the system configured currency."
|
||||
name={["price"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Density"
|
||||
name={["density"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="g/cm³" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Diameter"
|
||||
name={["diameter"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="mm" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Weight"
|
||||
help="The filament weight of a full spool (net weight)."
|
||||
name={["weight"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="g" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Spool Weight"
|
||||
help="The weight of an empty spool."
|
||||
name={["spool_weight"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="g" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Article Number"
|
||||
name={["article_number"]}
|
||||
help="E.g. EAN, UPC, etc."
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Comment"
|
||||
name={["comment"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextArea maxLength={1024} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Create>
|
||||
);
|
||||
};
|
||||
160
client/src/pages/filaments/edit.tsx
Normal file
160
client/src/pages/filaments/edit.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps } from "@refinedev/core";
|
||||
import { Edit, useForm } from "@refinedev/antd";
|
||||
import { Form, Input, DatePicker, Select, InputNumber } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
|
||||
const { formProps, saveButtonProps, queryResult } = useForm();
|
||||
|
||||
const filamentData = queryResult?.data?.data;
|
||||
|
||||
return (
|
||||
<Edit saveButtonProps={saveButtonProps}>
|
||||
<Form {...formProps} layout="vertical">
|
||||
<Form.Item
|
||||
label="Id"
|
||||
name={["id"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input readOnly disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Registered"
|
||||
name={["registered"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
>
|
||||
<DatePicker disabled format="lll" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Name"
|
||||
help="Filament name, to distinguish this filament type among others from the same vendor. Should contain the color for example."
|
||||
name={["name"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Vendor"
|
||||
name={["vendor", "name"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{/* <Select {...vendorSelectProps} /> */}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Material"
|
||||
help="E.g. PLA, ABS, PETG, etc."
|
||||
name={["material"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Price"
|
||||
help="Price of a full spool in the system configured currency."
|
||||
name={["price"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Density"
|
||||
name={["density"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="g/cm³" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Diameter"
|
||||
name={["diameter"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="mm" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Weight"
|
||||
help="The filament weight of a full spool (net weight)."
|
||||
name={["weight"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="g" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Spool Weight"
|
||||
help="The weight of an empty spool."
|
||||
name={["spool_weight"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="g" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Article Number"
|
||||
name={["article_number"]}
|
||||
help="E.g. EAN, UPC, etc."
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Comment"
|
||||
name={["comment"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextArea maxLength={1024} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Edit>
|
||||
);
|
||||
};
|
||||
4
client/src/pages/filaments/index.ts
Normal file
4
client/src/pages/filaments/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { FilamentCreate } from "./create";
|
||||
export { FilamentEdit } from "./edit";
|
||||
export { FilamentList } from "./list";
|
||||
export { FilamentShow } from "./show";
|
||||
106
client/src/pages/filaments/list.tsx
Normal file
106
client/src/pages/filaments/list.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps, BaseRecord } from "@refinedev/core";
|
||||
import {
|
||||
useTable,
|
||||
List,
|
||||
EditButton,
|
||||
ShowButton,
|
||||
DateField,
|
||||
NumberField,
|
||||
} from "@refinedev/antd";
|
||||
import { Table, Space } from "antd";
|
||||
|
||||
export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
||||
const { tableProps } = useTable({
|
||||
syncWithLocation: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<List>
|
||||
<Table {...tableProps} rowKey="id">
|
||||
<Table.Column dataIndex="id" title="Id" />
|
||||
<Table.Column dataIndex={["vendor", "name"]} title="Vendor" />
|
||||
<Table.Column dataIndex="name" title="Name" />
|
||||
<Table.Column dataIndex="material" title="Material" />
|
||||
<Table.Column dataIndex="price" title="Price" />
|
||||
<Table.Column
|
||||
dataIndex="density"
|
||||
title="Density"
|
||||
render={(value: any) => (
|
||||
<NumberField
|
||||
value={value}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 2,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="diameter"
|
||||
title="Diameter"
|
||||
render={(value: any) => (
|
||||
<NumberField
|
||||
value={value}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "millimeter",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 2,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="weight"
|
||||
title="Weight"
|
||||
render={(value: any) => (
|
||||
<NumberField
|
||||
value={value}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 1,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="spool_weight"
|
||||
title="Spool Weight"
|
||||
render={(value: any) => (
|
||||
<NumberField
|
||||
value={value}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 1,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Table.Column dataIndex="article_number" title="Article Number" />
|
||||
<Table.Column
|
||||
dataIndex={["registered"]}
|
||||
title="Registered"
|
||||
render={(value: any) => <DateField value={value} />}
|
||||
/>
|
||||
<Table.Column dataIndex={["comment"]} title="Comment" />
|
||||
<Table.Column
|
||||
title="Actions"
|
||||
dataIndex="actions"
|
||||
render={(_, record: BaseRecord) => (
|
||||
<Space>
|
||||
<EditButton hideText size="small" recordItemId={record.id} />
|
||||
<ShowButton hideText size="small" recordItemId={record.id} />
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
80
client/src/pages/filaments/show.tsx
Normal file
80
client/src/pages/filaments/show.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps, useShow } from "@refinedev/core";
|
||||
import {
|
||||
Show,
|
||||
NumberField,
|
||||
DateField,
|
||||
TagField,
|
||||
TextField,
|
||||
} from "@refinedev/antd";
|
||||
import { Typography } from "antd";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
||||
const { queryResult } = useShow();
|
||||
const { data, isLoading } = queryResult;
|
||||
|
||||
const record = data?.data;
|
||||
|
||||
return (
|
||||
<Show isLoading={isLoading}>
|
||||
<Title level={5}>Id</Title>
|
||||
<NumberField value={record?.id ?? ""} />
|
||||
<Title level={5}>Registered</Title>
|
||||
<DateField value={record?.registered} format="lll" />
|
||||
<Title level={5}>Name</Title>
|
||||
<TextField value={record?.name} />
|
||||
<Title level={5}>Vendor</Title>
|
||||
{/* {vendorIsLoading ? <>Loading...</> : <>{vendorData?.data?.id}</>} */}
|
||||
<Title level={5}>Material</Title>
|
||||
<TextField value={record?.material} />
|
||||
<Title level={5}>Price</Title>
|
||||
<NumberField value={record?.price ?? ""} />
|
||||
<Title level={5}>Density</Title>
|
||||
<NumberField
|
||||
value={record?.density ?? ""}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 2,
|
||||
}}
|
||||
/>
|
||||
<Title level={5}>Diameter</Title>
|
||||
<NumberField
|
||||
value={record?.diameter ?? ""}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "millimeter",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 2,
|
||||
}}
|
||||
/>
|
||||
<Title level={5}>Weight</Title>
|
||||
<NumberField
|
||||
value={record?.weight ?? ""}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 1,
|
||||
}}
|
||||
/>
|
||||
<Title level={5}>Spool Weight</Title>
|
||||
<NumberField
|
||||
value={record?.spool_weight ?? ""}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 1,
|
||||
}}
|
||||
/>
|
||||
<Title level={5}>Article Number</Title>
|
||||
<TextField value={record?.article_number} />
|
||||
<Title level={5}>Comment</Title>
|
||||
<TextField value={record?.comment} />
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
100
client/src/pages/spools/create.tsx
Normal file
100
client/src/pages/spools/create.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps } from "@refinedev/core";
|
||||
import { Create, useForm } from "@refinedev/antd";
|
||||
import { Form, Input, DatePicker, Select, InputNumber } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
export const SpoolCreate: React.FC<IResourceComponentsProps> = () => {
|
||||
const { formProps, saveButtonProps, queryResult } = useForm();
|
||||
|
||||
return (
|
||||
<Create saveButtonProps={saveButtonProps}>
|
||||
<Form {...formProps} layout="vertical">
|
||||
<Form.Item
|
||||
label="First Used"
|
||||
name={["first_used"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
>
|
||||
<DatePicker format="lll" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Last Used"
|
||||
name={["last_used"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
>
|
||||
<DatePicker format="lll" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Filament"
|
||||
name={["filament", "name"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{/* <Select {...filamentSelectProps} /> */}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Used Weight"
|
||||
name={["used_weight"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} addonAfter="g" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Location"
|
||||
name={["location"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Lot Nr"
|
||||
name={["lot_nr"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Comment"
|
||||
name={["comment"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextArea maxLength={1024} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Create>
|
||||
);
|
||||
};
|
||||
127
client/src/pages/spools/edit.tsx
Normal file
127
client/src/pages/spools/edit.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps } from "@refinedev/core";
|
||||
import { Edit, useForm } from "@refinedev/antd";
|
||||
import { Form, Input, DatePicker, Select, InputNumber } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
export const SpoolEdit: React.FC<IResourceComponentsProps> = () => {
|
||||
const { formProps, saveButtonProps, queryResult } = useForm();
|
||||
|
||||
const spoolData = queryResult?.data?.data;
|
||||
|
||||
return (
|
||||
<Edit saveButtonProps={saveButtonProps}>
|
||||
<Form {...formProps} layout="vertical">
|
||||
<Form.Item
|
||||
label="Id"
|
||||
name={["id"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input readOnly disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Registered"
|
||||
name={["registered"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
>
|
||||
<DatePicker disabled format="lll" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="First Used"
|
||||
name={["first_used"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
>
|
||||
<DatePicker format="lll" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Last Used"
|
||||
name={["last_used"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
>
|
||||
<DatePicker format="lll" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Filament"
|
||||
name={["filament", "name"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{/* <Select {...filamentSelectProps} /> */}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Used Weight"
|
||||
name={["used_weight"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input min={0} addonAfter="g" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Location"
|
||||
name={["location"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Lot Nr"
|
||||
name={["lot_nr"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Comment"
|
||||
name={["comment"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextArea maxLength={1024} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Edit>
|
||||
);
|
||||
};
|
||||
4
client/src/pages/spools/index.ts
Normal file
4
client/src/pages/spools/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { SpoolCreate } from "./create";
|
||||
export { SpoolEdit } from "./edit";
|
||||
export { SpoolList } from "./list";
|
||||
export { SpoolShow } from "./show";
|
||||
68
client/src/pages/spools/list.tsx
Normal file
68
client/src/pages/spools/list.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps, BaseRecord } from "@refinedev/core";
|
||||
import {
|
||||
useTable,
|
||||
List,
|
||||
EditButton,
|
||||
ShowButton,
|
||||
DateField,
|
||||
NumberField,
|
||||
} from "@refinedev/antd";
|
||||
import { Table, Space } from "antd";
|
||||
|
||||
export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
const { tableProps } = useTable({
|
||||
syncWithLocation: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<List>
|
||||
<Table {...tableProps} rowKey="id">
|
||||
<Table.Column dataIndex="id" title="Id" />
|
||||
<Table.Column
|
||||
dataIndex={["filament", "vendor", "name"]}
|
||||
title="Vendor"
|
||||
/>
|
||||
<Table.Column dataIndex={["filament", "name"]} title="Filament Name" />
|
||||
<Table.Column
|
||||
dataIndex="remaining_weight"
|
||||
title="Estimated Remaining Weight"
|
||||
render={(value: any) => (
|
||||
<NumberField
|
||||
value={value}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 1,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex={["first_used"]}
|
||||
title="First Used"
|
||||
render={(value: any) => <DateField value={value} format="lll" />}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex={["last_used"]}
|
||||
title="Last Used"
|
||||
render={(value: any) => <DateField value={value} format="lll" />}
|
||||
/>
|
||||
<Table.Column dataIndex="location" title="Location" />
|
||||
<Table.Column dataIndex="lot_nr" title="Lot Nr" />
|
||||
<Table.Column dataIndex={["comment"]} title="Comment" />
|
||||
<Table.Column
|
||||
title="Actions"
|
||||
dataIndex="actions"
|
||||
render={(_, record: BaseRecord) => (
|
||||
<Space>
|
||||
<EditButton hideText size="small" recordItemId={record.id} />
|
||||
<ShowButton hideText size="small" recordItemId={record.id} />
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
64
client/src/pages/spools/show.tsx
Normal file
64
client/src/pages/spools/show.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps, useShow } from "@refinedev/core";
|
||||
import {
|
||||
Show,
|
||||
NumberField,
|
||||
DateField,
|
||||
TagField,
|
||||
TextField,
|
||||
} from "@refinedev/antd";
|
||||
import { Typography } from "antd";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
|
||||
const { queryResult } = useShow();
|
||||
const { data, isLoading } = queryResult;
|
||||
|
||||
const record = data?.data;
|
||||
|
||||
return (
|
||||
<Show isLoading={isLoading}>
|
||||
<Title level={5}>Id</Title>
|
||||
<NumberField value={record?.id ?? ""} />
|
||||
<Title level={5}>Registered</Title>
|
||||
<DateField value={record?.registered} format="lll" />
|
||||
<Title level={5}>First Used</Title>
|
||||
<DateField value={record?.first_used} format="lll" />
|
||||
<Title level={5}>Last Used</Title>
|
||||
<DateField value={record?.last_used} format="lll" />
|
||||
<Title level={5}>Filament</Title>
|
||||
{/* {filamentIsLoading ? (
|
||||
<>Loading...</>
|
||||
) : (
|
||||
<>{filamentData?.data?.id}</>
|
||||
)} */}
|
||||
<Title level={5}>Remaining Weight</Title>
|
||||
<NumberField
|
||||
value={record?.remaining_weight ?? ""}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 1,
|
||||
}}
|
||||
/>
|
||||
<Title level={5}>Used Weight</Title>
|
||||
<NumberField
|
||||
value={record?.used_weight ?? ""}
|
||||
options={{
|
||||
unitDisplay: "short",
|
||||
unit: "gram",
|
||||
style: "unit",
|
||||
maximumFractionDigits: 1,
|
||||
}}
|
||||
/>
|
||||
<Title level={5}>Location</Title>
|
||||
<TextField value={record?.location} />
|
||||
<Title level={5}>Lot Nr</Title>
|
||||
<TextField value={record?.lot_nr} />
|
||||
<Title level={5}>Comment</Title>
|
||||
<TextField value={record?.comment} />
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
39
client/src/pages/vendors/create.tsx
vendored
Normal file
39
client/src/pages/vendors/create.tsx
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps } from "@refinedev/core";
|
||||
import { Create, useForm } from "@refinedev/antd";
|
||||
import { Form, Input, DatePicker } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
export const VendorCreate: React.FC<IResourceComponentsProps> = () => {
|
||||
const { formProps, saveButtonProps, queryResult } = useForm();
|
||||
|
||||
return (
|
||||
<Create saveButtonProps={saveButtonProps}>
|
||||
<Form {...formProps} layout="vertical">
|
||||
<Form.Item
|
||||
label="Name"
|
||||
name={["name"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Comment"
|
||||
name={["comment"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextArea maxLength={1024} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Create>
|
||||
);
|
||||
};
|
||||
66
client/src/pages/vendors/edit.tsx
vendored
Normal file
66
client/src/pages/vendors/edit.tsx
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps } from "@refinedev/core";
|
||||
import { Edit, useForm } from "@refinedev/antd";
|
||||
import { Form, Input, DatePicker } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
export const VendorEdit: React.FC<IResourceComponentsProps> = () => {
|
||||
const { formProps, saveButtonProps, queryResult } = useForm();
|
||||
|
||||
const vendorData = queryResult?.data?.data;
|
||||
|
||||
return (
|
||||
<Edit saveButtonProps={saveButtonProps}>
|
||||
<Form {...formProps} layout="vertical">
|
||||
<Form.Item
|
||||
label="Id"
|
||||
name={["id"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input readOnly disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Registered"
|
||||
name={["registered"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? dayjs(value) : undefined,
|
||||
})}
|
||||
>
|
||||
<DatePicker disabled format="lll" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Name"
|
||||
name={["name"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input maxLength={64} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Comment"
|
||||
name={["comment"]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextArea maxLength={1024} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Edit>
|
||||
);
|
||||
};
|
||||
4
client/src/pages/vendors/index.ts
vendored
Normal file
4
client/src/pages/vendors/index.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export { VendorCreate } from "./create";
|
||||
export { VendorEdit } from "./edit";
|
||||
export { VendorList } from "./list";
|
||||
export { VendorShow } from "./show";
|
||||
41
client/src/pages/vendors/list.tsx
vendored
Normal file
41
client/src/pages/vendors/list.tsx
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps, BaseRecord } from "@refinedev/core";
|
||||
import {
|
||||
useTable,
|
||||
List,
|
||||
EditButton,
|
||||
ShowButton,
|
||||
DateField,
|
||||
} from "@refinedev/antd";
|
||||
import { Table, Space } from "antd";
|
||||
|
||||
export const VendorList: React.FC<IResourceComponentsProps> = () => {
|
||||
const { tableProps } = useTable({
|
||||
syncWithLocation: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<List>
|
||||
<Table {...tableProps} rowKey="id">
|
||||
<Table.Column dataIndex="id" title="Id" />
|
||||
<Table.Column dataIndex="name" title="Name" />
|
||||
<Table.Column
|
||||
dataIndex={["registered"]}
|
||||
title="Registered"
|
||||
render={(value: any) => <DateField value={value} format="lll" />}
|
||||
/>
|
||||
<Table.Column dataIndex={["comment"]} title="Comment" />
|
||||
<Table.Column
|
||||
title="Actions"
|
||||
dataIndex="actions"
|
||||
render={(_, record: BaseRecord) => (
|
||||
<Space>
|
||||
<EditButton hideText size="small" recordItemId={record.id} />
|
||||
<ShowButton hideText size="small" recordItemId={record.id} />
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
32
client/src/pages/vendors/show.tsx
vendored
Normal file
32
client/src/pages/vendors/show.tsx
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps, useShow } from "@refinedev/core";
|
||||
import {
|
||||
Show,
|
||||
NumberField,
|
||||
DateField,
|
||||
TagField,
|
||||
TextField,
|
||||
} from "@refinedev/antd";
|
||||
import { Typography } from "antd";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export const VendorShow: React.FC<IResourceComponentsProps> = () => {
|
||||
const { queryResult } = useShow();
|
||||
const { data, isLoading } = queryResult;
|
||||
|
||||
const record = data?.data;
|
||||
|
||||
return (
|
||||
<Show isLoading={isLoading}>
|
||||
<Title level={5}>Id</Title>
|
||||
<NumberField value={record?.id ?? ""} />
|
||||
<Title level={5}>Registered</Title>
|
||||
<DateField value={record?.registered} format="lll" />
|
||||
<Title level={5}>Name</Title>
|
||||
<TextField value={record?.name} />
|
||||
<Title level={5}>Comment</Title>
|
||||
<TextField value={record?.comment} />
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
1
client/src/vite-env.d.ts
vendored
Normal file
1
client/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user