v1.0 ready to use
This commit is contained in:
parent
a0d90aaf79
commit
cfe5a813f7
@ -1,4 +1,4 @@
|
|||||||
# TailwindElements-React-Starter
|
# Universal-React-Starter
|
||||||
|
|
||||||
- [DaisyUI](https://daisyui.com/) - main styling
|
- [DaisyUI](https://daisyui.com/) - main styling
|
||||||
- [react-icons](https://react-icons.github.io/react-icons/) - big icon library
|
- [react-icons](https://react-icons.github.io/react-icons/) - big icon library
|
||||||
@ -11,8 +11,9 @@
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
1. Setup `VITE_APP_NAME` in `.env.{mode}` files
|
1. Setup `VITE_APP_NAME` in `.env.{mode}` files
|
||||||
2. run: `bun run dev`
|
2. Setup `VITE_BASE_PATH` in `.env.{mode}` files
|
||||||
3. To build for production, run: `bun run build`
|
3. run: `bun run dev`
|
||||||
|
4. To build for production, run: `bun run build`
|
||||||
|
|
||||||
_bun can be replaced by packet manager of your choice_
|
_bun can be replaced by packet manager of your choice_
|
||||||
|
|
||||||
|
28
index.html
28
index.html
@ -1,12 +1,34 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en" data-theme="light">
|
<html lang="en" data-theme="light">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="icon" href="/icon.png" />
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
|
|
||||||
|
<title>Fallback title</title>
|
||||||
|
|
||||||
|
<!-- SEO Meta Tags -->
|
||||||
|
<meta name="description" content="Brief description of the page" />
|
||||||
|
<meta name="keywords" content="keyword1, keyword2, keyword3" />
|
||||||
|
<meta name="author" content="Author Name" />
|
||||||
|
|
||||||
|
<!-- Social Media Meta Tags (Open Graph for Facebook, Twitter Card, etc.) -->
|
||||||
|
<meta property="og:title" content="Title for Social Media" />
|
||||||
|
<meta property="og:description" content="Description for Social Media" />
|
||||||
|
<meta property="og:image" content="URL to image for social media" />
|
||||||
|
<meta property="og:url" content="URL of the page" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" href="/cmms_servimain_logo.png" />
|
||||||
|
|
||||||
|
<!-- Additional Tags (like canonical, robots, etc.) -->
|
||||||
|
<link rel="canonical" href="https://www.example.com/page-url" />
|
||||||
|
<meta name="robots" content="index, follow" />
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite serve",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview --host"
|
"preview": "vite preview --host"
|
||||||
@ -18,6 +18,7 @@
|
|||||||
"axios": "^1.6.1",
|
"axios": "^1.6.1",
|
||||||
"daisyui": "latest",
|
"daisyui": "latest",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
|
"jotai": "^2.6.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.48.2",
|
"react-hook-form": "^7.48.2",
|
||||||
|
BIN
public/cmms_servimain_logo.png
Normal file
BIN
public/cmms_servimain_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 MiB |
BIN
public/cmms_servimain_logo_192.png
Normal file
BIN
public/cmms_servimain_logo_192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
public/cmms_servimain_logo_512.png
Normal file
BIN
public/cmms_servimain_logo_512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
public/icon.png
BIN
public/icon.png
Binary file not shown.
Before Width: | Height: | Size: 56 KiB |
21
public/manifest.json
Normal file
21
public/manifest.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "My App",
|
||||||
|
"short_name": "App",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "cmms_servimain_logo_192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "cmms_servimain_logo_512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 733 KiB |
@ -1,27 +1,32 @@
|
|||||||
import ThemeButton from "../components/ThemeButton";
|
import ThemeButton from "./components/ThemeButton";
|
||||||
import { main } from "../configure";
|
import { main } from "./configure";
|
||||||
import { Router } from "wouter";
|
import { Router } from "wouter";
|
||||||
import SwitchRoutesGenerator from "../routes/RouteStructureGenerator";
|
import SwitchRoutesGenerator from "./routes/SwitchRoutesGenerator";
|
||||||
import DebugNavigationButtonsGenerator from "../routes/DebugNavigationButtonsGenerator";
|
import GenerateNavigationButtonsDebug from "./routes/GenerateNavigationButtonsDebug";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
// Router component with base path
|
||||||
<Router base={main.basePath}>
|
<Router base={main.basePath}>
|
||||||
<div className="space-x-4 space-y-6">
|
<div className="space-x-4 space-y-6">
|
||||||
<div className="px-5 mt-6">
|
<div className="px-5 mt-6">
|
||||||
|
{/* Displaying program name and version */}
|
||||||
<h1 className="text-center ">
|
<h1 className="text-center ">
|
||||||
{main.program_name} v{main.program_version}
|
{main.program_name} v{main.program_version}
|
||||||
<p>Base path is: {main.basePath}</p>
|
<p>Base path is: {main.basePath}</p>
|
||||||
</h1>
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
|
{/* ThemeButton component */}
|
||||||
<ThemeButton />
|
<ThemeButton />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Separator */}
|
||||||
<hr className="my-12 h-[2px] border-t-0 bg-transparent bg-gradient-to-r from-transparent via-current to-transparent opacity-25 dark:opacity-100" />
|
<hr className="my-12 h-[2px] border-t-0 bg-transparent bg-gradient-to-r from-transparent via-current to-transparent opacity-25 dark:opacity-100" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<DebugNavigationButtonsGenerator />
|
{/* GenerateNavigationButtonsDebug component */}
|
||||||
|
<GenerateNavigationButtonsDebug />
|
||||||
{/* Paths in Route should be without base path (even without '/'). If want Route for base path than pass base path */}
|
{/* Paths in Route should be without base path (even without '/'). If want Route for base path than pass base path */}
|
||||||
|
{/* SwitchRoutesGenerator component */}
|
||||||
<SwitchRoutesGenerator />
|
<SwitchRoutesGenerator />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -1,28 +1,21 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect } from "react";
|
||||||
import useLocalStorage from "./useLocalStorage";
|
import useLocalStorage from "./useLocalStorage";
|
||||||
|
|
||||||
/**
|
|
||||||
* A hook to toggle between light and dark theme and store the value in local storage.
|
|
||||||
* @returns {isDark, toggleTheme} - Returns a boolean indicating if dark mode is active and a function to toggle the theme.
|
|
||||||
*/
|
|
||||||
const useTheme = (): [boolean, () => void] => {
|
const useTheme = (): [boolean, () => void] => {
|
||||||
// Initialize theme from local storage or default value
|
|
||||||
const [theme, setTheme] = useLocalStorage<string>("theme", "light");
|
const [theme, setTheme] = useLocalStorage<string>("theme", "light");
|
||||||
|
const isDark = theme === "dark";
|
||||||
// Determine if the current theme is dark
|
|
||||||
const [isDark, setIsDark] = useState<boolean>(theme === "dark");
|
|
||||||
|
|
||||||
const toggleTheme = () => {
|
const toggleTheme = () => {
|
||||||
|
console.log("toggleTheme called");
|
||||||
const newTheme = theme === "light" ? "dark" : "light";
|
const newTheme = theme === "light" ? "dark" : "light";
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
setIsDark(newTheme === "dark");
|
|
||||||
document.documentElement.dataset.theme = newTheme;
|
document.documentElement.dataset.theme = newTheme;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply the theme on component mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log("useEffect called");
|
||||||
document.documentElement.dataset.theme = theme;
|
document.documentElement.dataset.theme = theme;
|
||||||
}, [theme]); // Re-run effect when theme changes
|
}, [theme]);
|
||||||
|
|
||||||
return [isDark, toggleTheme];
|
return [isDark, toggleTheme];
|
||||||
};
|
};
|
||||||
|
@ -2,10 +2,11 @@ import React from "react";
|
|||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { setupAxiosInterceptors } from "./api/AxiosService";
|
import { setupAxiosInterceptors } from "./api/AxiosService";
|
||||||
import { viteEnv } from "./configure";
|
import { viteEnv } from "./configure";
|
||||||
import App from "./features/App";
|
import App from "./App";
|
||||||
import inDev from "./utils/inDebug";
|
import inDev from "./utils/inDebug";
|
||||||
import { Global, css } from "@emotion/react";
|
import { Global, css } from "@emotion/react";
|
||||||
import "/style.css";
|
import "/style.css";
|
||||||
|
import { Provider } from "jotai";
|
||||||
|
|
||||||
setupAxiosInterceptors();
|
setupAxiosInterceptors();
|
||||||
inDev(() => console.log(viteEnv));
|
inDev(() => console.log(viteEnv));
|
||||||
@ -18,6 +19,8 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
|||||||
// You can add more global styles here
|
// You can add more global styles here
|
||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
<App />
|
<Provider>
|
||||||
|
<App />
|
||||||
|
</Provider>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { useState } from "react";
|
import { atom, useAtom } from "jotai";
|
||||||
|
const counterAtom = atom(0);
|
||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
const [counter, setCounter] = useState<number>(0);
|
const [counter, setCounter] = useAtom(counterAtom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>HomePage</p>
|
<p>HomePage</p>
|
@ -1,7 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import * as R from "../routes/routes";
|
import { v4 } from "uuid";
|
||||||
import { Link } from "wouter";
|
import { Link } from "wouter";
|
||||||
import { Routes } from "../routes/routes";
|
import * as R from "./routes";
|
||||||
|
import { Routes } from "./routes";
|
||||||
import inDev from "../utils/inDebug";
|
import inDev from "../utils/inDebug";
|
||||||
|
|
||||||
const routesCrawler = (
|
const routesCrawler = (
|
||||||
@ -9,19 +10,18 @@ const routesCrawler = (
|
|||||||
routesPack: Routes[] = R.routesRoot,
|
routesPack: Routes[] = R.routesRoot,
|
||||||
): JSX.Element[] => {
|
): JSX.Element[] => {
|
||||||
// Roll through routes and generate Route for each
|
// Roll through routes and generate Route for each
|
||||||
return routesPack.map((route, index): JSX.Element => {
|
return routesPack.map((route): JSX.Element => {
|
||||||
// Debug log shows generated routes
|
// Debug log shows generated routes
|
||||||
inDev(() =>
|
inDev(() =>
|
||||||
console.log(
|
console.log(
|
||||||
"routesCrawler",
|
"routesCrawler buttons",
|
||||||
parentPath ? parentPath + route.path : route.path,
|
parentPath ? parentPath + route.path : route.path,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="join">
|
<div key={v4()} className="join">
|
||||||
{/* Check does route have children and perform routing generation for children */}
|
{/* Check does route have children and perform routing generation for children */}
|
||||||
<Link
|
<Link
|
||||||
key={index}
|
|
||||||
className={`btn join-item hover:btn-primary duration-75 `}
|
className={`btn join-item hover:btn-primary duration-75 `}
|
||||||
href={
|
href={
|
||||||
parentPath ? parentPath + route.path : route.path != "/" ? route.path : ""
|
parentPath ? parentPath + route.path : route.path != "/" ? route.path : ""
|
||||||
@ -43,8 +43,8 @@ const routesCrawler = (
|
|||||||
/**
|
/**
|
||||||
* This component is used to generate buttons for debug navigation
|
* This component is used to generate buttons for debug navigation
|
||||||
*/
|
*/
|
||||||
const DebugNavigationButtonsGenerator = () => {
|
const GenerateNavigationButtonsDebug = () => {
|
||||||
return <div className="space-y-2 space-x-2 mb-6">{routesCrawler()}</div>;
|
return <div className="space-y-2 space-x-2 mb-6">{routesCrawler()}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DebugNavigationButtonsGenerator;
|
export default GenerateNavigationButtonsDebug;
|
@ -1,34 +1,38 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { v4 } from "uuid";
|
||||||
import { Route, Switch } from "wouter";
|
import { Route, Switch } from "wouter";
|
||||||
import { Routes } from "./routes";
|
|
||||||
import * as R from "./routes";
|
|
||||||
import inDev from "../utils/inDebug";
|
import inDev from "../utils/inDebug";
|
||||||
|
import * as R from "./routes";
|
||||||
|
import { Routes } from "./routes";
|
||||||
|
|
||||||
const routesCrawler = (
|
const routesCrawler = (
|
||||||
parentPath?: string,
|
parentPath?: string,
|
||||||
routesPack: Routes[] = R.routesRoot,
|
routesPack: Routes[] = R.routesRoot,
|
||||||
): JSX.Element[] => {
|
): JSX.Element[] => {
|
||||||
// Roll through routes and generate Route for each
|
// Roll through routes and generate Route for each
|
||||||
return routesPack.map((route, index): JSX.Element => {
|
return routesPack.map((route): JSX.Element => {
|
||||||
// Debug log shows generated routes
|
// Debug log shows generated routes
|
||||||
inDev(() =>
|
inDev(() =>
|
||||||
console.log(
|
console.log(
|
||||||
"routesCrawler",
|
"routesCrawler routes",
|
||||||
parentPath ? parentPath + route.path : route.path,
|
parentPath ? parentPath + route.path : route.path,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return (
|
return route.children ? (
|
||||||
<>
|
<React.Fragment key={v4()}>
|
||||||
{/* Check does route have children and perform routing generation for children */}
|
{routesCrawler(parentPath ?? "" + route.path, route.children)}
|
||||||
|
|
||||||
{route.children
|
|
||||||
? routesCrawler(parentPath ?? "" + route.path, route.children)
|
|
||||||
: null}
|
|
||||||
<Route
|
<Route
|
||||||
key={index}
|
key={v4()}
|
||||||
path={parentPath ? parentPath + route.path : route.path}
|
path={parentPath ? parentPath + route.path : route.path}
|
||||||
component={route.component}
|
component={route.component}
|
||||||
/>
|
/>
|
||||||
</>
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<Route
|
||||||
|
key={v4()}
|
||||||
|
path={parentPath ? parentPath + route.path : route.path}
|
||||||
|
component={route.component}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -38,7 +42,7 @@ const routesCrawler = (
|
|||||||
*/
|
*/
|
||||||
const SwitchRoutesGenerator = () => {
|
const SwitchRoutesGenerator = () => {
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch key={v4()}>
|
||||||
{routesCrawler()}
|
{routesCrawler()}
|
||||||
<Route component={() => <>404</>} />
|
<Route component={() => <>404</>} />
|
||||||
</Switch>
|
</Switch>
|
@ -3,11 +3,11 @@ import { RiHome3Fill } from "react-icons/ri";
|
|||||||
import { IoMdOptions } from "react-icons/io";
|
import { IoMdOptions } from "react-icons/io";
|
||||||
import { FaUsers } from "react-icons/fa";
|
import { FaUsers } from "react-icons/fa";
|
||||||
// ----- IMPORT PAGES -----
|
// ----- IMPORT PAGES -----
|
||||||
import AdministrationPage from "../features/AdministrationPage";
|
import AdministrationPage from "../pages/AdministrationPage";
|
||||||
import HomePage from "../features/HomePage";
|
import HomePage from "../pages/HomePage";
|
||||||
// ----- IMPORT CONSTS -----
|
// ----- IMPORT CONSTS -----
|
||||||
import { ADMINISTRATION, SETTINGS } from "../consts";
|
import { ADMINISTRATION, SETTINGS } from "../consts";
|
||||||
import SettingsAdvancedPage from "../features/SettingsAdvancedPage";
|
import SettingsAdvancedPage from "../pages/SettingsAdvancedPage";
|
||||||
|
|
||||||
// ----- TYPE DEFINITIONS -----
|
// ----- TYPE DEFINITIONS -----
|
||||||
export type Routes = {
|
export type Routes = {
|
||||||
|
@ -6,5 +6,10 @@ export default ({ mode }) => {
|
|||||||
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
||||||
return defineConfig({
|
return defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
build: {
|
||||||
|
watch: {
|
||||||
|
clearScreen: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user