Initial commit
This commit is contained in:
commit
aa289d14ce
2
.env.development
Normal file
2
.env.development
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
VITE_APP_NAME=Frez Manager [Developer Build]
|
||||||
|
VITE_API_URL=http://localhost:7082
|
2
.env.production
Normal file
2
.env.production
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
VITE_APP_NAME=Frez Manager
|
||||||
|
VITE_API_URL=http://192.168.179.36:7082
|
11
.eslintrc.cjs
Normal file
11
.eslintrc.cjs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: { browser: true, es2020: true, node: true },
|
||||||
|
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended", "prettier"],
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
|
||||||
|
plugins: ["react-refresh", "prettier"],
|
||||||
|
rules: {
|
||||||
|
"react-refresh/only-export-components": "warn",
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
},
|
||||||
|
};
|
153
.gitignore
vendored
Normal file
153
.gitignore
vendored
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# ---> Node
|
||||||
|
# ---> Node
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
=======
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
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?
|
||||||
|
package-lock.json
|
12
.prettierrc.cjs
Normal file
12
.prettierrc.cjs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module.exports = {
|
||||||
|
printWidth: 120,
|
||||||
|
trailingComma: "all",
|
||||||
|
singleQuote: false,
|
||||||
|
semi: true,
|
||||||
|
arrowParens: "always",
|
||||||
|
jsxSingleQuote: false,
|
||||||
|
bracketSameLine: false,
|
||||||
|
endOfLine: "lf",
|
||||||
|
useTabs: true,
|
||||||
|
tabWidth: 1,
|
||||||
|
};
|
12
README.md
Normal file
12
README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# FrezManager
|
||||||
|
|
||||||
|
Nowoczesna platforma dedykowana specjalistom z branży obróbki skrawaniem. Nasza strona umożliwia profesjonalny dobór narzędzi do frezowania, gwarantując precyzję i efektywność w każdym projekcie. Dzięki intuicyjnemu interfejsowi i zaawansowanym algorytmom, Frez Manager staje się niezastąpionym narzędziem dla każdego, kto chce osiągnąć perfekcyjne rezultaty w obróbce materiałów.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Setup `VITE_APP_NAME` in `.env.{mode}` files
|
||||||
|
2. run: `bun run dev`
|
||||||
|
3. To build for production, run: `bun run build`
|
||||||
|
|
||||||
|
> - Use <https://daisyui.com/components> for components styling and <https://www.radix-ui.com/docs/primitives/overview/introduction> for components
|
||||||
|
> - `bun` commands can be replaced with you favorite package manager
|
1
daisyui.config.js
Normal file
1
daisyui.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const themes = ["acid"];
|
12
index.html
Normal file
12
index.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="/icon.png" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
47
package.json
Normal file
47
package.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"name": "app",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "vite preview --host"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hookform/resolvers": "^3.3.2",
|
||||||
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
"@types/react-router-dom": "^5.3.3",
|
||||||
|
"axios": "^1.5.1",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-hook-form": "^7.47.0",
|
||||||
|
"react-router-dom": "^6.17.0",
|
||||||
|
"uuid": "^9.0.1",
|
||||||
|
"zod": "^3.22.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
|
"@types/node": "^20.8.7",
|
||||||
|
"@types/react": "^18.2.29",
|
||||||
|
"@types/react-dom": "^18.2.14",
|
||||||
|
"@types/uuid": "^9.0.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
||||||
|
"@typescript-eslint/parser": "^6.8.0",
|
||||||
|
"@vitejs/plugin-react": "^4.1.0",
|
||||||
|
"autoprefixer": "^10.4.16",
|
||||||
|
"daisyui": "^3.9.3",
|
||||||
|
"eslint": "^8.51.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-plugin-prettier": "^5.0.1",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.3",
|
||||||
|
"postcss": "^8.4.31",
|
||||||
|
"prettier": "^3.0.3",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
|
"terser": "^5.22.0",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^4.5.0"
|
||||||
|
}
|
||||||
|
}
|
2645
pnpm-lock.yaml
generated
Normal file
2645
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
BIN
public/icon.png
Normal file
BIN
public/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
98
src/api/AxiosService.ts
Normal file
98
src/api/AxiosService.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
// TODO: Add calls for notifications on varius states of the request
|
||||||
|
|
||||||
|
export const setupAxiosInterceptors = () => {
|
||||||
|
axios.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
// Do something before request is sent
|
||||||
|
return Promise.resolve(config);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
// Do something with request error
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
axios.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
// Do something with response data
|
||||||
|
const status = response.status;
|
||||||
|
if (status === 200 && response.data) {
|
||||||
|
console.error(response.data);
|
||||||
|
}
|
||||||
|
// Check if the status code is 401 (requires user authentication)
|
||||||
|
if (status === 401) {
|
||||||
|
console.error(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(response);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
// Do something with response error
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAxiosError = (error: any, callbackFunction = null) => {
|
||||||
|
if (error.message == "canceled") {
|
||||||
|
callbackFunction && console.debug(callbackFunction);
|
||||||
|
console.debug("Request Aborted");
|
||||||
|
} else if (error.response) {
|
||||||
|
// Request made and server responded
|
||||||
|
console.log(error.response.data);
|
||||||
|
console.log(error.response.status);
|
||||||
|
console.log(error.response.headers);
|
||||||
|
} else if (error.request) {
|
||||||
|
// The request was made but no response was received
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
// Something happened in setting up the request that triggered an Error
|
||||||
|
console.log("Error", error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const axiosGet = async (url: string) => {
|
||||||
|
return axios
|
||||||
|
.get(url)
|
||||||
|
.then((response) => {
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
handleAxiosError(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const axiosPost = async (url: string, data: any) => {
|
||||||
|
return axios
|
||||||
|
.post(url, data)
|
||||||
|
.then((response) => {
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
handleAxiosError(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const axiosPut = async (url: string, data: any) => {
|
||||||
|
return axios
|
||||||
|
.put(url, data)
|
||||||
|
.then((response) => {
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
handleAxiosError(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const axiosDelete = async (url: string) => {
|
||||||
|
return axios
|
||||||
|
.delete(url)
|
||||||
|
.then((response) => {
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
handleAxiosError(error);
|
||||||
|
});
|
||||||
|
};
|
72
src/components/Breadcrumbs.tsx
Normal file
72
src/components/Breadcrumbs.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { Link, useLocation, useParams } from "react-router-dom";
|
||||||
|
import { findElementInFlatRoutes } from "../utils/RoutingTableUtils";
|
||||||
|
import { capitalizeFirstLetter } from "../utils/StringTransformationUtils";
|
||||||
|
|
||||||
|
const BREADCRUMB_BAR_CLASSES =
|
||||||
|
"text-sm breadcrumbs max-w pl-6 shadow-neutral-focus shadow-sm sticky top-2 z-10 bg-neutral transition-all rounded-md m-2 w-auto";
|
||||||
|
|
||||||
|
const Breadcrumbs = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const pathnames = location.pathname.split("/").filter((x) => x);
|
||||||
|
const pathParams = useParams();
|
||||||
|
|
||||||
|
const hideBreadcrumbBar = (path: string) => {
|
||||||
|
// if (Object.keys(pathParams).length !== 0) return true;
|
||||||
|
const _route = findElementInFlatRoutes(path);
|
||||||
|
return _route?.disableBreadcrumbBar || false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findRouteNameByPath = (path: string) => {
|
||||||
|
const _route = findElementInFlatRoutes(path);
|
||||||
|
return _route?.name || path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderLinkBreadcrumb = (
|
||||||
|
value: string,
|
||||||
|
index: number,
|
||||||
|
pathnames: string[],
|
||||||
|
) => {
|
||||||
|
if (value === pathParams.id) return null;
|
||||||
|
const to = `/${pathnames.slice(0, index + 1).join("/")}`;
|
||||||
|
const routeName = capitalizeFirstLetter(findRouteNameByPath(value));
|
||||||
|
return (
|
||||||
|
<li key={to} className="text-neutral-content">
|
||||||
|
<Link to={to}>{routeName}</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTextBreadcrumb = (value: string) => {
|
||||||
|
if (value === pathParams.id) return null;
|
||||||
|
const routeName = capitalizeFirstLetter(findRouteNameByPath(value));
|
||||||
|
return (
|
||||||
|
<li key={value} className="text-neutral-content">
|
||||||
|
{routeName}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hideBreadcrumbBar(location.pathname)) {
|
||||||
|
return (
|
||||||
|
<div className={BREADCRUMB_BAR_CLASSES}>{pathnames[0].toUpperCase()}</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={BREADCRUMB_BAR_CLASSES}>
|
||||||
|
<ul>
|
||||||
|
<li className="text-neutral-content">
|
||||||
|
<Link to="/">Home</Link>
|
||||||
|
</li>
|
||||||
|
{pathnames.map((value, index) => {
|
||||||
|
const isLast = index === pathnames.length - 1;
|
||||||
|
return isLast
|
||||||
|
? renderTextBreadcrumb(value)
|
||||||
|
: renderLinkBreadcrumb(value, index, pathnames);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Breadcrumbs;
|
35
src/components/Card.tsx
Normal file
35
src/components/Card.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
interface CardProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
bgImage?: string;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Card component
|
||||||
|
* @param {string} title - Card title
|
||||||
|
* @param {string} description - Card description
|
||||||
|
* @param {string} bgImage - Background image
|
||||||
|
* @param {string} link - Link to open on button click
|
||||||
|
*/
|
||||||
|
export function Card({ title, description, bgImage, link }: CardProps) {
|
||||||
|
return (
|
||||||
|
<div className="card min-w-[230px] bg-neutral text-neutral-content">
|
||||||
|
{!!bgImage && (
|
||||||
|
<figure>
|
||||||
|
<img src={bgImage} alt={title + "_bgImage"} />
|
||||||
|
</figure>
|
||||||
|
)}
|
||||||
|
<div className="card-body items-center text-center">
|
||||||
|
<h2 className="card-title">{title}</h2>
|
||||||
|
<p>{description}</p>
|
||||||
|
<div className="card-actions justify-end">
|
||||||
|
<Link to={link || "/"} className="btn btn-primary">
|
||||||
|
Go
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
13
src/components/CardGrid.tsx
Normal file
13
src/components/CardGrid.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
const CardGrid = ({ children }: { children: ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center items-center p-8">
|
||||||
|
<div className="grid gap-8 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CardGrid;
|
39
src/components/ConfirmationDialog/ConfirmationDialog.tsx
Normal file
39
src/components/ConfirmationDialog/ConfirmationDialog.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import Modal from "../Modal";
|
||||||
|
import useConfirmationDialog from "./useConfirmationDialog";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description ConfirmationDialog component displays a confirmation dialog/modal with a message and two buttons: Yes and No.
|
||||||
|
* Actions of buttons are definded via hook.
|
||||||
|
* State of dialog is based on react context.
|
||||||
|
* @returns ConfirmationDialog component
|
||||||
|
*/
|
||||||
|
const ConfirmationDialog = () => {
|
||||||
|
const {
|
||||||
|
isConfirmationDialogVisible,
|
||||||
|
handleConfirm,
|
||||||
|
handleCancel,
|
||||||
|
customModalBoxStyles,
|
||||||
|
customModalStyles,
|
||||||
|
} = useConfirmationDialog();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isConfirmationDialogVisible && (
|
||||||
|
<Modal
|
||||||
|
modalId={"confirmation_dialog"}
|
||||||
|
customModalBoxStyles={customModalBoxStyles}
|
||||||
|
customModalStyles={customModalStyles}
|
||||||
|
>
|
||||||
|
<button className="btn btn-primary" onClick={handleConfirm}>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-secondary" onClick={handleCancel}>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmationDialog;
|
@ -0,0 +1,52 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { ConfirmationDialogContext } from "../../contexts/ConfirmationDialogContext";
|
||||||
|
|
||||||
|
export const ConfirmationDialogProvider = ({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
const [isConfirmationDialogVisible, setIsConfirmationDialogVisible] =
|
||||||
|
useState(false);
|
||||||
|
const [customModalBoxStyles, setCustomModalBoxStyles] = useState("");
|
||||||
|
const [customModalStyles, setCustomModalStyles] = useState("");
|
||||||
|
|
||||||
|
const showDialog = () => {
|
||||||
|
setIsConfirmationDialogVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideDialog = () => {
|
||||||
|
setIsConfirmationDialogVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
// Logic for when the user confirms
|
||||||
|
// You can call external functions or dispatch actions here
|
||||||
|
hideDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
// Logic for when the user cancels
|
||||||
|
hideDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationDialogContext.Provider
|
||||||
|
value={{
|
||||||
|
isConfirmationDialogVisible,
|
||||||
|
showDialog,
|
||||||
|
hideDialog,
|
||||||
|
handleConfirm,
|
||||||
|
handleCancel,
|
||||||
|
customModalBoxStyles,
|
||||||
|
customModalStyles,
|
||||||
|
setCustomModalBoxStyles,
|
||||||
|
setCustomModalStyles,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ConfirmationDialogContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmationDialogProvider;
|
23
src/components/ConfirmationDialog/useConfirmationDialog.ts
Normal file
23
src/components/ConfirmationDialog/useConfirmationDialog.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { useContext } from "react";
|
||||||
|
import {
|
||||||
|
ConfirmationDialogContext,
|
||||||
|
ConfirmationDialogContextType,
|
||||||
|
} from "../../contexts/ConfirmationDialogContext";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Hook to manage the confirmation dialog, only passes the controls to specyfic dialog component
|
||||||
|
* @param onConfirm Function to be called when the user confirms the action
|
||||||
|
* @param onCancel Function to be called when the user cancels the action
|
||||||
|
* @returns Hook controls
|
||||||
|
*/
|
||||||
|
export const useConfirmationDialog = (): ConfirmationDialogContextType => {
|
||||||
|
const context = useContext(ConfirmationDialogContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useConfirmationDialog must be used within a ConfirmationDialogProvider",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useConfirmationDialog;
|
17
src/components/Loader.tsx
Normal file
17
src/components/Loader.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
function Loader() {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="loading loading-ring loading-lg"
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "300%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
height: "128px",
|
||||||
|
width: "128px",
|
||||||
|
}}
|
||||||
|
></span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Loader;
|
109
src/components/Modal.tsx
Normal file
109
src/components/Modal.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import { useCallback, useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import useConfirmationDialog from "./ConfirmationDialog/useConfirmationDialog";
|
||||||
|
import { Cross1Icon } from "@radix-ui/react-icons";
|
||||||
|
|
||||||
|
interface ModalProps {
|
||||||
|
modalId: string;
|
||||||
|
title?: string;
|
||||||
|
subTitle?: string;
|
||||||
|
content?: string;
|
||||||
|
navigatePathOnClose?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
customModalStyles?: string;
|
||||||
|
customModalBoxStyles?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description A modal component that can be used to display a fairly custom modal/dialog
|
||||||
|
* @param modalId The id of the modal
|
||||||
|
* @param title The title of the modal
|
||||||
|
* @param subTitle The subtitle of the modal
|
||||||
|
* @param content The content of the modal
|
||||||
|
* @param navigatePathOnClose The path to navigate to when the modal is closed
|
||||||
|
* @param children The children of the modal
|
||||||
|
* @param customModalStyles Custom styles for the modal
|
||||||
|
* @param customModalBoxStyles Custom styles for the modal box
|
||||||
|
* @returns A modal component
|
||||||
|
*/
|
||||||
|
export const Modal = ({
|
||||||
|
modalId,
|
||||||
|
navigatePathOnClose,
|
||||||
|
title,
|
||||||
|
subTitle,
|
||||||
|
content,
|
||||||
|
children,
|
||||||
|
customModalStyles,
|
||||||
|
customModalBoxStyles,
|
||||||
|
}: ModalProps) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { isConfirmationDialogVisible, hideDialog } = useConfirmationDialog();
|
||||||
|
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
if (navigatePathOnClose) navigate(navigatePathOnClose);
|
||||||
|
if (isConfirmationDialogVisible)
|
||||||
|
(document.getElementById(modalId) as HTMLDialogElement).close();
|
||||||
|
hideDialog();
|
||||||
|
}, [
|
||||||
|
hideDialog,
|
||||||
|
isConfirmationDialogVisible,
|
||||||
|
modalId,
|
||||||
|
navigate,
|
||||||
|
navigatePathOnClose,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleKeyPress = useCallback(
|
||||||
|
(event: KeyboardEvent) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleClose],
|
||||||
|
);
|
||||||
|
// Handle modal open
|
||||||
|
useEffect(() => {
|
||||||
|
if (!modalId) return;
|
||||||
|
(document.getElementById(modalId) as HTMLDialogElement).showModal();
|
||||||
|
document.addEventListener("keydown", handleKeyPress);
|
||||||
|
// Handle modal close
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", handleKeyPress);
|
||||||
|
handleClose;
|
||||||
|
};
|
||||||
|
}, [handleClose, handleKeyPress, modalId]);
|
||||||
|
|
||||||
|
const composeModalStyles = useCallback(() => {
|
||||||
|
const _baseStyles = "modal modal-bottom sm:modal-middle w-screen z-50";
|
||||||
|
if (customModalStyles) return _baseStyles + " " + customModalStyles;
|
||||||
|
return _baseStyles;
|
||||||
|
}, [customModalStyles]);
|
||||||
|
const comopseModalBoxStyles = useCallback(() => {
|
||||||
|
const _baseStyles = "modal-box";
|
||||||
|
if (customModalBoxStyles) return _baseStyles + " " + customModalBoxStyles;
|
||||||
|
return _baseStyles;
|
||||||
|
}, [customModalBoxStyles]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<dialog id={modalId} className={composeModalStyles()}>
|
||||||
|
<div className={comopseModalBoxStyles()}>
|
||||||
|
<h3 className="font-bold text-lg">{title ?? "Hello!"}</h3>
|
||||||
|
<p className="py-4">
|
||||||
|
{subTitle ?? "Press ESC key or click the button below to close"}
|
||||||
|
</p>
|
||||||
|
<p>{content}</p>
|
||||||
|
<div className="modal-action">
|
||||||
|
<form method="dialog">
|
||||||
|
<button
|
||||||
|
className="btn btn-circle btn-sm absolute top-2 right-2"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<Cross1Icon />
|
||||||
|
</button>
|
||||||
|
{children}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
79
src/components/NavigationTree.tsx
Normal file
79
src/components/NavigationTree.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { CustomRouteObject } from "../configure";
|
||||||
|
import { Link, useLocation } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
clearMultiplePathSlashes,
|
||||||
|
trimPathOfParameters,
|
||||||
|
} from "../utils/StringTransformationUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Navigation tree elements, require to be used like in example below
|
||||||
|
* @example
|
||||||
|
* ```tsx
|
||||||
|
* <ul className="menu bg-base-200 w-56 h-full">
|
||||||
|
* <NavigationTree routes={navigation} />
|
||||||
|
* </ul>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
function NavigationTree(props: {
|
||||||
|
routes: CustomRouteObject[];
|
||||||
|
}): React.JSX.Element {
|
||||||
|
const locationHook = useLocation(); // Used to highlight active link in navigation tree
|
||||||
|
|
||||||
|
const GenerateNavigationEntries = (
|
||||||
|
routes: CustomRouteObject[],
|
||||||
|
parentPath?: string,
|
||||||
|
): React.ReactNode => {
|
||||||
|
return (
|
||||||
|
routes.map((route) => {
|
||||||
|
// Prepare path for links
|
||||||
|
let combinedPath = undefined;
|
||||||
|
if (parentPath !== undefined && route.path !== undefined)
|
||||||
|
combinedPath = trimPathOfParameters(
|
||||||
|
clearMultiplePathSlashes(`/${parentPath}/${route.path}`),
|
||||||
|
);
|
||||||
|
else combinedPath = route.path;
|
||||||
|
// Does it have children and enabled? Make entry with `/{parent.path}/{route.path}`
|
||||||
|
if (route.children && !route.additionalProps.disableInNavbar) {
|
||||||
|
return (
|
||||||
|
<ul key={route.path}>
|
||||||
|
<li key={route.path}>
|
||||||
|
<Link
|
||||||
|
to={combinedPath || "/"}
|
||||||
|
className={locationHook.pathname === combinedPath ? "active" : ""}
|
||||||
|
>
|
||||||
|
{route.additionalProps.name}
|
||||||
|
</Link>
|
||||||
|
{route.children ? (
|
||||||
|
<ul>{GenerateNavigationEntries(route.children, combinedPath)}</ul>
|
||||||
|
) : null}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Does it have children and not visible? Skip this entry and call this function for children passing path.
|
||||||
|
else if (route.children && route.additionalProps.disableInNavbar) {
|
||||||
|
return GenerateNavigationEntries(route.children, combinedPath);
|
||||||
|
} else if (route.additionalProps.disableInNavbar) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Make entry with `/{route.path}`
|
||||||
|
else {
|
||||||
|
return (
|
||||||
|
<li key={route.path}>
|
||||||
|
<Link
|
||||||
|
to={combinedPath || "/"}
|
||||||
|
className={locationHook.pathname === combinedPath ? "active" : ""}
|
||||||
|
>
|
||||||
|
{route.additionalProps.name}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}) || <>empty navigation tree</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div className="h-fit">{GenerateNavigationEntries(props.routes)}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavigationTree;
|
96
src/configure.tsx
Normal file
96
src/configure.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/* eslint-disable react-refresh/only-export-components */
|
||||||
|
import { Suspense, lazy } from "react";
|
||||||
|
import { RouteObject } from "react-router-dom";
|
||||||
|
import Loader from "./components/Loader";
|
||||||
|
import AboutPage from "./features/About/AboutPage";
|
||||||
|
import App from "./features/App/App";
|
||||||
|
import Debugger from "./features/Debugger";
|
||||||
|
import { flatternRoutingTable } from "./utils/RoutingTableUtils";
|
||||||
|
//----
|
||||||
|
|
||||||
|
const HomePageLazy = lazy(() => import("./features/Home/HomePage"));
|
||||||
|
|
||||||
|
export const viteEnv = import.meta.env;
|
||||||
|
|
||||||
|
// Based on https://reactrouter.com/en/main/start/overview#nested-routes with additional props
|
||||||
|
export const navigation: CustomRouteObject[] = [
|
||||||
|
// root page, mainframe for all pages, skipped in navigation tree generation (inly childs are used)
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <App />,
|
||||||
|
additionalProps: {
|
||||||
|
name: "Root", // used for breadcrumbs and navigation tree
|
||||||
|
disableRedirect: false, //entry will not be included in the react-router redirect list, will cut out childs
|
||||||
|
disableInNavbar: true, //entry will not be rendered in the navbar
|
||||||
|
disableBreadcrumbBar: false, //entry will be directly under root in routing table
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
// home page
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
index: true, //if true will be used as a default route for parent, dont declare path if use this
|
||||||
|
element: (
|
||||||
|
<Suspense fallback={<Loader />}>
|
||||||
|
<HomePageLazy />
|
||||||
|
</Suspense>
|
||||||
|
),
|
||||||
|
additionalProps: {
|
||||||
|
name: "Home",
|
||||||
|
disableRedirect: false,
|
||||||
|
disableInNavbar: false,
|
||||||
|
disableBreadcrumbBar: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// about page
|
||||||
|
{
|
||||||
|
path: "about",
|
||||||
|
element: <AboutPage />,
|
||||||
|
additionalProps: {
|
||||||
|
name: "About",
|
||||||
|
disableRedirect: false,
|
||||||
|
disableInNavbar: false,
|
||||||
|
disableBreadcrumbBar: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (viteEnv.DEV) {
|
||||||
|
navigation[0].children!.push({
|
||||||
|
path: "debug_variables",
|
||||||
|
element: <Debugger />,
|
||||||
|
additionalProps: {
|
||||||
|
name: "Debug Variables",
|
||||||
|
disableRedirect: false,
|
||||||
|
disableInNavbar: false,
|
||||||
|
disableBreadcrumbBar: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
//Custom Route Object for handling custom behaviours on app navigation
|
||||||
|
export interface RouteObjectAdditionalProps {
|
||||||
|
name: string;
|
||||||
|
disableRedirect: boolean;
|
||||||
|
disableInNavbar: boolean;
|
||||||
|
disableBreadcrumbBar: boolean;
|
||||||
|
}
|
||||||
|
export interface CustomRouteObject extends Omit<RouteObject, "children"> {
|
||||||
|
additionalProps: RouteObjectAdditionalProps;
|
||||||
|
children?: CustomRouteObject[];
|
||||||
|
}
|
||||||
|
//----
|
||||||
|
//Main configuration, static data, mostly used here
|
||||||
|
export const main = {
|
||||||
|
program_name: viteEnv.VITE_APP_NAME,
|
||||||
|
program_version: "1.1.0",
|
||||||
|
};
|
||||||
|
//About page configuration
|
||||||
|
export const about = {
|
||||||
|
program_description: `${main.program_name} to nowoczesna platforma dedykowana specjalistom z branży obróbki skrawaniem. Nasza strona umożliwia profesjonalny dobór narzędzi do frezowania, gwarantując precyzję i efektywność w każdym projekcie. Dzięki intuicyjnemu interfejsowi i zaawansowanym algorytmom, ${main.program_name} staje się niezastąpionym narzędziem dla każdego, kto chce osiągnąć perfekcyjne rezultaty w obróbce materiałów.`,
|
||||||
|
program_authors: [{ name: "Bartosz Bielski", email: "bartbie194@student.polsl.pl" }],
|
||||||
|
};
|
||||||
|
//----
|
||||||
|
export const flatRoutes = flatternRoutingTable(navigation);
|
19
src/contexts/ConfirmationDialogContext.tsx
Normal file
19
src/contexts/ConfirmationDialogContext.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
// Define the shape of the context
|
||||||
|
export type ConfirmationDialogContextType = {
|
||||||
|
isConfirmationDialogVisible: boolean;
|
||||||
|
showDialog: () => void;
|
||||||
|
hideDialog: () => void;
|
||||||
|
handleConfirm: () => void;
|
||||||
|
handleCancel: () => void;
|
||||||
|
customModalStyles: string;
|
||||||
|
customModalBoxStyles: string;
|
||||||
|
setCustomModalBoxStyles: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
setCustomModalStyles: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
};
|
||||||
|
// Create the context with default values
|
||||||
|
export const ConfirmationDialogContext = createContext<
|
||||||
|
ConfirmationDialogContextType | undefined
|
||||||
|
>(undefined);
|
||||||
|
ConfirmationDialogContext.displayName = "ConfirmationDialog";
|
32
src/features/About/AboutPage.tsx
Normal file
32
src/features/About/AboutPage.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { about } from "../../configure";
|
||||||
|
|
||||||
|
/** Here is located about page where you can find information about this application:
|
||||||
|
* - short description of this application
|
||||||
|
* - how to use it
|
||||||
|
* - information about persons responsible for maintaining this application
|
||||||
|
*/
|
||||||
|
function AboutPage() {
|
||||||
|
return (
|
||||||
|
<div className="hero ">
|
||||||
|
<div className="hero-content text-center">
|
||||||
|
<div className="max-w-md">
|
||||||
|
<h1 className="text-5xl font-bold">About</h1>
|
||||||
|
<p className="py-6">{about.program_description}</p>
|
||||||
|
<h2 className="text-2xl font-bold pt-10">Authors</h2>
|
||||||
|
<div className="py-6">
|
||||||
|
{about.program_authors.map((author) => (
|
||||||
|
<div key={author.name}>
|
||||||
|
{author.name} -{" "}
|
||||||
|
<a href={"mailto:" + author.email} className="link">
|
||||||
|
{author.email}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AboutPage;
|
82
src/features/App/App.tsx
Normal file
82
src/features/App/App.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { HamburgerMenuIcon } from "@radix-ui/react-icons";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import { main, navigation } from "../../configure.tsx";
|
||||||
|
import NavigationTree from "../../components/NavigationTree.tsx";
|
||||||
|
import Breadcrumbs from "../../components/Breadcrumbs.tsx";
|
||||||
|
import ConfirmationDialog from "../../components/ConfirmationDialog/ConfirmationDialog.tsx";
|
||||||
|
|
||||||
|
/** Here is located global wrapper for entire application, here you canfind:
|
||||||
|
* - Drawer - contains navigation buttons
|
||||||
|
* - Navbar - contains hamburger menu and theme selector
|
||||||
|
* - Outlet - contains active page content
|
||||||
|
* - App theme controll
|
||||||
|
*/
|
||||||
|
function App() {
|
||||||
|
const [openDrawer, setOpenDrawer] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.querySelector("html")?.setAttribute("data-theme", "acid");
|
||||||
|
// To resolve this issue, you can use the import.meta.env object instead of process.env. The import.meta.env object is provided by Vite.js and allows you to access environment variables in your code.
|
||||||
|
document.title = import.meta.env.VITE_APP_NAME;
|
||||||
|
}, [openDrawer]);
|
||||||
|
|
||||||
|
// Function on click drawer hamburger button
|
||||||
|
const handleDrawerStatus = () => {
|
||||||
|
setOpenDrawer(!openDrawer);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ConfirmationDialog />
|
||||||
|
{/* Root drawer container */}
|
||||||
|
{/* Drawer opening is controlled directly via css prop and next via local useState variable */}
|
||||||
|
<div className={"drawer " + (openDrawer ? "lg:drawer-open" : "")}>
|
||||||
|
{/* A hidden checkbox to toggle the visibility of the drawer */}
|
||||||
|
<input
|
||||||
|
id="my-drawer"
|
||||||
|
type="checkbox"
|
||||||
|
placeholder="drawer-checkbox"
|
||||||
|
className="drawer-toggle"
|
||||||
|
checked={openDrawer}
|
||||||
|
onChange={handleDrawerStatus}
|
||||||
|
/>
|
||||||
|
{/* The actual drawer content */}
|
||||||
|
<div className="drawer-content">
|
||||||
|
{/* Navbar */}
|
||||||
|
<div className="navbar bg-neutral text-neutral-content w-auto">
|
||||||
|
{/* Left side navbar */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<label
|
||||||
|
htmlFor="my-drawer"
|
||||||
|
className="btn btn-circle btn-neutral drawer-button"
|
||||||
|
>
|
||||||
|
<HamburgerMenuIcon className="w-6 h-6" />
|
||||||
|
</label>
|
||||||
|
<p className="text-lg font-bold ml-8">{main.program_name}</p>
|
||||||
|
</div>
|
||||||
|
{/* Right side navbar */}
|
||||||
|
<div className="flex-none gap-2">{main.program_version}</div>
|
||||||
|
</div>
|
||||||
|
{/* App/active_drawer content */}
|
||||||
|
<div className="drawer-content">
|
||||||
|
{/*Automatically generated breadcrumbs based on routing table from configuration file and active path*/}
|
||||||
|
{/*Get active route and find it in routing file*/}
|
||||||
|
<Breadcrumbs />
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Drawer sidebar wrapper */}
|
||||||
|
<div className="drawer-side z-20">
|
||||||
|
{/* Dark overlay on mobile devices, clickable to close drawer */}
|
||||||
|
<label htmlFor="my-drawer" className="drawer-overlay"></label>
|
||||||
|
<ul className="menu bg-base-200 w-56 h-full overflow-auto">
|
||||||
|
<NavigationTree routes={navigation} />
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
31
src/features/Debugger.tsx
Normal file
31
src/features/Debugger.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { flatRoutes } from "../configure";
|
||||||
|
|
||||||
|
function Debugger() {
|
||||||
|
const windowLocation = window.location.pathname;
|
||||||
|
const locationHook = useLocation();
|
||||||
|
return (
|
||||||
|
<div className="m-auto p-4">
|
||||||
|
<details className="collapse bg-base-200 mb-4">
|
||||||
|
<summary className="collapse-title text-xl font-medium">
|
||||||
|
Flat Routes
|
||||||
|
</summary>
|
||||||
|
<code className="collapse-content">{JSON.stringify(flatRoutes)}</code>
|
||||||
|
</details>
|
||||||
|
<details className="collapse bg-base-200 mb-4">
|
||||||
|
<summary className="collapse-title text-xl font-medium">Locations</summary>
|
||||||
|
<div className="collapse-content">
|
||||||
|
<code>Location hook: {JSON.stringify(locationHook)}</code>
|
||||||
|
<br />
|
||||||
|
<code>Location window: {JSON.stringify(windowLocation)}</code>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
<details className="collapse bg-base-200 mb-4">
|
||||||
|
<summary className="collapse-title text-xl font-medium">Routes</summary>
|
||||||
|
<code className="collapse-content">{JSON.stringify(flatRoutes)}</code>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Debugger;
|
175
src/features/Home/HomePage.tsx
Normal file
175
src/features/Home/HomePage.tsx
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const lists = {
|
||||||
|
rodzajFrezowania: ["Czolowe", "Walcowo Czolowe", "Rowkowe"],
|
||||||
|
rodzajObrobki: ["Zgrubna", "Wykończeniowa"],
|
||||||
|
material: ["Stal węglowa", "Stal nierdzewna", "Żeliwo"],
|
||||||
|
};
|
||||||
|
import { ChangeEvent } from "react";
|
||||||
|
|
||||||
|
interface SelectProps {
|
||||||
|
value: string | number;
|
||||||
|
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
|
||||||
|
options: string[];
|
||||||
|
label: string;
|
||||||
|
disabledLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Select = ({ value, onChange, options, label, disabledLabel }: SelectProps) => (
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-gray-700 text-sm font-bold mb-2">{label}</label>
|
||||||
|
<select className="select w-full input input-secondary" value={value} onChange={onChange}>
|
||||||
|
<option disabled>{disabledLabel}</option>
|
||||||
|
{options.map((option, index) => (
|
||||||
|
<option key={option} value={index}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
rodzajFrezowania: 0,
|
||||||
|
rodzajObrobki: 0,
|
||||||
|
material: 0,
|
||||||
|
glebokoscSkrawania: Math.floor(Math.random() * 50 + 1),
|
||||||
|
szerokoscSkrawania: Math.floor(Math.random() * 30 + 1),
|
||||||
|
tolerancja: parseFloat((0.1 + Math.random() * (5 - 0.1)).toFixed(2)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (key: keyof typeof state) => (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
const value = Number(e.target.value);
|
||||||
|
setState((prev) => ({ ...prev, [key]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="p-6 bg-white rounded shadow-lg lg:w-[500px] space-y-4 mb-4 lg:absolute lg:right-1/2 lg:translate-x-1/2 lg:translate-y-4">
|
||||||
|
<h1 className="text-3xl mb-6 text-center text-gray-700">Wybór parametrów</h1>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{Object.keys(lists).map((key) => (
|
||||||
|
<Select key={key} value={state[key as keyof typeof state]} onChange={handleChange(key as keyof typeof state)} options={lists[key as keyof typeof lists]} label={key} disabledLabel={`Wybierz ${key}`} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label className="flex text-gray-700 text-sm font-bold mb-0">Głębokość Skrawania</label>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={1}
|
||||||
|
max={50}
|
||||||
|
value={state.glebokoscSkrawania}
|
||||||
|
className="range flex-grow mr-2 "
|
||||||
|
onChange={(e) => {
|
||||||
|
setState((prev) => ({ ...prev, glebokoscSkrawania: Number(e.target.value) }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
value={state.glebokoscSkrawania}
|
||||||
|
onChange={(e) => {
|
||||||
|
setState((prev) => ({ ...prev, glebokoscSkrawania: Number(e.target.value) }));
|
||||||
|
}}
|
||||||
|
className="input input-secondary w-20"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={50}
|
||||||
|
/>
|
||||||
|
<span>mm</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label className="flex text-gray-700 text-sm font-bold mb-0">Szerokość Skrawania</label>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={1}
|
||||||
|
max={30}
|
||||||
|
value={state.szerokoscSkrawania}
|
||||||
|
className="range flex-grow mr-2"
|
||||||
|
onChange={(e) => {
|
||||||
|
setState((prev) => ({ ...prev, szerokoscSkrawania: Number(e.target.value) }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
value={state.szerokoscSkrawania}
|
||||||
|
onChange={(e) => {
|
||||||
|
setState((prev) => ({ ...prev, szerokoscSkrawania: Number(e.target.value) }));
|
||||||
|
}}
|
||||||
|
className="input input-secondary w-20"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={30}
|
||||||
|
/>
|
||||||
|
<span>mm</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label className="flex text-gray-700 text-sm font-bold mb-0">Tolerancja</label>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={0.1}
|
||||||
|
max={5}
|
||||||
|
value={state.tolerancja}
|
||||||
|
className="range flex-grow mr-2"
|
||||||
|
onChange={(e) => {
|
||||||
|
setState((prev) => ({ ...prev, tolerancja: Number(e.target.value) }));
|
||||||
|
}}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
value={state.tolerancja}
|
||||||
|
onChange={(e) => {
|
||||||
|
setState((prev) => ({ ...prev, tolerancja: Number(e.target.value) }));
|
||||||
|
}}
|
||||||
|
className="input input-secondary w-20"
|
||||||
|
type="number"
|
||||||
|
min={0.1}
|
||||||
|
max={5}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
<span>mm</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary w-full mt-4"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
console.log(state.rodzajFrezowania, state.rodzajObrobki, state.material, state.glebokoscSkrawania, state.szerokoscSkrawania, state.tolerancja);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Policz
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="m-4 p-6 bg-white rounded-lg shadow-md w-fit max-w-xs">
|
||||||
|
<h1 className="text-4xl mb-8 text-center text-gray-800 tracking-wider">Wyniki</h1>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<span className="text-gray-800 block tracking-wide">
|
||||||
|
rodzajFrezowania: <span className="text-black font-medium">{state.rodzajFrezowania}</span>
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-800 block tracking-wide">
|
||||||
|
rodzajObrobki: <span className="text-black font-medium">{state.rodzajObrobki}</span>
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-800 block tracking-wide">
|
||||||
|
material: <span className="text-black font-medium">{state.material}</span>
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-800 block tracking-wide">
|
||||||
|
glebokoscSkrawania: <span className="text-black font-medium">{state.glebokoscSkrawania}</span>
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-800 block tracking-wide">
|
||||||
|
szerokoscSkrawania: <span className="text-black font-medium">{state.szerokoscSkrawania}</span>
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-800 block tracking-wide">
|
||||||
|
tolerancja: <span className="text-black font-medium">{state.tolerancja}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HomePage;
|
3
src/main.css
Normal file
3
src/main.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
21
src/main.tsx
Normal file
21
src/main.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { RouterProvider } from "react-router-dom";
|
||||||
|
import { setupAxiosInterceptors } from "./api/AxiosService";
|
||||||
|
import "./main.css";
|
||||||
|
import router from "./routes";
|
||||||
|
import ConfirmationDialogProvider from "./components/ConfirmationDialog/ConfirmationDialogProvider";
|
||||||
|
|
||||||
|
setupAxiosInterceptors();
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
{/* fallbackElement - use when routing table is quite big to load, it will display desired element in convention of loading screen */}
|
||||||
|
<ConfirmationDialogProvider>
|
||||||
|
<RouterProvider
|
||||||
|
router={router}
|
||||||
|
fallbackElement={<p>Loading route table...</p>}
|
||||||
|
/>
|
||||||
|
</ConfirmationDialogProvider>
|
||||||
|
</React.StrictMode>,
|
||||||
|
);
|
69
src/routes.tsx
Normal file
69
src/routes.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* This file is used to configure react-router-dom.
|
||||||
|
* Do not change anything here, unless you know what you are doing.
|
||||||
|
* Every path and route is configured in `configure.tsx` file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createBrowserRouter, RouteObject } from "react-router-dom";
|
||||||
|
import { CustomRouteObject, navigation } from "./configure.tsx";
|
||||||
|
import Redirect from "./utils/Redirect.tsx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to convert `CustomRouteObject` to `RouteObject` and process all additionalProps
|
||||||
|
*/
|
||||||
|
const createRoutingFromCustomRouteObject = (
|
||||||
|
routes: CustomRouteObject[],
|
||||||
|
): RouteObject[] => {
|
||||||
|
const navigationRoutes: RouteObject[] = []; //Root routes that will be used for navigation
|
||||||
|
routes.forEach((route) => {
|
||||||
|
// Extract everything except additionalProps and children
|
||||||
|
const { additionalProps, children, ...routeNative } = route;
|
||||||
|
//Filter routes that are disabled for navigation
|
||||||
|
const isRouteDisabled = additionalProps.disableRedirect;
|
||||||
|
if (isRouteDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Add route to navigationRoutes, array that will be returned
|
||||||
|
const isRouteWithChildren = children !== undefined && children.length > 0;
|
||||||
|
if (!isRouteWithChildren) {
|
||||||
|
navigationRoutes.push({
|
||||||
|
...routeNative,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
navigationRoutes.push({
|
||||||
|
path: route.path,
|
||||||
|
element: route.element,
|
||||||
|
children: createRoutingFromCustomRouteObject(children),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//In case of empty routes, add a default route with message
|
||||||
|
if (navigationRoutes.length === 0) {
|
||||||
|
navigationRoutes.push({
|
||||||
|
path: "/",
|
||||||
|
element: <div>Empty routes</div>,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//Always add redirect unmached route to root at the end
|
||||||
|
return navigationRoutes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** It is main variable that handle all navigation rules and paths */
|
||||||
|
export const routes: RouteObject[] = [
|
||||||
|
...createRoutingFromCustomRouteObject(navigation),
|
||||||
|
{
|
||||||
|
path: "*",
|
||||||
|
element: <Redirect to="/" />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/** It is variable that have routes for react-router-dom, final form */
|
||||||
|
export const router = createBrowserRouter(routes, {
|
||||||
|
basename: "/",
|
||||||
|
future: {
|
||||||
|
// Normalize `useNavigation()`/`useFetcher()` `formMethod` to uppercase
|
||||||
|
v7_normalizeFormMethod: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
9
src/utils/ObjectUtils.ts
Normal file
9
src/utils/ObjectUtils.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export const rollThroughObj = (
|
||||||
|
obj: Readonly<Partial<{ state: string; id: string }>>,
|
||||||
|
) => {
|
||||||
|
let result = "";
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
result += ` -${key}: ${value}`;
|
||||||
|
}
|
||||||
|
return result.trim();
|
||||||
|
};
|
18
src/utils/Redirect.tsx
Normal file
18
src/utils/Redirect.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Redirects to the given page
|
||||||
|
* @param {string} to - The page to redirect to
|
||||||
|
*/
|
||||||
|
function Redirect({ to }: { to: string }) {
|
||||||
|
// The navigate function from useNavigate is used to navigate to the given page
|
||||||
|
const navigate = useNavigate();
|
||||||
|
// useEffect is used to navigate to the given page when the component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
navigate(to);
|
||||||
|
});
|
||||||
|
// A null element is returned because the component does not need to render anything
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
export default Redirect;
|
79
src/utils/RoutingTableUtils.ts
Normal file
79
src/utils/RoutingTableUtils.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import {
|
||||||
|
CustomRouteObject,
|
||||||
|
RouteObjectAdditionalProps,
|
||||||
|
flatRoutes,
|
||||||
|
} from "../configure";
|
||||||
|
import {
|
||||||
|
clearMultiplePathSlashes,
|
||||||
|
trimPathOfParameters,
|
||||||
|
} from "./StringTransformationUtils";
|
||||||
|
|
||||||
|
interface FlatternRoutingTableElement extends RouteObjectAdditionalProps {
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
//! WARNING: This function will generate error if paths aren't unique, disableInNavbar or disableRedirect to prevent this. It's a useful feature to prevent duplicate path in navbar
|
||||||
|
/**
|
||||||
|
* @description Convert a existing routing table as a flat array
|
||||||
|
*/
|
||||||
|
export const flatternRoutingTable = (
|
||||||
|
routes: CustomRouteObject[],
|
||||||
|
previousPath = "undefined",
|
||||||
|
): FlatternRoutingTableElement[] => {
|
||||||
|
const result: FlatternRoutingTableElement[] = [];
|
||||||
|
routes.forEach((route: CustomRouteObject) => {
|
||||||
|
if (route.additionalProps.disableRedirect) return; // Skip if disable redirect, children are skipped too
|
||||||
|
if (
|
||||||
|
typeof route.path !== "undefined" &&
|
||||||
|
typeof previousPath === "undefined" &&
|
||||||
|
!route.additionalProps.disableInNavbar
|
||||||
|
) {
|
||||||
|
result.push({
|
||||||
|
path: trimPathOfParameters(route.path),
|
||||||
|
name: route.additionalProps.name,
|
||||||
|
disableBreadcrumbBar: route.additionalProps.disableBreadcrumbBar,
|
||||||
|
disableInNavbar: route.additionalProps.disableInNavbar,
|
||||||
|
disableRedirect: route.additionalProps.disableRedirect,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof route.path !== "undefined" &&
|
||||||
|
typeof previousPath !== "undefined" &&
|
||||||
|
!route.additionalProps.disableInNavbar
|
||||||
|
) {
|
||||||
|
result.push({
|
||||||
|
path: trimPathOfParameters(
|
||||||
|
clearMultiplePathSlashes(`/${previousPath}/${route.path}`),
|
||||||
|
),
|
||||||
|
name: route.additionalProps.name,
|
||||||
|
disableBreadcrumbBar: route.additionalProps.disableBreadcrumbBar,
|
||||||
|
disableInNavbar: route.additionalProps.disableInNavbar,
|
||||||
|
disableRedirect: route.additionalProps.disableRedirect,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (route.children && typeof previousPath === "undefined") {
|
||||||
|
result.push(...flatternRoutingTable(route.children));
|
||||||
|
}
|
||||||
|
if (route.children && typeof previousPath !== "undefined") {
|
||||||
|
result.push(...flatternRoutingTable(route.children, route.path));
|
||||||
|
}
|
||||||
|
// Errors handling
|
||||||
|
if (typeof route.path === "undefined")
|
||||||
|
console.error(`Route ${route.additionalProps.name} is missing path`);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @description Function to find element in flattern routes array by LAST path ex: /admin/MAINTENANCE
|
||||||
|
*/
|
||||||
|
export const findElementInFlatRoutes = (
|
||||||
|
path: string,
|
||||||
|
): FlatternRoutingTableElement | undefined => {
|
||||||
|
// Split path string into array, split by '/', then get the last element
|
||||||
|
const _route = flatRoutes.find((route) => {
|
||||||
|
const pathArray = route.path.split("/");
|
||||||
|
if (pathArray[pathArray.length - 1] === path) return true;
|
||||||
|
else return false;
|
||||||
|
});
|
||||||
|
return _route;
|
||||||
|
};
|
22
src/utils/StringTransformationUtils.ts
Normal file
22
src/utils/StringTransformationUtils.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export function capitalizeFirstLetter(string: string): string {
|
||||||
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description Function to clear multiple path slashes
|
||||||
|
* @example clearMultiplePathSlashes("/admin//MAINTENANCE") => "/admin/MAINTENANCE"
|
||||||
|
*/
|
||||||
|
export const clearMultiplePathSlashes = (path: string): string => {
|
||||||
|
return path.replace(/\/{2,}/g, "/");
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Trims parameters and queries from a URL path that start with ':' or '?'.
|
||||||
|
*
|
||||||
|
* @param {string} path - The URL path to trim.
|
||||||
|
* @returns {string} The URL path without parameters and queries.
|
||||||
|
* @example
|
||||||
|
* // returns "/path"
|
||||||
|
* trimPathOfParameters("/path/:param1/:param2:param3/?param4")
|
||||||
|
*/
|
||||||
|
export const trimPathOfParameters = (path: string) => {
|
||||||
|
return path.replace(/\/:[^/]*|\?[^/]*/g, "");
|
||||||
|
};
|
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" />
|
11
tailwind.config.js
Normal file
11
tailwind.config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
import { themes } from "./daisyui.config.js";
|
||||||
|
export default {
|
||||||
|
content: ["./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
// safelist is used to allow classes to not be purged by tailwind
|
||||||
|
safelist: ["alert-info", "alert-success", "alert-warning", "alert-error"],
|
||||||
|
daisyui: {
|
||||||
|
themes: [...themes],
|
||||||
|
},
|
||||||
|
plugins: [require("daisyui"), require("@tailwindcss/typography")],
|
||||||
|
};
|
49
tsconfig.json
Normal file
49
tsconfig.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable",
|
||||||
|
"ESNext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noErrorTruncation": true,
|
||||||
|
/* "react-jsx" and "react-jsxdev": These are new options available from TypeScript 4.1 onwards. "react-jsx" transforms JSX into calls to a function that will be imported from react/jsx-runtime. "react-jsxdev" does the same, but for development builds. These options are useful if you're using React 17 or later, which introduced a new JSX Transform.
|
||||||
|
Remember that to use these options, you also need to set the module compiler option to esnext or commonjs, because the output will contain import statements.*/
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
/* Vite handle emiting */
|
||||||
|
"noEmit": true,
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
/*
|
||||||
|
"strict": true in TypeScript's tsconfig.json is an overarching setting that turns on a number of strict type-checking options. Some of these could overlap with ESLint rules related to good practices in JavaScript and TypeScript coding. However, since ESLint and TypeScript serve different purposes (ESLint for style and syntax, TypeScript for type checking), there usually isn't a direct conflict.
|
||||||
|
"noUnusedLocals": true and "noUnusedParameters": true in TypeScript could overlap with ESLint's no-unused-vars rule, which flags declared variables or arguments that are not used anywhere in the code.
|
||||||
|
"noFallthroughCasesInSwitch": true could overlap with ESLint's no-fallthrough rule, which disallows fallthrough behavior in switch statements, a common error in JavaScript. */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"types": [
|
||||||
|
"@types/node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
// "references": [
|
||||||
|
// {
|
||||||
|
// "path": "./tsconfig.node.json"
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
}
|
15
vite.config.ts
Normal file
15
vite.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig, loadEnv } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default ({ mode }) => {
|
||||||
|
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
||||||
|
return defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
// resolve: {
|
||||||
|
// alias: {
|
||||||
|
// "tailwind.config.js": path.resolve(__dirname, "tailwind.config.js"),
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user