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
|
||||
- [react-icons](https://react-icons.github.io/react-icons/) - big icon library
|
||||
@ -11,8 +11,9 @@
|
||||
## Usage
|
||||
|
||||
1. Setup `VITE_APP_NAME` in `.env.{mode}` files
|
||||
2. run: `bun run dev`
|
||||
3. To build for production, run: `bun run build`
|
||||
2. Setup `VITE_BASE_PATH` in `.env.{mode}` files
|
||||
3. run: `bun run dev`
|
||||
4. To build for production, run: `bun run build`
|
||||
|
||||
_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">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<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>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<script type="module" src="src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -4,7 +4,7 @@
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"dev": "vite serve",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview --host"
|
||||
@ -18,6 +18,7 @@
|
||||
"axios": "^1.6.1",
|
||||
"daisyui": "latest",
|
||||
"echarts": "^5.4.3",
|
||||
"jotai": "^2.6.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"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 { main } from "../configure";
|
||||
import ThemeButton from "./components/ThemeButton";
|
||||
import { main } from "./configure";
|
||||
import { Router } from "wouter";
|
||||
import SwitchRoutesGenerator from "../routes/RouteStructureGenerator";
|
||||
import DebugNavigationButtonsGenerator from "../routes/DebugNavigationButtonsGenerator";
|
||||
import SwitchRoutesGenerator from "./routes/SwitchRoutesGenerator";
|
||||
import GenerateNavigationButtonsDebug from "./routes/GenerateNavigationButtonsDebug";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
// Router component with base path
|
||||
<Router base={main.basePath}>
|
||||
<div className="space-x-4 space-y-6">
|
||||
<div className="px-5 mt-6">
|
||||
{/* Displaying program name and version */}
|
||||
<h1 className="text-center ">
|
||||
{main.program_name} v{main.program_version}
|
||||
<p>Base path is: {main.basePath}</p>
|
||||
</h1>
|
||||
<div>
|
||||
{/* ThemeButton component */}
|
||||
<ThemeButton />
|
||||
</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" />
|
||||
|
||||
<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 */}
|
||||
{/* SwitchRoutesGenerator component */}
|
||||
<SwitchRoutesGenerator />
|
||||
</div>
|
||||
</div>
|
@ -1,28 +1,21 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect } from "react";
|
||||
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] => {
|
||||
// Initialize theme from local storage or default value
|
||||
const [theme, setTheme] = useLocalStorage<string>("theme", "light");
|
||||
|
||||
// Determine if the current theme is dark
|
||||
const [isDark, setIsDark] = useState<boolean>(theme === "dark");
|
||||
const isDark = theme === "dark";
|
||||
|
||||
const toggleTheme = () => {
|
||||
console.log("toggleTheme called");
|
||||
const newTheme = theme === "light" ? "dark" : "light";
|
||||
setTheme(newTheme);
|
||||
setIsDark(newTheme === "dark");
|
||||
document.documentElement.dataset.theme = newTheme;
|
||||
};
|
||||
|
||||
// Apply the theme on component mount
|
||||
useEffect(() => {
|
||||
console.log("useEffect called");
|
||||
document.documentElement.dataset.theme = theme;
|
||||
}, [theme]); // Re-run effect when theme changes
|
||||
}, [theme]);
|
||||
|
||||
return [isDark, toggleTheme];
|
||||
};
|
||||
|
@ -2,10 +2,11 @@ import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { setupAxiosInterceptors } from "./api/AxiosService";
|
||||
import { viteEnv } from "./configure";
|
||||
import App from "./features/App";
|
||||
import App from "./App";
|
||||
import inDev from "./utils/inDebug";
|
||||
import { Global, css } from "@emotion/react";
|
||||
import "/style.css";
|
||||
import { Provider } from "jotai";
|
||||
|
||||
setupAxiosInterceptors();
|
||||
inDev(() => console.log(viteEnv));
|
||||
@ -18,6 +19,8 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
// You can add more global styles here
|
||||
`}
|
||||
/>
|
||||
<Provider>
|
||||
<App />
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import { atom, useAtom } from "jotai";
|
||||
const counterAtom = atom(0);
|
||||
|
||||
const HomePage = () => {
|
||||
const [counter, setCounter] = useState<number>(0);
|
||||
const [counter, setCounter] = useAtom(counterAtom);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>HomePage</p>
|
@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import * as R from "../routes/routes";
|
||||
import { v4 } from "uuid";
|
||||
import { Link } from "wouter";
|
||||
import { Routes } from "../routes/routes";
|
||||
import * as R from "./routes";
|
||||
import { Routes } from "./routes";
|
||||
import inDev from "../utils/inDebug";
|
||||
|
||||
const routesCrawler = (
|
||||
@ -9,19 +10,18 @@ const routesCrawler = (
|
||||
routesPack: Routes[] = R.routesRoot,
|
||||
): JSX.Element[] => {
|
||||
// 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
|
||||
inDev(() =>
|
||||
console.log(
|
||||
"routesCrawler",
|
||||
"routesCrawler buttons",
|
||||
parentPath ? parentPath + route.path : route.path,
|
||||
),
|
||||
);
|
||||
return (
|
||||
<div className="join">
|
||||
<div key={v4()} className="join">
|
||||
{/* Check does route have children and perform routing generation for children */}
|
||||
<Link
|
||||
key={index}
|
||||
className={`btn join-item hover:btn-primary duration-75 `}
|
||||
href={
|
||||
parentPath ? parentPath + route.path : route.path != "/" ? route.path : ""
|
||||
@ -43,8 +43,8 @@ const routesCrawler = (
|
||||
/**
|
||||
* 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>;
|
||||
};
|
||||
|
||||
export default DebugNavigationButtonsGenerator;
|
||||
export default GenerateNavigationButtonsDebug;
|
@ -1,34 +1,38 @@
|
||||
import React from "react";
|
||||
import { v4 } from "uuid";
|
||||
import { Route, Switch } from "wouter";
|
||||
import { Routes } from "./routes";
|
||||
import * as R from "./routes";
|
||||
import inDev from "../utils/inDebug";
|
||||
import * as R from "./routes";
|
||||
import { Routes } from "./routes";
|
||||
|
||||
const routesCrawler = (
|
||||
parentPath?: string,
|
||||
routesPack: Routes[] = R.routesRoot,
|
||||
): JSX.Element[] => {
|
||||
// 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
|
||||
inDev(() =>
|
||||
console.log(
|
||||
"routesCrawler",
|
||||
"routesCrawler routes",
|
||||
parentPath ? parentPath + route.path : route.path,
|
||||
),
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{/* Check does route have children and perform routing generation for children */}
|
||||
|
||||
{route.children
|
||||
? routesCrawler(parentPath ?? "" + route.path, route.children)
|
||||
: null}
|
||||
return route.children ? (
|
||||
<React.Fragment key={v4()}>
|
||||
{routesCrawler(parentPath ?? "" + route.path, route.children)}
|
||||
<Route
|
||||
key={index}
|
||||
key={v4()}
|
||||
path={parentPath ? parentPath + route.path : route.path}
|
||||
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 = () => {
|
||||
return (
|
||||
<Switch>
|
||||
<Switch key={v4()}>
|
||||
{routesCrawler()}
|
||||
<Route component={() => <>404</>} />
|
||||
</Switch>
|
@ -3,11 +3,11 @@ import { RiHome3Fill } from "react-icons/ri";
|
||||
import { IoMdOptions } from "react-icons/io";
|
||||
import { FaUsers } from "react-icons/fa";
|
||||
// ----- IMPORT PAGES -----
|
||||
import AdministrationPage from "../features/AdministrationPage";
|
||||
import HomePage from "../features/HomePage";
|
||||
import AdministrationPage from "../pages/AdministrationPage";
|
||||
import HomePage from "../pages/HomePage";
|
||||
// ----- IMPORT CONSTS -----
|
||||
import { ADMINISTRATION, SETTINGS } from "../consts";
|
||||
import SettingsAdvancedPage from "../features/SettingsAdvancedPage";
|
||||
import SettingsAdvancedPage from "../pages/SettingsAdvancedPage";
|
||||
|
||||
// ----- TYPE DEFINITIONS -----
|
||||
export type Routes = {
|
||||
|
@ -6,5 +6,10 @@ export default ({ mode }) => {
|
||||
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
||||
return defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
watch: {
|
||||
clearScreen: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user