init refine app
				
					
				
			This commit is contained in:
		
							
								
								
									
										16
									
								
								.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| /* eslint-env node */ | ||||
|  | ||||
| module.exports = { | ||||
|   env: { browser: true, es2020: true }, | ||||
|   extends: [ | ||||
|     "eslint:recommended", | ||||
|     "plugin:@typescript-eslint/recommended", | ||||
|     "plugin:react-hooks/recommended", | ||||
|   ], | ||||
|   parser: "@typescript-eslint/parser", | ||||
|   parserOptions: { ecmaVersion: "latest", sourceType: "module" }, | ||||
|   plugins: ["react-refresh"], | ||||
|   rules: { | ||||
|     "react-refresh/only-export-components": "warn", | ||||
|   }, | ||||
| }; | ||||
							
								
								
									
										26
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # Logs | ||||
|  | ||||
| logs | ||||
| _.log | ||||
| npm-debug.log_ | ||||
| yarn-debug.log* | ||||
| yarn-error.log* | ||||
| pnpm-debug.log* | ||||
| lerna-debug.log* | ||||
|  | ||||
| node_modules | ||||
| dist | ||||
| dist-ssr | ||||
| \*.local | ||||
|  | ||||
| # Editor directories and files | ||||
|  | ||||
| .vscode/_ | ||||
| !.vscode/extensions.json | ||||
| .idea | ||||
| .DS_Store | ||||
| _.suo | ||||
| _.ntvs_ | ||||
| _.njsproj | ||||
| _.sln | ||||
| \*.sw? | ||||
							
								
								
									
										37
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| # This Dockerfile uses `serve` npm package to serve the static files with node process. | ||||
| # You can find the Dockerfile for nginx in the following link: | ||||
| # https://github.com/refinedev/dockerfiles/blob/main/vite/Dockerfile.nginx | ||||
| FROM refinedev/node:18 AS base | ||||
|  | ||||
| FROM base as deps | ||||
|  | ||||
| COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ | ||||
|  | ||||
| RUN \ | ||||
|   if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ | ||||
|   elif [ -f package-lock.json ]; then npm ci; \ | ||||
|   elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \ | ||||
|   else echo "Lockfile not found." && exit 1; \ | ||||
|   fi | ||||
|  | ||||
| FROM base as builder | ||||
|  | ||||
| ENV NODE_ENV production | ||||
|  | ||||
| COPY --from=deps /app/refine/node_modules ./node_modules | ||||
|  | ||||
| COPY . . | ||||
|  | ||||
| RUN npm run build | ||||
|  | ||||
| FROM base as runner | ||||
|  | ||||
| ENV NODE_ENV production | ||||
|  | ||||
| RUN npm install -g serve | ||||
|  | ||||
| COPY --from=builder /app/refine/dist ./ | ||||
|  | ||||
| USER refine | ||||
|  | ||||
| CMD ["serve"] | ||||
							
								
								
									
										49
									
								
								README.MD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								README.MD
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| # white-nights | ||||
|  | ||||
| <div align="center" style="margin: 30px;"> | ||||
|     <a href="https://refine.dev"> | ||||
|     <img alt="refine logo" src="https://refine.ams3.cdn.digitaloceanspaces.com/readme/refine-readme-banner.png"> | ||||
|     </a> | ||||
| </div> | ||||
| <br/> | ||||
|  | ||||
| This [Refine](https://github.com/refinedev/refine) project was generated with [create refine-app](https://github.com/refinedev/refine/tree/master/packages/create-refine-app). | ||||
|  | ||||
| ## Getting Started | ||||
|  | ||||
| A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility ✨ | ||||
|  | ||||
| Refine's hooks and components simplifies the development process and eliminates the repetitive tasks by providing industry-standard solutions for crucial aspects of a project, including authentication, access control, routing, networking, state management, and i18n. | ||||
|  | ||||
| ## Available Scripts | ||||
|  | ||||
| ### Running the development server. | ||||
|  | ||||
| ```bash | ||||
|     pnpm dev | ||||
| ``` | ||||
|  | ||||
| ### Building for production. | ||||
|  | ||||
| ```bash | ||||
|     pnpm build | ||||
| ``` | ||||
|  | ||||
| ### Running the production server. | ||||
|  | ||||
| ```bash | ||||
|     pnpm start | ||||
| ``` | ||||
|  | ||||
| ## Learn More | ||||
|  | ||||
| To learn more about **Refine**, please check out the [Documentation](https://refine.dev/docs) | ||||
|  | ||||
| - **REST Data Provider** [Docs](https://refine.dev/docs/core/providers/data-provider/#overview) | ||||
| - **Material UI** [Docs](https://refine.dev/docs/ui-frameworks/mui/tutorial/) | ||||
| - **React Router** [Docs](https://refine.dev/docs/core/providers/router-provider/) | ||||
| - **Custom Auth Provider** [Docs](https://refine.dev/docs/core/providers/auth-provider/) | ||||
|  | ||||
| ## License | ||||
|  | ||||
| MIT | ||||
							
								
								
									
										41
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="utf-8" /> | ||||
|     <link rel="icon" href="/favicon.ico" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
|     <meta name="theme-color" content="#000000" /> | ||||
|     <meta | ||||
|       name="description" | ||||
|       content="refine | Build your React-based CRUD applications, without constraints." | ||||
|     /> | ||||
|     <meta | ||||
|       data-rh="true" | ||||
|       property="og:image" | ||||
|       content="https://refine.dev/img/refine_social.png" | ||||
|     /> | ||||
|     <meta | ||||
|       data-rh="true" | ||||
|       name="twitter:image" | ||||
|       content="https://refine.dev/img/refine_social.png" | ||||
|     /> | ||||
|     <title> | ||||
|       Refine - Build your React-based CRUD applications, without constraints. | ||||
|     </title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <noscript>You need to enable JavaScript to run this app.</noscript> | ||||
|     <div id="root"></div> | ||||
|     <script type="module" src="/src/index.tsx"></script> | ||||
|     <!-- | ||||
|       This HTML file is a template. | ||||
|       If you open it directly in the browser, you will see an empty page. | ||||
|  | ||||
|       You can add webfonts, meta tags, or analytics to this file. | ||||
|       The build step will place the bundled scripts into the <body> tag. | ||||
|  | ||||
|       To begin the development, run `npm dev` or `yarn start`. | ||||
|       To create a production bundle, use `npm run build` or `yarn build`. | ||||
|     --> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										60
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| { | ||||
|   "name": "white-nights", | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "type": "module", | ||||
|   "dependencies": { | ||||
|     "@refinedev/cli": "^2.16.21", | ||||
|     "@refinedev/core": "^4.47.1", | ||||
|     "@refinedev/devtools": "^1.1.32", | ||||
|     "@refinedev/kbar": "^1.3.6", | ||||
|     "react": "^18.0.0", | ||||
|     "react-dom": "^18.0.0", | ||||
|     "react-router": "^7.0.2", | ||||
|     "@refinedev/simple-rest": "^5.0.1", | ||||
|     "@refinedev/mui": "^6.0.0", | ||||
|     "@refinedev/react-hook-form": "^4.8.14", | ||||
|     "@mui/icons-material": "^6.1.6", | ||||
|     "@emotion/react": "^11.8.2", | ||||
|     "@emotion/styled": "^11.8.1", | ||||
|     "@mui/lab": "^6.0.0-beta.14", | ||||
|     "@mui/material": "^6.1.7", | ||||
|     "@mui/x-data-grid": "^7.22.2", | ||||
|     "react-hook-form": "^7.30.0", | ||||
|     "@refinedev/react-router": "^1.0.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/node": "^18.16.2", | ||||
|     "@types/react": "^18.0.0", | ||||
|     "@types/react-dom": "^18.0.0", | ||||
|     "@typescript-eslint/eslint-plugin": "^5.57.1", | ||||
|     "@typescript-eslint/parser": "^5.57.1", | ||||
|     "@vitejs/plugin-react": "^4.0.0", | ||||
|     "eslint": "^8.38.0", | ||||
|     "eslint-plugin-react-hooks": "^4.6.0", | ||||
|     "eslint-plugin-react-refresh": "^0.3.4", | ||||
|     "typescript": "^5.4.2", | ||||
|     "vite": "^4.3.1" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "dev": "refine dev", | ||||
|     "build": "tsc && refine build", | ||||
|     "start": "refine start", | ||||
|     "refine": "refine" | ||||
|   }, | ||||
|   "browserslist": { | ||||
|     "production": [ | ||||
|       ">0.2%", | ||||
|       "not dead", | ||||
|       "not op_mini all" | ||||
|     ], | ||||
|     "development": [ | ||||
|       "last 1 chrome version", | ||||
|       "last 1 firefox version", | ||||
|       "last 1 safari version" | ||||
|     ] | ||||
|   }, | ||||
|   "refine": { | ||||
|     "projectId": "Wv044J-t53S3s-PcbJGe" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										6401
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6401
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 7.2 KiB | 
							
								
								
									
										154
									
								
								src/App.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/App.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | ||||
| import { | ||||
|   Refine, | ||||
|   GitHubBanner, | ||||
|   WelcomePage, | ||||
|   Authenticated, | ||||
| } from "@refinedev/core"; | ||||
| import { DevtoolsPanel, DevtoolsProvider } from "@refinedev/devtools"; | ||||
| import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar"; | ||||
|  | ||||
| import { | ||||
|   AuthPage, | ||||
|   ErrorComponent, | ||||
|   useNotificationProvider, | ||||
|   RefineSnackbarProvider, | ||||
|   ThemedLayoutV2, | ||||
| } from "@refinedev/mui"; | ||||
|  | ||||
| import dataProvider from "@refinedev/simple-rest"; | ||||
| import CssBaseline from "@mui/material/CssBaseline"; | ||||
| import GlobalStyles from "@mui/material/GlobalStyles"; | ||||
| import { BrowserRouter, Route, Routes, Outlet } from "react-router"; | ||||
| import routerBindings, { | ||||
|   NavigateToResource, | ||||
|   CatchAllNavigate, | ||||
|   UnsavedChangesNotifier, | ||||
|   DocumentTitleHandler, | ||||
| } from "@refinedev/react-router"; | ||||
| import { | ||||
|   BlogPostList, | ||||
|   BlogPostCreate, | ||||
|   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() { | ||||
|   return ( | ||||
|     <BrowserRouter> | ||||
|       <GitHubBanner /> | ||||
|       <RefineKbarProvider> | ||||
|         <ColorModeContextProvider> | ||||
|           <CssBaseline /> | ||||
|           <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} /> | ||||
|           <RefineSnackbarProvider> | ||||
|             <DevtoolsProvider> | ||||
|               <Refine | ||||
|                 dataProvider={dataProvider("https://api.fake-rest.refine.dev")} | ||||
|                 notificationProvider={useNotificationProvider} | ||||
|                 routerProvider={routerBindings} | ||||
|                 authProvider={authProvider} | ||||
|                 resources={[ | ||||
|                   { | ||||
|                     name: "blog_posts", | ||||
|                     list: "/blog-posts", | ||||
|                     create: "/blog-posts/create", | ||||
|                     edit: "/blog-posts/edit/:id", | ||||
|                     show: "/blog-posts/show/:id", | ||||
|                     meta: { | ||||
|                       canDelete: true, | ||||
|                     }, | ||||
|                   }, | ||||
|                   { | ||||
|                     name: "categories", | ||||
|                     list: "/categories", | ||||
|                     create: "/categories/create", | ||||
|                     edit: "/categories/edit/:id", | ||||
|                     show: "/categories/show/:id", | ||||
|                     meta: { | ||||
|                       canDelete: true, | ||||
|                     }, | ||||
|                   }, | ||||
|                 ]} | ||||
|                 options={{ | ||||
|                   syncWithLocation: true, | ||||
|                   warnWhenUnsavedChanges: true, | ||||
|                   useNewQueryKeys: true, | ||||
|                   projectId: "Wv044J-t53S3s-PcbJGe", | ||||
|                 }} | ||||
|               > | ||||
|                 <Routes> | ||||
|                   <Route | ||||
|                     element={ | ||||
|                       <Authenticated | ||||
|                         key="authenticated-inner" | ||||
|                         fallback={<CatchAllNavigate to="/login" />} | ||||
|                       > | ||||
|                         <ThemedLayoutV2 Header={Header}> | ||||
|                           <Outlet /> | ||||
|                         </ThemedLayoutV2> | ||||
|                       </Authenticated> | ||||
|                     } | ||||
|                   > | ||||
|                     <Route | ||||
|                       index | ||||
|                       element={<NavigateToResource resource="blog_posts" />} | ||||
|                     /> | ||||
|                     <Route path="/blog-posts"> | ||||
|                       <Route index element={<BlogPostList />} /> | ||||
|                       <Route path="create" element={<BlogPostCreate />} /> | ||||
|                       <Route path="edit/:id" element={<BlogPostEdit />} /> | ||||
|                       <Route path="show/:id" element={<BlogPostShow />} /> | ||||
|                     </Route> | ||||
|                     <Route path="/categories"> | ||||
|                       <Route index element={<CategoryList />} /> | ||||
|                       <Route path="create" element={<CategoryCreate />} /> | ||||
|                       <Route path="edit/:id" element={<CategoryEdit />} /> | ||||
|                       <Route path="show/:id" element={<CategoryShow />} /> | ||||
|                     </Route> | ||||
|                     <Route path="*" element={<ErrorComponent />} /> | ||||
|                   </Route> | ||||
|                   <Route | ||||
|                     element={ | ||||
|                       <Authenticated | ||||
|                         key="authenticated-outer" | ||||
|                         fallback={<Outlet />} | ||||
|                       > | ||||
|                         <NavigateToResource /> | ||||
|                       </Authenticated> | ||||
|                     } | ||||
|                   > | ||||
|                     <Route path="/login" element={<Login />} /> | ||||
|                     <Route path="/register" element={<Register />} /> | ||||
|                     <Route | ||||
|                       path="/forgot-password" | ||||
|                       element={<ForgotPassword />} | ||||
|                     /> | ||||
|                   </Route> | ||||
|                 </Routes> | ||||
|  | ||||
|                 <RefineKbar /> | ||||
|                 <UnsavedChangesNotifier /> | ||||
|                 <DocumentTitleHandler /> | ||||
|               </Refine> | ||||
|               <DevtoolsPanel /> | ||||
|             </DevtoolsProvider> | ||||
|           </RefineSnackbarProvider> | ||||
|         </ColorModeContextProvider> | ||||
|       </RefineKbarProvider> | ||||
|     </BrowserRouter> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export default App; | ||||
							
								
								
									
										59
									
								
								src/authProvider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/authProvider.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import type { AuthProvider } from "@refinedev/core"; | ||||
|  | ||||
| export const TOKEN_KEY = "refine-auth"; | ||||
|  | ||||
| export const authProvider: AuthProvider = { | ||||
|   login: async ({ username, email, password }) => { | ||||
|     if ((username || email) && password) { | ||||
|       localStorage.setItem(TOKEN_KEY, username); | ||||
|       return { | ||||
|         success: true, | ||||
|         redirectTo: "/", | ||||
|       }; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       success: false, | ||||
|       error: { | ||||
|         name: "LoginError", | ||||
|         message: "Invalid username or password", | ||||
|       }, | ||||
|     }; | ||||
|   }, | ||||
|   logout: async () => { | ||||
|     localStorage.removeItem(TOKEN_KEY); | ||||
|     return { | ||||
|       success: true, | ||||
|       redirectTo: "/login", | ||||
|     }; | ||||
|   }, | ||||
|   check: async () => { | ||||
|     const token = localStorage.getItem(TOKEN_KEY); | ||||
|     if (token) { | ||||
|       return { | ||||
|         authenticated: true, | ||||
|       }; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       authenticated: false, | ||||
|       redirectTo: "/login", | ||||
|     }; | ||||
|   }, | ||||
|   getPermissions: async () => null, | ||||
|   getIdentity: async () => { | ||||
|     const token = localStorage.getItem(TOKEN_KEY); | ||||
|     if (token) { | ||||
|       return { | ||||
|         id: 1, | ||||
|         name: "John Doe", | ||||
|         avatar: "https://i.pravatar.cc/300", | ||||
|       }; | ||||
|     } | ||||
|     return null; | ||||
|   }, | ||||
|   onError: async (error) => { | ||||
|     console.error(error); | ||||
|     return { error }; | ||||
|   }, | ||||
| }; | ||||
							
								
								
									
										80
									
								
								src/components/header/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/components/header/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined"; | ||||
| import LightModeOutlined from "@mui/icons-material/LightModeOutlined"; | ||||
| import AppBar from "@mui/material/AppBar"; | ||||
| import Avatar from "@mui/material/Avatar"; | ||||
| import IconButton from "@mui/material/IconButton"; | ||||
| import Stack from "@mui/material/Stack"; | ||||
| import Toolbar from "@mui/material/Toolbar"; | ||||
| import Typography from "@mui/material/Typography"; | ||||
| import { useGetIdentity } from "@refinedev/core"; | ||||
| import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from "@refinedev/mui"; | ||||
| import React, { useContext } from "react"; | ||||
| import { ColorModeContext } from "../../contexts/color-mode"; | ||||
|  | ||||
| type IUser = { | ||||
|   id: number; | ||||
|   name: string; | ||||
|   avatar: string; | ||||
| }; | ||||
|  | ||||
| export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({ | ||||
|   sticky = true, | ||||
| }) => { | ||||
|   const { mode, setMode } = useContext(ColorModeContext); | ||||
|  | ||||
|   const { data: user } = useGetIdentity<IUser>(); | ||||
|  | ||||
|   return ( | ||||
|     <AppBar position={sticky ? "sticky" : "relative"}> | ||||
|       <Toolbar> | ||||
|         <Stack | ||||
|           direction="row" | ||||
|           width="100%" | ||||
|           justifyContent="flex-end" | ||||
|           alignItems="center" | ||||
|         > | ||||
|           <HamburgerMenu /> | ||||
|           <Stack | ||||
|             direction="row" | ||||
|             width="100%" | ||||
|             justifyContent="flex-end" | ||||
|             alignItems="center" | ||||
|           > | ||||
|             <IconButton | ||||
|               color="inherit" | ||||
|               onClick={() => { | ||||
|                 setMode(); | ||||
|               }} | ||||
|             > | ||||
|               {mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />} | ||||
|             </IconButton> | ||||
|  | ||||
|             {(user?.avatar || user?.name) && ( | ||||
|               <Stack | ||||
|                 direction="row" | ||||
|                 gap="16px" | ||||
|                 alignItems="center" | ||||
|                 justifyContent="center" | ||||
|               > | ||||
|                 {user?.name && ( | ||||
|                   <Typography | ||||
|                     sx={{ | ||||
|                       display: { | ||||
|                         xs: "none", | ||||
|                         sm: "inline-block", | ||||
|                       }, | ||||
|                     }} | ||||
|                     variant="subtitle2" | ||||
|                   > | ||||
|                     {user?.name} | ||||
|                   </Typography> | ||||
|                 )} | ||||
|                 <Avatar src={user?.avatar} alt={user?.name} /> | ||||
|               </Stack> | ||||
|             )} | ||||
|           </Stack> | ||||
|         </Stack> | ||||
|       </Toolbar> | ||||
|     </AppBar> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										1
									
								
								src/components/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/components/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export { Header } from "./header"; | ||||
							
								
								
									
										59
									
								
								src/contexts/color-mode/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/contexts/color-mode/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import React, { | ||||
|   PropsWithChildren, | ||||
|   createContext, | ||||
|   useEffect, | ||||
|   useState, | ||||
| } from "react"; | ||||
| import { ThemeProvider } from "@mui/material/styles"; | ||||
| import { RefineThemes } from "@refinedev/mui"; | ||||
|  | ||||
| type ColorModeContextType = { | ||||
|   mode: string; | ||||
|   setMode: () => void; | ||||
| }; | ||||
|  | ||||
| export const ColorModeContext = createContext<ColorModeContextType>( | ||||
|   {} as ColorModeContextType | ||||
| ); | ||||
|  | ||||
| export const ColorModeContextProvider: React.FC<PropsWithChildren> = ({ | ||||
|   children, | ||||
| }) => { | ||||
|   const colorModeFromLocalStorage = localStorage.getItem("colorMode"); | ||||
|   const isSystemPreferenceDark = window?.matchMedia( | ||||
|     "(prefers-color-scheme: dark)" | ||||
|   ).matches; | ||||
|  | ||||
|   const systemPreference = isSystemPreferenceDark ? "dark" : "light"; | ||||
|   const [mode, setMode] = useState( | ||||
|     colorModeFromLocalStorage || systemPreference | ||||
|   ); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     window.localStorage.setItem("colorMode", mode); | ||||
|   }, [mode]); | ||||
|  | ||||
|   const setColorMode = () => { | ||||
|     if (mode === "light") { | ||||
|       setMode("dark"); | ||||
|     } else { | ||||
|       setMode("light"); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <ColorModeContext.Provider | ||||
|       value={{ | ||||
|         setMode: setColorMode, | ||||
|         mode, | ||||
|       }} | ||||
|     > | ||||
|       <ThemeProvider | ||||
|         // you can change the theme colors here. example: mode === "light" ? RefineThemes.Magenta : RefineThemes.MagentaDark | ||||
|         theme={mode === "light" ? RefineThemes.Blue : RefineThemes.BlueDark} | ||||
|       > | ||||
|         {children} | ||||
|       </ThemeProvider> | ||||
|     </ColorModeContext.Provider> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										13
									
								
								src/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import React from "react"; | ||||
| import { createRoot } from "react-dom/client"; | ||||
|  | ||||
| import App from "./App"; | ||||
|  | ||||
| const container = document.getElementById("root") as HTMLElement; | ||||
| const root = createRoot(container); | ||||
|  | ||||
| root.render( | ||||
|   <React.StrictMode> | ||||
|     <App /> | ||||
|   </React.StrictMode> | ||||
| ); | ||||
							
								
								
									
										120
									
								
								src/pages/blog-posts/create.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/pages/blog-posts/create.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| import { Autocomplete, Box, MenuItem, Select, TextField } from "@mui/material"; | ||||
| import { Create, useAutocomplete } from "@refinedev/mui"; | ||||
| import { useForm } from "@refinedev/react-hook-form"; | ||||
| import React from "react"; | ||||
| import { Controller } from "react-hook-form"; | ||||
|  | ||||
| export const BlogPostCreate = () => { | ||||
|   const { | ||||
|     saveButtonProps, | ||||
|     refineCore: { formLoading }, | ||||
|     register, | ||||
|     control, | ||||
|     formState: { errors }, | ||||
|   } = useForm({}); | ||||
|  | ||||
|   const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({ | ||||
|     resource: "categories", | ||||
|   }); | ||||
|  | ||||
|   return ( | ||||
|     <Create isLoading={formLoading} saveButtonProps={saveButtonProps}> | ||||
|       <Box | ||||
|         component="form" | ||||
|         sx={{ display: "flex", flexDirection: "column" }} | ||||
|         autoComplete="off" | ||||
|       > | ||||
|         <TextField | ||||
|           {...register("title", { | ||||
|             required: "This field is required", | ||||
|           })} | ||||
|           error={!!(errors as any)?.title} | ||||
|           helperText={(errors as any)?.title?.message} | ||||
|           margin="normal" | ||||
|           fullWidth | ||||
|           InputLabelProps={{ shrink: true }} | ||||
|           type="text" | ||||
|           label={"Title"} | ||||
|           name="title" | ||||
|         /> | ||||
|         <TextField | ||||
|           {...register("content", { | ||||
|             required: "This field is required", | ||||
|           })} | ||||
|           error={!!(errors as any)?.content} | ||||
|           helperText={(errors as any)?.content?.message} | ||||
|           margin="normal" | ||||
|           fullWidth | ||||
|           InputLabelProps={{ shrink: true }} | ||||
|           multiline | ||||
|           label={"Content"} | ||||
|           name="content" | ||||
|         /> | ||||
|         <Controller | ||||
|           control={control} | ||||
|           name={"category.id"} | ||||
|           rules={{ required: "This field is required" }} | ||||
|           // eslint-disable-next-line | ||||
|           defaultValue={null as any} | ||||
|           render={({ field }) => ( | ||||
|             <Autocomplete | ||||
|               {...categoryAutocompleteProps} | ||||
|               {...field} | ||||
|               onChange={(_, value) => { | ||||
|                 field.onChange(value.id); | ||||
|               }} | ||||
|               getOptionLabel={(item) => { | ||||
|                 return ( | ||||
|                   categoryAutocompleteProps?.options?.find((p) => { | ||||
|                     const itemId = | ||||
|                       typeof item === "object" | ||||
|                         ? item?.id?.toString() | ||||
|                         : item?.toString(); | ||||
|                     const pId = p?.id?.toString(); | ||||
|                     return itemId === pId; | ||||
|                   })?.title ?? "" | ||||
|                 ); | ||||
|               }} | ||||
|               isOptionEqualToValue={(option, value) => { | ||||
|                 const optionId = option?.id?.toString(); | ||||
|                 const valueId = | ||||
|                   typeof value === "object" | ||||
|                     ? value?.id?.toString() | ||||
|                     : value?.toString(); | ||||
|                 return value === undefined || optionId === valueId; | ||||
|               }} | ||||
|               renderInput={(params) => ( | ||||
|                 <TextField | ||||
|                   {...params} | ||||
|                   label={"Category"} | ||||
|                   margin="normal" | ||||
|                   variant="outlined" | ||||
|                   error={!!(errors as any)?.category?.id} | ||||
|                   helperText={(errors as any)?.category?.id?.message} | ||||
|                   required | ||||
|                 /> | ||||
|               )} | ||||
|             /> | ||||
|           )} | ||||
|         /> | ||||
|         <Controller | ||||
|           name="status" | ||||
|           control={control} | ||||
|           render={({ field }) => { | ||||
|             return ( | ||||
|               <Select | ||||
|                 {...field} | ||||
|                 value={field?.value || "draft"} | ||||
|                 label={"Status"} | ||||
|               > | ||||
|                 <MenuItem value="draft">Draft</MenuItem> | ||||
|                 <MenuItem value="published">Published</MenuItem> | ||||
|                 <MenuItem value="rejected">Rejected</MenuItem> | ||||
|               </Select> | ||||
|             ); | ||||
|           }} | ||||
|         /> | ||||
|       </Box> | ||||
|     </Create> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										125
									
								
								src/pages/blog-posts/edit.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/pages/blog-posts/edit.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| import { Autocomplete, Box, Select, TextField } from "@mui/material"; | ||||
| import MenuItem from "@mui/material/MenuItem"; | ||||
| import { Edit, useAutocomplete } from "@refinedev/mui"; | ||||
| import { useForm } from "@refinedev/react-hook-form"; | ||||
| import React from "react"; | ||||
| import { Controller } from "react-hook-form"; | ||||
|  | ||||
| export const BlogPostEdit = () => { | ||||
|   const { | ||||
|     saveButtonProps, | ||||
|     refineCore: { queryResult, formLoading }, | ||||
|     register, | ||||
|     control, | ||||
|     formState: { errors }, | ||||
|   } = useForm({}); | ||||
|  | ||||
|   const blogPostsData = queryResult?.data?.data; | ||||
|  | ||||
|   const { autocompleteProps: categoryAutocompleteProps } = useAutocomplete({ | ||||
|     resource: "categories", | ||||
|     defaultValue: blogPostsData?.category?.id, | ||||
|   }); | ||||
|  | ||||
|   return ( | ||||
|     <Edit isLoading={formLoading} saveButtonProps={saveButtonProps}> | ||||
|       <Box | ||||
|         component="form" | ||||
|         sx={{ display: "flex", flexDirection: "column" }} | ||||
|         autoComplete="off" | ||||
|       > | ||||
|         <TextField | ||||
|           {...register("title", { | ||||
|             required: "This field is required", | ||||
|           })} | ||||
|           error={!!(errors as any)?.title} | ||||
|           helperText={(errors as any)?.title?.message} | ||||
|           margin="normal" | ||||
|           fullWidth | ||||
|           InputLabelProps={{ shrink: true }} | ||||
|           type="text" | ||||
|           label={"Title"} | ||||
|           name="title" | ||||
|         /> | ||||
|         <TextField | ||||
|           {...register("content", { | ||||
|             required: "This field is required", | ||||
|           })} | ||||
|           error={!!(errors as any)?.content} | ||||
|           helperText={(errors as any)?.content?.message} | ||||
|           margin="normal" | ||||
|           fullWidth | ||||
|           InputLabelProps={{ shrink: true }} | ||||
|           multiline | ||||
|           label={"Content"} | ||||
|           name="content" | ||||
|           rows={4} | ||||
|         /> | ||||
|         <Controller | ||||
|           control={control} | ||||
|           name={"category.id"} | ||||
|           rules={{ required: "This field is required" }} | ||||
|           // eslint-disable-next-line | ||||
|           defaultValue={null as any} | ||||
|           render={({ field }) => ( | ||||
|             <Autocomplete | ||||
|               {...categoryAutocompleteProps} | ||||
|               {...field} | ||||
|               onChange={(_, value) => { | ||||
|                 field.onChange(value.id); | ||||
|               }} | ||||
|               getOptionLabel={(item) => { | ||||
|                 return ( | ||||
|                   categoryAutocompleteProps?.options?.find((p) => { | ||||
|                     const itemId = | ||||
|                       typeof item === "object" | ||||
|                         ? item?.id?.toString() | ||||
|                         : item?.toString(); | ||||
|                     const pId = p?.id?.toString(); | ||||
|                     return itemId === pId; | ||||
|                   })?.title ?? "" | ||||
|                 ); | ||||
|               }} | ||||
|               isOptionEqualToValue={(option, value) => { | ||||
|                 const optionId = option?.id?.toString(); | ||||
|                 const valueId = | ||||
|                   typeof value === "object" | ||||
|                     ? value?.id?.toString() | ||||
|                     : value?.toString(); | ||||
|                 return value === undefined || optionId === valueId; | ||||
|               }} | ||||
|               renderInput={(params) => ( | ||||
|                 <TextField | ||||
|                   {...params} | ||||
|                   label={"Category"} | ||||
|                   margin="normal" | ||||
|                   variant="outlined" | ||||
|                   error={!!(errors as any)?.category?.id} | ||||
|                   helperText={(errors as any)?.category?.id?.message} | ||||
|                   required | ||||
|                 /> | ||||
|               )} | ||||
|             /> | ||||
|           )} | ||||
|         /> | ||||
|         <Controller | ||||
|           name="status" | ||||
|           control={control} | ||||
|           render={({ field }) => { | ||||
|             return ( | ||||
|               <Select | ||||
|                 {...field} | ||||
|                 value={field?.value || "draft"} | ||||
|                 label={"Status"} | ||||
|               > | ||||
|                 <MenuItem value="draft">Draft</MenuItem> | ||||
|                 <MenuItem value="published">Published</MenuItem> | ||||
|                 <MenuItem value="rejected">Rejected</MenuItem> | ||||
|               </Select> | ||||
|             ); | ||||
|           }} | ||||
|         /> | ||||
|       </Box> | ||||
|     </Edit> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										4
									
								
								src/pages/blog-posts/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/pages/blog-posts/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export * from "./create"; | ||||
| export * from "./edit"; | ||||
| export * from "./list"; | ||||
| export * from "./show"; | ||||
							
								
								
									
										124
									
								
								src/pages/blog-posts/list.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/pages/blog-posts/list.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| import { DataGrid, type GridColDef } from "@mui/x-data-grid"; | ||||
| import { useMany } from "@refinedev/core"; | ||||
| import { | ||||
|   DateField, | ||||
|   DeleteButton, | ||||
|   EditButton, | ||||
|   List, | ||||
|   ShowButton, | ||||
|   useDataGrid, | ||||
| } from "@refinedev/mui"; | ||||
| import { Typography } from "@mui/material"; | ||||
| import React from "react"; | ||||
|  | ||||
| export const BlogPostList = () => { | ||||
|   const { dataGridProps } = useDataGrid({}); | ||||
|  | ||||
|   const { data: categoryData, isLoading: categoryIsLoading } = useMany({ | ||||
|     resource: "categories", | ||||
|     ids: | ||||
|       dataGridProps?.rows | ||||
|         ?.map((item: any) => item?.category?.id) | ||||
|         .filter(Boolean) ?? [], | ||||
|     queryOptions: { | ||||
|       enabled: !!dataGridProps?.rows, | ||||
|     }, | ||||
|   }); | ||||
|  | ||||
|   const columns = React.useMemo<GridColDef[]>( | ||||
|     () => [ | ||||
|       { | ||||
|         field: "id", | ||||
|         headerName: "ID", | ||||
|         type: "number", | ||||
|         minWidth: 50, | ||||
|         display: "flex", | ||||
|         align: "left", | ||||
|         headerAlign: "left", | ||||
|       }, | ||||
|       { | ||||
|         field: "title", | ||||
|         headerName: "Title", | ||||
|         minWidth: 200, | ||||
|         display: "flex", | ||||
|       }, | ||||
|       { | ||||
|         field: "content", | ||||
|         flex: 1, | ||||
|         headerName: "Content", | ||||
|         minWidth: 250, | ||||
|         display: "flex", | ||||
|         renderCell: function render({ value }) { | ||||
|           if (!value) return "-"; | ||||
|           return ( | ||||
|             <Typography | ||||
|               component="p" | ||||
|               whiteSpace="pre" | ||||
|               overflow="hidden" | ||||
|               textOverflow="ellipsis" | ||||
|             > | ||||
|               {value} | ||||
|             </Typography> | ||||
|           ); | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         field: "category", | ||||
|         headerName: "Category", | ||||
|         minWidth: 160, | ||||
|         display: "flex", | ||||
|         valueGetter: (_, row) => { | ||||
|           const value = row?.category; | ||||
|           return value; | ||||
|         }, | ||||
|         renderCell: function render({ value }) { | ||||
|           return categoryIsLoading ? ( | ||||
|             <>Loading...</> | ||||
|           ) : ( | ||||
|             categoryData?.data?.find((item) => item.id === value?.id)?.title | ||||
|           ); | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         field: "status", | ||||
|         headerName: "Status", | ||||
|         minWidth: 80, | ||||
|         display: "flex", | ||||
|       }, | ||||
|       { | ||||
|         field: "createdAt", | ||||
|         headerName: "Created at", | ||||
|         minWidth: 120, | ||||
|         display: "flex", | ||||
|         renderCell: function render({ value }) { | ||||
|           return <DateField value={value} />; | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         field: "actions", | ||||
|         headerName: "Actions", | ||||
|         align: "right", | ||||
|         headerAlign: "right", | ||||
|         minWidth: 120, | ||||
|         sortable: false, | ||||
|         display: "flex", | ||||
|         renderCell: function render({ row }) { | ||||
|           return ( | ||||
|             <> | ||||
|               <EditButton hideText recordItemId={row.id} /> | ||||
|               <ShowButton hideText recordItemId={row.id} /> | ||||
|               <DeleteButton hideText recordItemId={row.id} /> | ||||
|             </> | ||||
|           ); | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|     [categoryData, categoryIsLoading] | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <List> | ||||
|       <DataGrid {...dataGridProps} columns={columns} /> | ||||
|     </List> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										59
									
								
								src/pages/blog-posts/show.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/pages/blog-posts/show.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import { Stack, Typography } from "@mui/material"; | ||||
| import { useOne, useShow } from "@refinedev/core"; | ||||
| import { | ||||
|   DateField, | ||||
|   MarkdownField, | ||||
|   NumberField, | ||||
|   Show, | ||||
|   TextFieldComponent as TextField, | ||||
| } from "@refinedev/mui"; | ||||
|  | ||||
| export const BlogPostShow = () => { | ||||
|   const { query } = useShow({}); | ||||
|  | ||||
|   const { data, isLoading } = query; | ||||
|  | ||||
|   const record = data?.data; | ||||
|  | ||||
|   const { data: categoryData, isLoading: categoryIsLoading } = useOne({ | ||||
|     resource: "categories", | ||||
|     id: record?.category?.id || "", | ||||
|     queryOptions: { | ||||
|       enabled: !!record, | ||||
|     }, | ||||
|   }); | ||||
|  | ||||
|   return ( | ||||
|     <Show isLoading={isLoading}> | ||||
|       <Stack gap={1}> | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"ID"} | ||||
|         </Typography> | ||||
|         <TextField value={record?.id} /> | ||||
|  | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"Title"} | ||||
|         </Typography> | ||||
|         <TextField value={record?.title} /> | ||||
|  | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"Content"} | ||||
|         </Typography> | ||||
|         <MarkdownField value={record?.content} /> | ||||
|  | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"Category"} | ||||
|         </Typography> | ||||
|         {categoryIsLoading ? <>Loading...</> : <>{categoryData?.data?.title}</>} | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"Status"} | ||||
|         </Typography> | ||||
|         <TextField value={record?.status} /> | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"CreatedAt"} | ||||
|         </Typography> | ||||
|         <DateField value={record?.createdAt} /> | ||||
|       </Stack> | ||||
|     </Show> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										36
									
								
								src/pages/categories/create.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/pages/categories/create.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| import { Box, TextField } from "@mui/material"; | ||||
| import { Create } from "@refinedev/mui"; | ||||
| import { useForm } from "@refinedev/react-hook-form"; | ||||
|  | ||||
| export const CategoryCreate = () => { | ||||
|   const { | ||||
|     saveButtonProps, | ||||
|     refineCore: { formLoading }, | ||||
|     register, | ||||
|     formState: { errors }, | ||||
|   } = useForm({}); | ||||
|  | ||||
|   return ( | ||||
|     <Create isLoading={formLoading} saveButtonProps={saveButtonProps}> | ||||
|       <Box | ||||
|         component="form" | ||||
|         sx={{ display: "flex", flexDirection: "column" }} | ||||
|         autoComplete="off" | ||||
|       > | ||||
|         <TextField | ||||
|           {...register("title", { | ||||
|             required: "This field is required", | ||||
|           })} | ||||
|           error={!!(errors as any)?.title} | ||||
|           helperText={(errors as any)?.title?.message} | ||||
|           margin="normal" | ||||
|           fullWidth | ||||
|           InputLabelProps={{ shrink: true }} | ||||
|           type="text" | ||||
|           label={"Title"} | ||||
|           name="title" | ||||
|         /> | ||||
|       </Box> | ||||
|     </Create> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										35
									
								
								src/pages/categories/edit.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/pages/categories/edit.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import { Box, TextField } from "@mui/material"; | ||||
| import { Edit } from "@refinedev/mui"; | ||||
| import { useForm } from "@refinedev/react-hook-form"; | ||||
|  | ||||
| export const CategoryEdit = () => { | ||||
|   const { | ||||
|     saveButtonProps, | ||||
|     register, | ||||
|     formState: { errors }, | ||||
|   } = useForm({}); | ||||
|  | ||||
|   return ( | ||||
|     <Edit saveButtonProps={saveButtonProps}> | ||||
|       <Box | ||||
|         component="form" | ||||
|         sx={{ display: "flex", flexDirection: "column" }} | ||||
|         autoComplete="off" | ||||
|       > | ||||
|         <TextField | ||||
|           {...register("title", { | ||||
|             required: "This field is required", | ||||
|           })} | ||||
|           error={!!(errors as any)?.title} | ||||
|           helperText={(errors as any)?.title?.message} | ||||
|           margin="normal" | ||||
|           fullWidth | ||||
|           InputLabelProps={{ shrink: true }} | ||||
|           type="text" | ||||
|           label={"Title"} | ||||
|           name="title" | ||||
|         /> | ||||
|       </Box> | ||||
|     </Edit> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										4
									
								
								src/pages/categories/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/pages/categories/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export * from "./create"; | ||||
| export * from "./edit"; | ||||
| export * from "./list"; | ||||
| export * from "./show"; | ||||
							
								
								
									
										59
									
								
								src/pages/categories/list.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/pages/categories/list.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import { DataGrid, type GridColDef } from "@mui/x-data-grid"; | ||||
| import { | ||||
|   DeleteButton, | ||||
|   EditButton, | ||||
|   List, | ||||
|   ShowButton, | ||||
|   useDataGrid, | ||||
| } from "@refinedev/mui"; | ||||
| import React from "react"; | ||||
|  | ||||
| export const CategoryList = () => { | ||||
|   const { dataGridProps } = useDataGrid({}); | ||||
|  | ||||
|   const columns = React.useMemo<GridColDef[]>( | ||||
|     () => [ | ||||
|       { | ||||
|         field: "id", | ||||
|         headerName: "ID", | ||||
|         type: "number", | ||||
|         minWidth: 50, | ||||
|         display: "flex", | ||||
|         align: "left", | ||||
|         headerAlign: "left", | ||||
|       }, | ||||
|       { | ||||
|         field: "title", | ||||
|         flex: 1, | ||||
|         headerName: "Title", | ||||
|         minWidth: 200, | ||||
|         display: "flex", | ||||
|       }, | ||||
|       { | ||||
|         field: "actions", | ||||
|         headerName: "Actions", | ||||
|         align: "right", | ||||
|         headerAlign: "right", | ||||
|         minWidth: 120, | ||||
|         sortable: false, | ||||
|         display: "flex", | ||||
|         renderCell: function render({ row }) { | ||||
|           return ( | ||||
|             <> | ||||
|               <EditButton hideText recordItemId={row.id} /> | ||||
|               <ShowButton hideText recordItemId={row.id} /> | ||||
|               <DeleteButton hideText recordItemId={row.id} /> | ||||
|             </> | ||||
|           ); | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|     [] | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <List> | ||||
|       <DataGrid {...dataGridProps} columns={columns} /> | ||||
|     </List> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										29
									
								
								src/pages/categories/show.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/pages/categories/show.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| import { Stack, Typography } from "@mui/material"; | ||||
| import { useShow } from "@refinedev/core"; | ||||
| import { | ||||
|   NumberField, | ||||
|   Show, | ||||
|   TextFieldComponent as TextField, | ||||
| } from "@refinedev/mui"; | ||||
|  | ||||
| export const CategoryShow = () => { | ||||
|   const { query } = useShow({}); | ||||
|   const { data, isLoading } = query; | ||||
|  | ||||
|   const record = data?.data; | ||||
|  | ||||
|   return ( | ||||
|     <Show isLoading={isLoading}> | ||||
|       <Stack gap={1}> | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"ID"} | ||||
|         </Typography> | ||||
|         <TextField value={record?.id} /> | ||||
|         <Typography variant="body1" fontWeight="bold"> | ||||
|           {"Title"} | ||||
|         </Typography> | ||||
|         <TextField value={record?.title} /> | ||||
|       </Stack> | ||||
|     </Show> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										5
									
								
								src/pages/forgotPassword/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/pages/forgotPassword/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| import { AuthPage } from "@refinedev/mui"; | ||||
|  | ||||
| export const ForgotPassword = () => { | ||||
|   return <AuthPage type="forgotPassword" />; | ||||
| }; | ||||
							
								
								
									
										12
									
								
								src/pages/login/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/pages/login/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| import { AuthPage } from "@refinedev/mui"; | ||||
|  | ||||
| export const Login = () => { | ||||
|   return ( | ||||
|     <AuthPage | ||||
|       type="login" | ||||
|       formProps={{ | ||||
|         defaultValues: { email: "demo@refine.dev", password: "demodemo" }, | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										5
									
								
								src/pages/register/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/pages/register/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| import { AuthPage } from "@refinedev/mui"; | ||||
|  | ||||
| export const Register = () => { | ||||
|   return <AuthPage type="register" />; | ||||
| }; | ||||
							
								
								
									
										1
									
								
								src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| /// <reference types="vite/client" /> | ||||
							
								
								
									
										25
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "target": "ESNext", | ||||
|     "useDefineForClassFields": true, | ||||
|     "lib": ["DOM", "DOM.Iterable", "ESNext"], | ||||
|     "allowJs": false, | ||||
|     "skipLibCheck": true, | ||||
|     "esModuleInterop": false, | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "strict": true, | ||||
|     "forceConsistentCasingInFileNames": true, | ||||
|     "module": "ESNext", | ||||
|     "moduleResolution": "Node", | ||||
|     "resolveJsonModule": true, | ||||
|     "isolatedModules": true, | ||||
|     "noEmit": true, | ||||
|     "jsx": "react-jsx" | ||||
|   }, | ||||
|   "include": ["src"], | ||||
|   "references": [ | ||||
|     { | ||||
|       "path": "./tsconfig.node.json" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										8
									
								
								tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "composite": true, | ||||
|     "module": "ESNext", | ||||
|     "moduleResolution": "node" | ||||
|   }, | ||||
|   "include": ["vite.config.ts"] | ||||
| } | ||||
							
								
								
									
										6
									
								
								vite.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vite.config.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import { defineConfig } from "vite"; | ||||
| import react from "@vitejs/plugin-react"; | ||||
|  | ||||
| export default defineConfig({ | ||||
|   plugins: [react()], | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user