~ format project files using prettier local configuration

This commit is contained in:
maxim 2025-01-19 17:33:27 +03:00
parent 266c7fa070
commit 80d7e52b32
20 changed files with 346 additions and 505 deletions

View File

@ -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

View File

@ -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}
}, },
}; }

View File

@ -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>
); )
}; }

View File

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

View File

@ -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>
); )
}; }

View File

@ -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>,
); )

View File

@ -1,8 +1,8 @@
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 {
@ -11,22 +11,18 @@ export const BlogPostCreate = () => {
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}
@ -34,12 +30,12 @@ export const BlogPostCreate = () => {
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}
@ -47,13 +43,13 @@ export const BlogPostCreate = () => {
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}) => (
@ -61,39 +57,23 @@ export const BlogPostCreate = () => {
{...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
/>
)}
/> />
)} )}
/> />
@ -102,19 +82,15 @@ export const BlogPostCreate = () => {
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>
); )
}; }

View File

@ -1,9 +1,9 @@
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 {
@ -12,25 +12,21 @@ export const BlogPostEdit = () => {
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}
@ -38,12 +34,12 @@ export const BlogPostEdit = () => {
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}
@ -51,14 +47,14 @@ export const BlogPostEdit = () => {
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}) => (
@ -66,39 +62,23 @@ export const BlogPostEdit = () => {
{...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
/>
)}
/> />
)} )}
/> />
@ -107,19 +87,15 @@ export const BlogPostEdit = () => {
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>
); )
}; }

View File

@ -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'

View File

@ -1,107 +1,88 @@
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 (
<> <>
@ -109,16 +90,16 @@ export const BlogPostList = () => {
<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>
); )
}; }

View File

@ -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>
); )
}; }

View File

@ -1,6 +1,6 @@
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 {
@ -8,18 +8,14 @@ export const CategoryCreate = () => {
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}
@ -27,10 +23,10 @@ export const CategoryCreate = () => {
fullWidth fullWidth
InputLabelProps={{shrink: true}} InputLabelProps={{shrink: true}}
type="text" type="text"
label={"Title"} label={'Title'}
name="title" name="title"
/> />
</Box> </Box>
</Create> </Create>
); )
}; }

View File

@ -1,24 +1,20 @@
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}
@ -26,10 +22,10 @@ export const CategoryEdit = () => {
fullWidth fullWidth
InputLabelProps={{shrink: true}} InputLabelProps={{shrink: true}}
type="text" type="text"
label={"Title"} label={'Title'}
name="title" name="title"
/> />
</Box> </Box>
</Edit> </Edit>
); )
}; }

View File

@ -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'

View File

@ -1,42 +1,36 @@
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 (
<> <>
@ -44,16 +38,16 @@ export const CategoryList = () => {
<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>
); )
}; }

View File

@ -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>
); )
}; }

View File

@ -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" />
}; }

View File

@ -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'},
}} }}
/> />
); )
}; }

View File

@ -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" />
}; }

View File

@ -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()],
}); })