~ format project files
using prettier
local configuration
This commit is contained in:
parent
266c7fa070
commit
80d7e52b32
110
src/App.tsx
110
src/App.tsx
@ -1,48 +1,22 @@
|
|||||||
import {
|
import {Refine, GitHubBanner, WelcomePage, Authenticated} from '@refinedev/core'
|
||||||
Refine,
|
import {DevtoolsPanel, DevtoolsProvider} from '@refinedev/devtools'
|
||||||
GitHubBanner,
|
import {RefineKbar, RefineKbarProvider} from '@refinedev/kbar'
|
||||||
WelcomePage,
|
|
||||||
Authenticated,
|
|
||||||
} from "@refinedev/core";
|
|
||||||
import { DevtoolsPanel, DevtoolsProvider } from "@refinedev/devtools";
|
|
||||||
import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar";
|
|
||||||
|
|
||||||
import {
|
import {AuthPage, ErrorComponent, useNotificationProvider, RefineSnackbarProvider, ThemedLayoutV2} from '@refinedev/mui'
|
||||||
AuthPage,
|
|
||||||
ErrorComponent,
|
|
||||||
useNotificationProvider,
|
|
||||||
RefineSnackbarProvider,
|
|
||||||
ThemedLayoutV2,
|
|
||||||
} from "@refinedev/mui";
|
|
||||||
|
|
||||||
import dataProvider from "@refinedev/simple-rest";
|
import dataProvider from '@refinedev/simple-rest'
|
||||||
import CssBaseline from "@mui/material/CssBaseline";
|
import CssBaseline from '@mui/material/CssBaseline'
|
||||||
import GlobalStyles from "@mui/material/GlobalStyles";
|
import GlobalStyles from '@mui/material/GlobalStyles'
|
||||||
import { BrowserRouter, Route, Routes, Outlet } from "react-router";
|
import {BrowserRouter, Route, Routes, Outlet} from 'react-router'
|
||||||
import routerBindings, {
|
import routerBindings, {NavigateToResource, CatchAllNavigate, UnsavedChangesNotifier, DocumentTitleHandler} from '@refinedev/react-router'
|
||||||
NavigateToResource,
|
import {BlogPostList, BlogPostCreate, BlogPostEdit, BlogPostShow} from './pages/blog-posts'
|
||||||
CatchAllNavigate,
|
import {CategoryList, CategoryCreate, CategoryEdit, CategoryShow} from './pages/categories'
|
||||||
UnsavedChangesNotifier,
|
import {ColorModeContextProvider} from './contexts/color-mode'
|
||||||
DocumentTitleHandler,
|
import {Header} from './components/header'
|
||||||
} from "@refinedev/react-router";
|
import {Login} from './pages/login'
|
||||||
import {
|
import {Register} from './pages/register'
|
||||||
BlogPostList,
|
import {ForgotPassword} from './pages/forgotPassword'
|
||||||
BlogPostCreate,
|
import {authProvider} from './authProvider'
|
||||||
BlogPostEdit,
|
|
||||||
BlogPostShow,
|
|
||||||
} from "./pages/blog-posts";
|
|
||||||
import {
|
|
||||||
CategoryList,
|
|
||||||
CategoryCreate,
|
|
||||||
CategoryEdit,
|
|
||||||
CategoryShow,
|
|
||||||
} from "./pages/categories";
|
|
||||||
import { ColorModeContextProvider } from "./contexts/color-mode";
|
|
||||||
import { Header } from "./components/header";
|
|
||||||
import { Login } from "./pages/login";
|
|
||||||
import { Register } from "./pages/register";
|
|
||||||
import { ForgotPassword } from "./pages/forgotPassword";
|
|
||||||
import { authProvider } from "./authProvider";
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@ -51,31 +25,31 @@ function App() {
|
|||||||
<RefineKbarProvider>
|
<RefineKbarProvider>
|
||||||
<ColorModeContextProvider>
|
<ColorModeContextProvider>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
|
<GlobalStyles styles={{html: {WebkitFontSmoothing: 'auto'}}} />
|
||||||
<RefineSnackbarProvider>
|
<RefineSnackbarProvider>
|
||||||
<DevtoolsProvider>
|
<DevtoolsProvider>
|
||||||
<Refine
|
<Refine
|
||||||
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
|
dataProvider={dataProvider('https://api.fake-rest.refine.dev')}
|
||||||
notificationProvider={useNotificationProvider}
|
notificationProvider={useNotificationProvider}
|
||||||
routerProvider={routerBindings}
|
routerProvider={routerBindings}
|
||||||
authProvider={authProvider}
|
authProvider={authProvider}
|
||||||
resources={[
|
resources={[
|
||||||
{
|
{
|
||||||
name: "blog_posts",
|
name: 'blog_posts',
|
||||||
list: "/blog-posts",
|
list: '/blog-posts',
|
||||||
create: "/blog-posts/create",
|
create: '/blog-posts/create',
|
||||||
edit: "/blog-posts/edit/:id",
|
edit: '/blog-posts/edit/:id',
|
||||||
show: "/blog-posts/show/:id",
|
show: '/blog-posts/show/:id',
|
||||||
meta: {
|
meta: {
|
||||||
canDelete: true,
|
canDelete: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "categories",
|
name: 'categories',
|
||||||
list: "/categories",
|
list: '/categories',
|
||||||
create: "/categories/create",
|
create: '/categories/create',
|
||||||
edit: "/categories/edit/:id",
|
edit: '/categories/edit/:id',
|
||||||
show: "/categories/show/:id",
|
show: '/categories/show/:id',
|
||||||
meta: {
|
meta: {
|
||||||
canDelete: true,
|
canDelete: true,
|
||||||
},
|
},
|
||||||
@ -85,26 +59,20 @@ function App() {
|
|||||||
syncWithLocation: true,
|
syncWithLocation: true,
|
||||||
warnWhenUnsavedChanges: true,
|
warnWhenUnsavedChanges: true,
|
||||||
useNewQueryKeys: true,
|
useNewQueryKeys: true,
|
||||||
projectId: "Wv044J-t53S3s-PcbJGe",
|
projectId: 'Wv044J-t53S3s-PcbJGe',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
element={
|
element={
|
||||||
<Authenticated
|
<Authenticated key="authenticated-inner" fallback={<CatchAllNavigate to="/login" />}>
|
||||||
key="authenticated-inner"
|
|
||||||
fallback={<CatchAllNavigate to="/login" />}
|
|
||||||
>
|
|
||||||
<ThemedLayoutV2 Header={Header}>
|
<ThemedLayoutV2 Header={Header}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</ThemedLayoutV2>
|
</ThemedLayoutV2>
|
||||||
</Authenticated>
|
</Authenticated>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route
|
<Route index element={<NavigateToResource resource="blog_posts" />} />
|
||||||
index
|
|
||||||
element={<NavigateToResource resource="blog_posts" />}
|
|
||||||
/>
|
|
||||||
<Route path="/blog-posts">
|
<Route path="/blog-posts">
|
||||||
<Route index element={<BlogPostList />} />
|
<Route index element={<BlogPostList />} />
|
||||||
<Route path="create" element={<BlogPostCreate />} />
|
<Route path="create" element={<BlogPostCreate />} />
|
||||||
@ -121,20 +89,14 @@ function App() {
|
|||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
element={
|
element={
|
||||||
<Authenticated
|
<Authenticated key="authenticated-outer" fallback={<Outlet />}>
|
||||||
key="authenticated-outer"
|
|
||||||
fallback={<Outlet />}
|
|
||||||
>
|
|
||||||
<NavigateToResource />
|
<NavigateToResource />
|
||||||
</Authenticated>
|
</Authenticated>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/register" element={<Register />} />
|
<Route path="/register" element={<Register />} />
|
||||||
<Route
|
<Route path="/forgot-password" element={<ForgotPassword />} />
|
||||||
path="/forgot-password"
|
|
||||||
element={<ForgotPassword />}
|
|
||||||
/>
|
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
||||||
@ -148,7 +110,7 @@ function App() {
|
|||||||
</ColorModeContextProvider>
|
</ColorModeContextProvider>
|
||||||
</RefineKbarProvider>
|
</RefineKbarProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
import type { AuthProvider } from "@refinedev/core";
|
import type {AuthProvider} from '@refinedev/core'
|
||||||
|
|
||||||
export const TOKEN_KEY = "refine-auth";
|
export const TOKEN_KEY = 'refine-auth'
|
||||||
|
|
||||||
export const authProvider: AuthProvider = {
|
export const authProvider: AuthProvider = {
|
||||||
login: async ({ username, email, password }) => {
|
login: async ({username, email, password}) => {
|
||||||
if ((username || email) && password) {
|
if ((username || email) && password) {
|
||||||
localStorage.setItem(TOKEN_KEY, username);
|
localStorage.setItem(TOKEN_KEY, username)
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
redirectTo: "/",
|
redirectTo: '/',
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
name: "LoginError",
|
name: 'LoginError',
|
||||||
message: "Invalid username or password",
|
message: 'Invalid username or password',
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
logout: async () => {
|
logout: async () => {
|
||||||
localStorage.removeItem(TOKEN_KEY);
|
localStorage.removeItem(TOKEN_KEY)
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
redirectTo: "/login",
|
redirectTo: '/login',
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
check: async () => {
|
check: async () => {
|
||||||
const token = localStorage.getItem(TOKEN_KEY);
|
const token = localStorage.getItem(TOKEN_KEY)
|
||||||
if (token) {
|
if (token) {
|
||||||
return {
|
return {
|
||||||
authenticated: true,
|
authenticated: true,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
redirectTo: "/login",
|
redirectTo: '/login',
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
getPermissions: async () => null,
|
getPermissions: async () => null,
|
||||||
getIdentity: async () => {
|
getIdentity: async () => {
|
||||||
const token = localStorage.getItem(TOKEN_KEY);
|
const token = localStorage.getItem(TOKEN_KEY)
|
||||||
if (token) {
|
if (token) {
|
||||||
return {
|
return {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "John Doe",
|
name: 'John Doe',
|
||||||
avatar: "https://i.pravatar.cc/300",
|
avatar: 'https://i.pravatar.cc/300',
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
},
|
},
|
||||||
onError: async (error) => {
|
onError: async (error) => {
|
||||||
console.error(error);
|
console.error(error)
|
||||||
return { error };
|
return {error}
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
@ -1,67 +1,50 @@
|
|||||||
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
|
import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined'
|
||||||
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
|
import LightModeOutlined from '@mui/icons-material/LightModeOutlined'
|
||||||
import AppBar from "@mui/material/AppBar";
|
import AppBar from '@mui/material/AppBar'
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from '@mui/material/Avatar'
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from '@mui/material/IconButton'
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from '@mui/material/Stack'
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from '@mui/material/Toolbar'
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from '@mui/material/Typography'
|
||||||
import { useGetIdentity } from "@refinedev/core";
|
import {useGetIdentity} from '@refinedev/core'
|
||||||
import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from "@refinedev/mui";
|
import {HamburgerMenu, RefineThemedLayoutV2HeaderProps} from '@refinedev/mui'
|
||||||
import React, { useContext } from "react";
|
import React, {useContext} from 'react'
|
||||||
import { ColorModeContext } from "../../contexts/color-mode";
|
import {ColorModeContext} from '../../contexts/color-mode'
|
||||||
|
|
||||||
type IUser = {
|
type IUser = {
|
||||||
id: number;
|
id: number
|
||||||
name: string;
|
name: string
|
||||||
avatar: string;
|
avatar: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({sticky = true}) => {
|
||||||
sticky = true,
|
const {mode, setMode} = useContext(ColorModeContext)
|
||||||
}) => {
|
|
||||||
const { mode, setMode } = useContext(ColorModeContext);
|
|
||||||
|
|
||||||
const { data: user } = useGetIdentity<IUser>();
|
const {data: user} = useGetIdentity<IUser>()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position={sticky ? "sticky" : "relative"}>
|
<AppBar position={sticky ? 'sticky' : 'relative'}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Stack
|
<Stack direction="row" width="100%" justifyContent="flex-end" alignItems="center">
|
||||||
direction="row"
|
|
||||||
width="100%"
|
|
||||||
justifyContent="flex-end"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<HamburgerMenu />
|
<HamburgerMenu />
|
||||||
<Stack
|
<Stack direction="row" width="100%" justifyContent="flex-end" alignItems="center">
|
||||||
direction="row"
|
|
||||||
width="100%"
|
|
||||||
justifyContent="flex-end"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
color="inherit"
|
color="inherit"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMode();
|
setMode()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />}
|
{mode === 'dark' ? <LightModeOutlined /> : <DarkModeOutlined />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
{(user?.avatar || user?.name) && (
|
{(user?.avatar || user?.name) && (
|
||||||
<Stack
|
<Stack direction="row" gap="16px" alignItems="center" justifyContent="center">
|
||||||
direction="row"
|
|
||||||
gap="16px"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
{user?.name && (
|
{user?.name && (
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
display: {
|
display: {
|
||||||
xs: "none",
|
xs: 'none',
|
||||||
sm: "inline-block",
|
sm: 'inline-block',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
variant="subtitle2"
|
variant="subtitle2"
|
||||||
@ -76,5 +59,5 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
export { Header } from "./header";
|
export {Header} from './header'
|
||||||
|
@ -1,45 +1,32 @@
|
|||||||
import React, {
|
import React, {PropsWithChildren, createContext, useEffect, useState} from 'react'
|
||||||
PropsWithChildren,
|
import {ThemeProvider} from '@mui/material/styles'
|
||||||
createContext,
|
import {RefineThemes} from '@refinedev/mui'
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { ThemeProvider } from "@mui/material/styles";
|
|
||||||
import { RefineThemes } from "@refinedev/mui";
|
|
||||||
|
|
||||||
type ColorModeContextType = {
|
type ColorModeContextType = {
|
||||||
mode: string;
|
mode: string
|
||||||
setMode: () => void;
|
setMode: () => void
|
||||||
};
|
}
|
||||||
|
|
||||||
export const ColorModeContext = createContext<ColorModeContextType>(
|
export const ColorModeContext = createContext<ColorModeContextType>({} as ColorModeContextType)
|
||||||
{} as ColorModeContextType
|
|
||||||
);
|
|
||||||
|
|
||||||
export const ColorModeContextProvider: React.FC<PropsWithChildren> = ({
|
export const ColorModeContextProvider: React.FC<PropsWithChildren> = ({children}) => {
|
||||||
children,
|
const colorModeFromLocalStorage = localStorage.getItem('colorMode')
|
||||||
}) => {
|
const isSystemPreferenceDark = window?.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
const colorModeFromLocalStorage = localStorage.getItem("colorMode");
|
|
||||||
const isSystemPreferenceDark = window?.matchMedia(
|
|
||||||
"(prefers-color-scheme: dark)"
|
|
||||||
).matches;
|
|
||||||
|
|
||||||
const systemPreference = isSystemPreferenceDark ? "dark" : "light";
|
const systemPreference = isSystemPreferenceDark ? 'dark' : 'light'
|
||||||
const [mode, setMode] = useState(
|
const [mode, setMode] = useState(colorModeFromLocalStorage || systemPreference)
|
||||||
colorModeFromLocalStorage || systemPreference
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.localStorage.setItem("colorMode", mode);
|
window.localStorage.setItem('colorMode', mode)
|
||||||
}, [mode]);
|
}, [mode])
|
||||||
|
|
||||||
const setColorMode = () => {
|
const setColorMode = () => {
|
||||||
if (mode === "light") {
|
if (mode === 'light') {
|
||||||
setMode("dark");
|
setMode('dark')
|
||||||
} else {
|
} else {
|
||||||
setMode("light");
|
setMode('light')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColorModeContext.Provider
|
<ColorModeContext.Provider
|
||||||
@ -50,10 +37,10 @@ export const ColorModeContextProvider: React.FC<PropsWithChildren> = ({
|
|||||||
>
|
>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
// you can change the theme colors here. example: mode === "light" ? RefineThemes.Magenta : RefineThemes.MagentaDark
|
// you can change the theme colors here. example: mode === "light" ? RefineThemes.Magenta : RefineThemes.MagentaDark
|
||||||
theme={mode === "light" ? RefineThemes.Blue : RefineThemes.BlueDark}
|
theme={mode === 'light' ? RefineThemes.Blue : RefineThemes.BlueDark}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</ColorModeContext.Provider>
|
</ColorModeContext.Provider>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import React from "react";
|
import React from 'react'
|
||||||
import { createRoot } from "react-dom/client";
|
import {createRoot} from 'react-dom/client'
|
||||||
|
|
||||||
import App from "./App";
|
import App from './App'
|
||||||
|
|
||||||
const container = document.getElementById("root") as HTMLElement;
|
const container = document.getElementById('root') as HTMLElement
|
||||||
const root = createRoot(container);
|
const root = createRoot(container)
|
||||||
|
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>,
|
||||||
);
|
)
|
||||||
|
@ -1,120 +1,96 @@
|
|||||||
import { Autocomplete, Box, MenuItem, Select, TextField } from "@mui/material";
|
import {Autocomplete, Box, MenuItem, Select, TextField} from '@mui/material'
|
||||||
import { Create, useAutocomplete } from "@refinedev/mui";
|
import {Create, useAutocomplete} from '@refinedev/mui'
|
||||||
import { useForm } from "@refinedev/react-hook-form";
|
import {useForm} from '@refinedev/react-hook-form'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { Controller } from "react-hook-form";
|
import {Controller} from 'react-hook-form'
|
||||||
|
|
||||||
export const BlogPostCreate = () => {
|
export const BlogPostCreate = () => {
|
||||||
const {
|
const {
|
||||||
saveButtonProps,
|
saveButtonProps,
|
||||||
refineCore: { formLoading },
|
refineCore: {formLoading},
|
||||||
register,
|
register,
|
||||||
control,
|
control,
|
||||||
formState: { errors },
|
formState: {errors},
|
||||||
} = useForm({});
|
} = useForm({})
|
||||||
|
|
||||||
const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
|
const {autocompleteProps: categoryAutocompleteProps} = useAutocomplete({
|
||||||
resource: "categories",
|
resource: 'categories',
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
||||||
<Box
|
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
||||||
component="form"
|
|
||||||
sx={{ display: "flex", flexDirection: "column" }}
|
|
||||||
autoComplete="off"
|
|
||||||
>
|
|
||||||
<TextField
|
<TextField
|
||||||
{...register("title", {
|
{...register('title', {
|
||||||
required: "This field is required",
|
required: 'This field is required',
|
||||||
})}
|
})}
|
||||||
error={!!(errors as any)?.title}
|
error={!!(errors as any)?.title}
|
||||||
helperText={(errors as any)?.title?.message}
|
helperText={(errors as any)?.title?.message}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth
|
fullWidth
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{shrink: true}}
|
||||||
type="text"
|
type="text"
|
||||||
label={"Title"}
|
label={'Title'}
|
||||||
name="title"
|
name="title"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
{...register("content", {
|
{...register('content', {
|
||||||
required: "This field is required",
|
required: 'This field is required',
|
||||||
})}
|
})}
|
||||||
error={!!(errors as any)?.content}
|
error={!!(errors as any)?.content}
|
||||||
helperText={(errors as any)?.content?.message}
|
helperText={(errors as any)?.content?.message}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth
|
fullWidth
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{shrink: true}}
|
||||||
multiline
|
multiline
|
||||||
label={"Content"}
|
label={'Content'}
|
||||||
name="content"
|
name="content"
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={"category.id"}
|
name={'category.id'}
|
||||||
rules={{ required: "This field is required" }}
|
rules={{required: 'This field is required'}}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
defaultValue={null as any}
|
defaultValue={null as any}
|
||||||
render={({ field }) => (
|
render={({field}) => (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
{...categoryAutocompleteProps}
|
{...categoryAutocompleteProps}
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(_, value) => {
|
onChange={(_, value) => {
|
||||||
field.onChange(value.id);
|
field.onChange(value.id)
|
||||||
}}
|
}}
|
||||||
getOptionLabel={(item) => {
|
getOptionLabel={(item) => {
|
||||||
return (
|
return (
|
||||||
categoryAutocompleteProps?.options?.find((p) => {
|
categoryAutocompleteProps?.options?.find((p) => {
|
||||||
const itemId =
|
const itemId = typeof item === 'object' ? item?.id?.toString() : item?.toString()
|
||||||
typeof item === "object"
|
const pId = p?.id?.toString()
|
||||||
? item?.id?.toString()
|
return itemId === pId
|
||||||
: item?.toString();
|
})?.title ?? ''
|
||||||
const pId = p?.id?.toString();
|
)
|
||||||
return itemId === pId;
|
|
||||||
})?.title ?? ""
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
isOptionEqualToValue={(option, value) => {
|
isOptionEqualToValue={(option, value) => {
|
||||||
const optionId = option?.id?.toString();
|
const optionId = option?.id?.toString()
|
||||||
const valueId =
|
const valueId = typeof value === 'object' ? value?.id?.toString() : value?.toString()
|
||||||
typeof value === "object"
|
return value === undefined || optionId === valueId
|
||||||
? value?.id?.toString()
|
|
||||||
: value?.toString();
|
|
||||||
return value === undefined || optionId === valueId;
|
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => <TextField {...params} label={'Category'} margin="normal" variant="outlined" error={!!(errors as any)?.category?.id} helperText={(errors as any)?.category?.id?.message} required />}
|
||||||
<TextField
|
|
||||||
{...params}
|
|
||||||
label={"Category"}
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
error={!!(errors as any)?.category?.id}
|
|
||||||
helperText={(errors as any)?.category?.id?.message}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
name="status"
|
name="status"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => {
|
render={({field}) => {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select {...field} value={field?.value || 'draft'} label={'Status'}>
|
||||||
{...field}
|
|
||||||
value={field?.value || "draft"}
|
|
||||||
label={"Status"}
|
|
||||||
>
|
|
||||||
<MenuItem value="draft">Draft</MenuItem>
|
<MenuItem value="draft">Draft</MenuItem>
|
||||||
<MenuItem value="published">Published</MenuItem>
|
<MenuItem value="published">Published</MenuItem>
|
||||||
<MenuItem value="rejected">Rejected</MenuItem>
|
<MenuItem value="rejected">Rejected</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
);
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Create>
|
</Create>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,125 +1,101 @@
|
|||||||
import { Autocomplete, Box, Select, TextField } from "@mui/material";
|
import {Autocomplete, Box, Select, TextField} from '@mui/material'
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from '@mui/material/MenuItem'
|
||||||
import { Edit, useAutocomplete } from "@refinedev/mui";
|
import {Edit, useAutocomplete} from '@refinedev/mui'
|
||||||
import { useForm } from "@refinedev/react-hook-form";
|
import {useForm} from '@refinedev/react-hook-form'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { Controller } from "react-hook-form";
|
import {Controller} from 'react-hook-form'
|
||||||
|
|
||||||
export const BlogPostEdit = () => {
|
export const BlogPostEdit = () => {
|
||||||
const {
|
const {
|
||||||
saveButtonProps,
|
saveButtonProps,
|
||||||
refineCore: { queryResult, formLoading },
|
refineCore: {queryResult, formLoading},
|
||||||
register,
|
register,
|
||||||
control,
|
control,
|
||||||
formState: { errors },
|
formState: {errors},
|
||||||
} = useForm({});
|
} = useForm({})
|
||||||
|
|
||||||
const blogPostsData = queryResult?.data?.data;
|
const blogPostsData = queryResult?.data?.data
|
||||||
|
|
||||||
const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({
|
const {autocompleteProps: categoryAutocompleteProps} = useAutocomplete({
|
||||||
resource: "categories",
|
resource: 'categories',
|
||||||
defaultValue: blogPostsData?.category?.id,
|
defaultValue: blogPostsData?.category?.id,
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
||||||
<Box
|
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
||||||
component="form"
|
|
||||||
sx={{ display: "flex", flexDirection: "column" }}
|
|
||||||
autoComplete="off"
|
|
||||||
>
|
|
||||||
<TextField
|
<TextField
|
||||||
{...register("title", {
|
{...register('title', {
|
||||||
required: "This field is required",
|
required: 'This field is required',
|
||||||
})}
|
})}
|
||||||
error={!!(errors as any)?.title}
|
error={!!(errors as any)?.title}
|
||||||
helperText={(errors as any)?.title?.message}
|
helperText={(errors as any)?.title?.message}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth
|
fullWidth
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{shrink: true}}
|
||||||
type="text"
|
type="text"
|
||||||
label={"Title"}
|
label={'Title'}
|
||||||
name="title"
|
name="title"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
{...register("content", {
|
{...register('content', {
|
||||||
required: "This field is required",
|
required: 'This field is required',
|
||||||
})}
|
})}
|
||||||
error={!!(errors as any)?.content}
|
error={!!(errors as any)?.content}
|
||||||
helperText={(errors as any)?.content?.message}
|
helperText={(errors as any)?.content?.message}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth
|
fullWidth
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{shrink: true}}
|
||||||
multiline
|
multiline
|
||||||
label={"Content"}
|
label={'Content'}
|
||||||
name="content"
|
name="content"
|
||||||
rows={4}
|
rows={4}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={"category.id"}
|
name={'category.id'}
|
||||||
rules={{ required: "This field is required" }}
|
rules={{required: 'This field is required'}}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
defaultValue={null as any}
|
defaultValue={null as any}
|
||||||
render={({ field }) => (
|
render={({field}) => (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
{...categoryAutocompleteProps}
|
{...categoryAutocompleteProps}
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(_, value) => {
|
onChange={(_, value) => {
|
||||||
field.onChange(value.id);
|
field.onChange(value.id)
|
||||||
}}
|
}}
|
||||||
getOptionLabel={(item) => {
|
getOptionLabel={(item) => {
|
||||||
return (
|
return (
|
||||||
categoryAutocompleteProps?.options?.find((p) => {
|
categoryAutocompleteProps?.options?.find((p) => {
|
||||||
const itemId =
|
const itemId = typeof item === 'object' ? item?.id?.toString() : item?.toString()
|
||||||
typeof item === "object"
|
const pId = p?.id?.toString()
|
||||||
? item?.id?.toString()
|
return itemId === pId
|
||||||
: item?.toString();
|
})?.title ?? ''
|
||||||
const pId = p?.id?.toString();
|
)
|
||||||
return itemId === pId;
|
|
||||||
})?.title ?? ""
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
isOptionEqualToValue={(option, value) => {
|
isOptionEqualToValue={(option, value) => {
|
||||||
const optionId = option?.id?.toString();
|
const optionId = option?.id?.toString()
|
||||||
const valueId =
|
const valueId = typeof value === 'object' ? value?.id?.toString() : value?.toString()
|
||||||
typeof value === "object"
|
return value === undefined || optionId === valueId
|
||||||
? value?.id?.toString()
|
|
||||||
: value?.toString();
|
|
||||||
return value === undefined || optionId === valueId;
|
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => <TextField {...params} label={'Category'} margin="normal" variant="outlined" error={!!(errors as any)?.category?.id} helperText={(errors as any)?.category?.id?.message} required />}
|
||||||
<TextField
|
|
||||||
{...params}
|
|
||||||
label={"Category"}
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
error={!!(errors as any)?.category?.id}
|
|
||||||
helperText={(errors as any)?.category?.id?.message}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
name="status"
|
name="status"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => {
|
render={({field}) => {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select {...field} value={field?.value || 'draft'} label={'Status'}>
|
||||||
{...field}
|
|
||||||
value={field?.value || "draft"}
|
|
||||||
label={"Status"}
|
|
||||||
>
|
|
||||||
<MenuItem value="draft">Draft</MenuItem>
|
<MenuItem value="draft">Draft</MenuItem>
|
||||||
<MenuItem value="published">Published</MenuItem>
|
<MenuItem value="published">Published</MenuItem>
|
||||||
<MenuItem value="rejected">Rejected</MenuItem>
|
<MenuItem value="rejected">Rejected</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
);
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export * from "./create";
|
export * from './create'
|
||||||
export * from "./edit";
|
export * from './edit'
|
||||||
export * from "./list";
|
export * from './list'
|
||||||
export * from "./show";
|
export * from './show'
|
||||||
|
@ -1,124 +1,105 @@
|
|||||||
import { DataGrid, type GridColDef } from "@mui/x-data-grid";
|
import {DataGrid, type GridColDef} from '@mui/x-data-grid'
|
||||||
import { useMany } from "@refinedev/core";
|
import {useMany} from '@refinedev/core'
|
||||||
import {
|
import {DateField, DeleteButton, EditButton, List, ShowButton, useDataGrid} from '@refinedev/mui'
|
||||||
DateField,
|
import {Typography} from '@mui/material'
|
||||||
DeleteButton,
|
import React from 'react'
|
||||||
EditButton,
|
|
||||||
List,
|
|
||||||
ShowButton,
|
|
||||||
useDataGrid,
|
|
||||||
} from "@refinedev/mui";
|
|
||||||
import { Typography } from "@mui/material";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export const BlogPostList = () => {
|
export const BlogPostList = () => {
|
||||||
const { dataGridProps } = useDataGrid({});
|
const {dataGridProps} = useDataGrid({})
|
||||||
|
|
||||||
const { data: categoryData, isLoading: categoryIsLoading } = useMany({
|
const {data: categoryData, isLoading: categoryIsLoading} = useMany({
|
||||||
resource: "categories",
|
resource: 'categories',
|
||||||
ids:
|
ids: dataGridProps?.rows?.map((item: any) => item?.category?.id).filter(Boolean) ?? [],
|
||||||
dataGridProps?.rows
|
|
||||||
?.map((item: any) => item?.category?.id)
|
|
||||||
.filter(Boolean) ?? [],
|
|
||||||
queryOptions: {
|
queryOptions: {
|
||||||
enabled: !!dataGridProps?.rows,
|
enabled: !!dataGridProps?.rows,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const columns = React.useMemo<GridColDef[]>(
|
const columns = React.useMemo<GridColDef[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
field: "id",
|
field: 'id',
|
||||||
headerName: "ID",
|
headerName: 'ID',
|
||||||
type: "number",
|
type: 'number',
|
||||||
minWidth: 50,
|
minWidth: 50,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
align: "left",
|
align: 'left',
|
||||||
headerAlign: "left",
|
headerAlign: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "title",
|
field: 'title',
|
||||||
headerName: "Title",
|
headerName: 'Title',
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "content",
|
field: 'content',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
headerName: "Content",
|
headerName: 'Content',
|
||||||
minWidth: 250,
|
minWidth: 250,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
renderCell: function render({ value }) {
|
renderCell: function render({value}) {
|
||||||
if (!value) return "-";
|
if (!value) return '-'
|
||||||
return (
|
return (
|
||||||
<Typography
|
<Typography component="p" whiteSpace="pre" overflow="hidden" textOverflow="ellipsis">
|
||||||
component="p"
|
|
||||||
whiteSpace="pre"
|
|
||||||
overflow="hidden"
|
|
||||||
textOverflow="ellipsis"
|
|
||||||
>
|
|
||||||
{value}
|
{value}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "category",
|
field: 'category',
|
||||||
headerName: "Category",
|
headerName: 'Category',
|
||||||
minWidth: 160,
|
minWidth: 160,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
valueGetter: (_, row) => {
|
valueGetter: (_, row) => {
|
||||||
const value = row?.category;
|
const value = row?.category
|
||||||
return value;
|
return value
|
||||||
},
|
},
|
||||||
renderCell: function render({ value }) {
|
renderCell: function render({value}) {
|
||||||
return categoryIsLoading ? (
|
return categoryIsLoading ? <>Loading...</> : categoryData?.data?.find((item) => item.id === value?.id)?.title
|
||||||
<>Loading...</>
|
|
||||||
) : (
|
|
||||||
categoryData?.data?.find((item) => item.id === value?.id)?.title
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "status",
|
field: 'status',
|
||||||
headerName: "Status",
|
headerName: 'Status',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "createdAt",
|
field: 'createdAt',
|
||||||
headerName: "Created at",
|
headerName: 'Created at',
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
renderCell: function render({ value }) {
|
renderCell: function render({value}) {
|
||||||
return <DateField value={value} />;
|
return <DateField value={value} />
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "actions",
|
field: 'actions',
|
||||||
headerName: "Actions",
|
headerName: 'Actions',
|
||||||
align: "right",
|
align: 'right',
|
||||||
headerAlign: "right",
|
headerAlign: 'right',
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
renderCell: function render({ row }) {
|
renderCell: function render({row}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EditButton hideText recordItemId={row.id} />
|
<EditButton hideText recordItemId={row.id} />
|
||||||
<ShowButton hideText recordItemId={row.id} />
|
<ShowButton hideText recordItemId={row.id} />
|
||||||
<DeleteButton hideText recordItemId={row.id} />
|
<DeleteButton hideText recordItemId={row.id} />
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[categoryData, categoryIsLoading]
|
[categoryData, categoryIsLoading],
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<List>
|
||||||
<DataGrid {...dataGridProps} columns={columns} />
|
<DataGrid {...dataGridProps} columns={columns} />
|
||||||
</List>
|
</List>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,59 +1,53 @@
|
|||||||
import { Stack, Typography } from "@mui/material";
|
import {Stack, Typography} from '@mui/material'
|
||||||
import { useOne, useShow } from "@refinedev/core";
|
import {useOne, useShow} from '@refinedev/core'
|
||||||
import {
|
import {DateField, MarkdownField, NumberField, Show, TextFieldComponent as TextField} from '@refinedev/mui'
|
||||||
DateField,
|
|
||||||
MarkdownField,
|
|
||||||
NumberField,
|
|
||||||
Show,
|
|
||||||
TextFieldComponent as TextField,
|
|
||||||
} from "@refinedev/mui";
|
|
||||||
|
|
||||||
export const BlogPostShow = () => {
|
export const BlogPostShow = () => {
|
||||||
const { query } = useShow({});
|
const {query} = useShow({})
|
||||||
|
|
||||||
const { data, isLoading } = query;
|
const {data, isLoading} = query
|
||||||
|
|
||||||
const record = data?.data;
|
const record = data?.data
|
||||||
|
|
||||||
const { data: categoryData, isLoading: categoryIsLoading } = useOne({
|
const {data: categoryData, isLoading: categoryIsLoading} = useOne({
|
||||||
resource: "categories",
|
resource: 'categories',
|
||||||
id: record?.category?.id || "",
|
id: record?.category?.id || '',
|
||||||
queryOptions: {
|
queryOptions: {
|
||||||
enabled: !!record,
|
enabled: !!record,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show isLoading={isLoading}>
|
<Show isLoading={isLoading}>
|
||||||
<Stack gap={1}>
|
<Stack gap={1}>
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"ID"}
|
{'ID'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField value={record?.id} />
|
<TextField value={record?.id} />
|
||||||
|
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"Title"}
|
{'Title'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField value={record?.title} />
|
<TextField value={record?.title} />
|
||||||
|
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"Content"}
|
{'Content'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<MarkdownField value={record?.content} />
|
<MarkdownField value={record?.content} />
|
||||||
|
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"Category"}
|
{'Category'}
|
||||||
</Typography>
|
</Typography>
|
||||||
{categoryIsLoading ? <>Loading...</> : <>{categoryData?.data?.title}</>}
|
{categoryIsLoading ? <>Loading...</> : <>{categoryData?.data?.title}</>}
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"Status"}
|
{'Status'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField value={record?.status} />
|
<TextField value={record?.status} />
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"CreatedAt"}
|
{'CreatedAt'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<DateField value={record?.createdAt} />
|
<DateField value={record?.createdAt} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Show>
|
</Show>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,36 +1,32 @@
|
|||||||
import { Box, TextField } from "@mui/material";
|
import {Box, TextField} from '@mui/material'
|
||||||
import { Create } from "@refinedev/mui";
|
import {Create} from '@refinedev/mui'
|
||||||
import { useForm } from "@refinedev/react-hook-form";
|
import {useForm} from '@refinedev/react-hook-form'
|
||||||
|
|
||||||
export const CategoryCreate = () => {
|
export const CategoryCreate = () => {
|
||||||
const {
|
const {
|
||||||
saveButtonProps,
|
saveButtonProps,
|
||||||
refineCore: { formLoading },
|
refineCore: {formLoading},
|
||||||
register,
|
register,
|
||||||
formState: { errors },
|
formState: {errors},
|
||||||
} = useForm({});
|
} = useForm({})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
|
||||||
<Box
|
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
||||||
component="form"
|
|
||||||
sx={{ display: "flex", flexDirection: "column" }}
|
|
||||||
autoComplete="off"
|
|
||||||
>
|
|
||||||
<TextField
|
<TextField
|
||||||
{...register("title", {
|
{...register('title', {
|
||||||
required: "This field is required",
|
required: 'This field is required',
|
||||||
})}
|
})}
|
||||||
error={!!(errors as any)?.title}
|
error={!!(errors as any)?.title}
|
||||||
helperText={(errors as any)?.title?.message}
|
helperText={(errors as any)?.title?.message}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth
|
fullWidth
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{shrink: true}}
|
||||||
type="text"
|
type="text"
|
||||||
label={"Title"}
|
label={'Title'}
|
||||||
name="title"
|
name="title"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Create>
|
</Create>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,35 +1,31 @@
|
|||||||
import { Box, TextField } from "@mui/material";
|
import {Box, TextField} from '@mui/material'
|
||||||
import { Edit } from "@refinedev/mui";
|
import {Edit} from '@refinedev/mui'
|
||||||
import { useForm } from "@refinedev/react-hook-form";
|
import {useForm} from '@refinedev/react-hook-form'
|
||||||
|
|
||||||
export const CategoryEdit = () => {
|
export const CategoryEdit = () => {
|
||||||
const {
|
const {
|
||||||
saveButtonProps,
|
saveButtonProps,
|
||||||
register,
|
register,
|
||||||
formState: { errors },
|
formState: {errors},
|
||||||
} = useForm({});
|
} = useForm({})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Edit saveButtonProps={saveButtonProps}>
|
<Edit saveButtonProps={saveButtonProps}>
|
||||||
<Box
|
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
|
||||||
component="form"
|
|
||||||
sx={{ display: "flex", flexDirection: "column" }}
|
|
||||||
autoComplete="off"
|
|
||||||
>
|
|
||||||
<TextField
|
<TextField
|
||||||
{...register("title", {
|
{...register('title', {
|
||||||
required: "This field is required",
|
required: 'This field is required',
|
||||||
})}
|
})}
|
||||||
error={!!(errors as any)?.title}
|
error={!!(errors as any)?.title}
|
||||||
helperText={(errors as any)?.title?.message}
|
helperText={(errors as any)?.title?.message}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth
|
fullWidth
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{shrink: true}}
|
||||||
type="text"
|
type="text"
|
||||||
label={"Title"}
|
label={'Title'}
|
||||||
name="title"
|
name="title"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export * from "./create";
|
export * from './create'
|
||||||
export * from "./edit";
|
export * from './edit'
|
||||||
export * from "./list";
|
export * from './list'
|
||||||
export * from "./show";
|
export * from './show'
|
||||||
|
@ -1,59 +1,53 @@
|
|||||||
import { DataGrid, type GridColDef } from "@mui/x-data-grid";
|
import {DataGrid, type GridColDef} from '@mui/x-data-grid'
|
||||||
import {
|
import {DeleteButton, EditButton, List, ShowButton, useDataGrid} from '@refinedev/mui'
|
||||||
DeleteButton,
|
import React from 'react'
|
||||||
EditButton,
|
|
||||||
List,
|
|
||||||
ShowButton,
|
|
||||||
useDataGrid,
|
|
||||||
} from "@refinedev/mui";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export const CategoryList = () => {
|
export const CategoryList = () => {
|
||||||
const { dataGridProps } = useDataGrid({});
|
const {dataGridProps} = useDataGrid({})
|
||||||
|
|
||||||
const columns = React.useMemo<GridColDef[]>(
|
const columns = React.useMemo<GridColDef[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
field: "id",
|
field: 'id',
|
||||||
headerName: "ID",
|
headerName: 'ID',
|
||||||
type: "number",
|
type: 'number',
|
||||||
minWidth: 50,
|
minWidth: 50,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
align: "left",
|
align: 'left',
|
||||||
headerAlign: "left",
|
headerAlign: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "title",
|
field: 'title',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
headerName: "Title",
|
headerName: 'Title',
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "actions",
|
field: 'actions',
|
||||||
headerName: "Actions",
|
headerName: 'Actions',
|
||||||
align: "right",
|
align: 'right',
|
||||||
headerAlign: "right",
|
headerAlign: 'right',
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
renderCell: function render({ row }) {
|
renderCell: function render({row}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EditButton hideText recordItemId={row.id} />
|
<EditButton hideText recordItemId={row.id} />
|
||||||
<ShowButton hideText recordItemId={row.id} />
|
<ShowButton hideText recordItemId={row.id} />
|
||||||
<DeleteButton hideText recordItemId={row.id} />
|
<DeleteButton hideText recordItemId={row.id} />
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[]
|
[],
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<List>
|
||||||
<DataGrid {...dataGridProps} columns={columns} />
|
<DataGrid {...dataGridProps} columns={columns} />
|
||||||
</List>
|
</List>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,29 +1,25 @@
|
|||||||
import { Stack, Typography } from "@mui/material";
|
import {Stack, Typography} from '@mui/material'
|
||||||
import { useShow } from "@refinedev/core";
|
import {useShow} from '@refinedev/core'
|
||||||
import {
|
import {NumberField, Show, TextFieldComponent as TextField} from '@refinedev/mui'
|
||||||
NumberField,
|
|
||||||
Show,
|
|
||||||
TextFieldComponent as TextField,
|
|
||||||
} from "@refinedev/mui";
|
|
||||||
|
|
||||||
export const CategoryShow = () => {
|
export const CategoryShow = () => {
|
||||||
const { query } = useShow({});
|
const {query} = useShow({})
|
||||||
const { data, isLoading } = query;
|
const {data, isLoading} = query
|
||||||
|
|
||||||
const record = data?.data;
|
const record = data?.data
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show isLoading={isLoading}>
|
<Show isLoading={isLoading}>
|
||||||
<Stack gap={1}>
|
<Stack gap={1}>
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"ID"}
|
{'ID'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField value={record?.id} />
|
<TextField value={record?.id} />
|
||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{"Title"}
|
{'Title'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField value={record?.title} />
|
<TextField value={record?.title} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Show>
|
</Show>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { AuthPage } from "@refinedev/mui";
|
import {AuthPage} from '@refinedev/mui'
|
||||||
|
|
||||||
export const ForgotPassword = () => {
|
export const ForgotPassword = () => {
|
||||||
return <AuthPage type="forgotPassword" />;
|
return <AuthPage type="forgotPassword" />
|
||||||
};
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { AuthPage } from "@refinedev/mui";
|
import {AuthPage} from '@refinedev/mui'
|
||||||
|
|
||||||
export const Login = () => {
|
export const Login = () => {
|
||||||
return (
|
return (
|
||||||
<AuthPage
|
<AuthPage
|
||||||
type="login"
|
type="login"
|
||||||
formProps={{
|
formProps={{
|
||||||
defaultValues: { email: "demo@refine.dev", password: "demodemo" },
|
defaultValues: {email: 'demo@refine.dev', password: 'demodemo'},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { AuthPage } from "@refinedev/mui";
|
import {AuthPage} from '@refinedev/mui'
|
||||||
|
|
||||||
export const Register = () => {
|
export const Register = () => {
|
||||||
return <AuthPage type="register" />;
|
return <AuthPage type="register" />
|
||||||
};
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineConfig } from "vite";
|
import {defineConfig} from 'vite'
|
||||||
import react from "@vitejs/plugin-react";
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
});
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user