Big comeback to DaisyUI, removed other stuff
This commit is contained in:
parent
82e0b21c06
commit
8ea78084c9
@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<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" />
|
||||||
|
@ -10,9 +10,14 @@
|
|||||||
"preview": "vite preview --host"
|
"preview": "vite preview --host"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.11.1",
|
||||||
|
"@emotion/styled": "^11.11.0",
|
||||||
"@hookform/resolvers": "^3.3.2",
|
"@hookform/resolvers": "^3.3.2",
|
||||||
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@tanstack/react-table": "^8.10.7",
|
"@tanstack/react-table": "^8.10.7",
|
||||||
|
"appwrite": "^13.0.0",
|
||||||
"axios": "^1.6.1",
|
"axios": "^1.6.1",
|
||||||
|
"daisyui": "latest",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@ -38,7 +43,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.4.4",
|
"eslint-plugin-react-refresh": "^0.4.4",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.1.0",
|
||||||
"tailwindcss": "^3.3.5",
|
"tailwindcss": "^3.3.5",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.5.0"
|
"vite": "^4.5.0"
|
||||||
|
3000
pnpm-lock.yaml
generated
Normal file
3000
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
|
"postcss-import": {},
|
||||||
|
"tailwindcss/nesting": {},
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
|
BIN
public/pattern.png
Executable file
BIN
public/pattern.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 733 KiB |
33
src/components/ThemeButton.tsx
Normal file
33
src/components/ThemeButton.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import useTheme from "../hooks/useTheme";
|
||||||
|
import inDev from "../utils/inDebug";
|
||||||
|
|
||||||
|
const ThemeButton = () => {
|
||||||
|
const [isDark, toggleTheme] = useTheme();
|
||||||
|
return (
|
||||||
|
<label className="swap swap-rotate btn btn-outline btn-circle">
|
||||||
|
<input
|
||||||
|
checked={isDark}
|
||||||
|
className="hidden"
|
||||||
|
onChange={() => inDev(() => console.log("Theme changed"))}
|
||||||
|
onClick={toggleTheme}
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
className="swap-on fill-current w-6 h-6"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
className="swap-off fill-current w-6 h-6"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeButton;
|
@ -1,3 +1,4 @@
|
|||||||
|
// -------- ENVIRONMENT VARIABLES --------
|
||||||
export const viteEnv = import.meta.env;
|
export const viteEnv = import.meta.env;
|
||||||
//Main configuration, static data, mostly used here
|
//Main configuration, static data, mostly used here
|
||||||
export const main = {
|
export const main = {
|
||||||
@ -11,3 +12,7 @@ export const about = {
|
|||||||
program_description: `This is a ${main.program_name} for <purpose>.`,
|
program_description: `This is a ${main.program_name} for <purpose>.`,
|
||||||
program_authors: [{ name: "Author Name", email: "email@example.com" }],
|
program_authors: [{ name: "Author Name", email: "email@example.com" }],
|
||||||
};
|
};
|
||||||
|
// -------- FRONTEND CONFIGURATION --------
|
||||||
|
// sizes in pixels
|
||||||
|
export const topbarSize = 64;
|
||||||
|
export const sidebarSize = 256;
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
|
import ThemeButton from "../components/ThemeButton";
|
||||||
import { main } from "../configure";
|
import { main } from "../configure";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="space-x-4 space-y-6 m-4">
|
||||||
{main.program_name} v{main.program_version}
|
<div className="px-5 mt-6">
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc magna massa,
|
<h1 className="text-center ">
|
||||||
ornare quis interdum a, cursus in quam. Quisque risus libero, cursus eget
|
{main.program_name} v{main.program_version}
|
||||||
eros vitae, aliquam placerat velit. Vivamus luctus eros id sagittis luctus.
|
</h1>
|
||||||
Pellentesque felis nulla, rhoncus viverra nunc vitae, viverra aliquam ante.
|
<div>
|
||||||
Ut feugiat mattis tempor.
|
<ThemeButton />
|
||||||
</>
|
</div>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
src/hooks/useLocalStorage.ts
Normal file
35
src/hooks/useLocalStorage.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
/**
|
||||||
|
* Works like useState but stores the value in local storage
|
||||||
|
* @param key Create a key to store the value in local storage
|
||||||
|
* @param initialValue Assign an initial value to the key
|
||||||
|
* @returns [storedValue, setValue] Returns the stored value and a function to set the value
|
||||||
|
*/
|
||||||
|
const useLocalStorage = <T>(
|
||||||
|
key: string,
|
||||||
|
initialValue: T,
|
||||||
|
): [T, (value: T) => void] => {
|
||||||
|
const [storedValue, setStoredValue] = useState<T>(() => {
|
||||||
|
try {
|
||||||
|
const item = window.localStorage.getItem(key);
|
||||||
|
return item ? JSON.parse(item) : initialValue;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return initialValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const setValue = (value: T) => {
|
||||||
|
try {
|
||||||
|
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||||
|
setStoredValue(valueToStore);
|
||||||
|
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return [storedValue, setValue];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLocalStorage;
|
30
src/hooks/useTheme.ts
Normal file
30
src/hooks/useTheme.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useEffect, useState } 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 toggleTheme = () => {
|
||||||
|
const newTheme = theme === "light" ? "dark" : "light";
|
||||||
|
setTheme(newTheme);
|
||||||
|
setIsDark(newTheme === "dark");
|
||||||
|
document.documentElement.dataset.theme = newTheme;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply the theme on component mount
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.dataset.theme = theme;
|
||||||
|
}, [theme]); // Re-run effect when theme changes
|
||||||
|
|
||||||
|
return [isDark, toggleTheme];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useTheme;
|
@ -4,6 +4,7 @@ import { setupAxiosInterceptors } from "./api/AxiosService";
|
|||||||
import { viteEnv } from "./configure";
|
import { viteEnv } from "./configure";
|
||||||
import App from "./features/App";
|
import App from "./features/App";
|
||||||
import inDev from "./utils/inDebug";
|
import inDev from "./utils/inDebug";
|
||||||
|
import { Global, css } from "@emotion/react";
|
||||||
|
|
||||||
setupAxiosInterceptors();
|
setupAxiosInterceptors();
|
||||||
inDev(() => console.log(viteEnv));
|
inDev(() => console.log(viteEnv));
|
||||||
@ -11,6 +12,11 @@ inDev(() => console.log(viteEnv));
|
|||||||
// Hook react to the HTML element with id="root"
|
// Hook react to the HTML element with id="root"
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
<Global
|
||||||
|
styles={css`
|
||||||
|
// You can add more global styles here
|
||||||
|
`}
|
||||||
|
/>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
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;
|
|
||||||
};
|
|
12
style.css
12
style.css
@ -1,15 +1,3 @@
|
|||||||
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900&display=swap");
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
|
|
||||||
@layer base {
|
|
||||||
html {
|
|
||||||
@apply text-neutral-800;
|
|
||||||
}
|
|
||||||
html.dark {
|
|
||||||
@apply text-neutral-50;
|
|
||||||
@apply bg-neutral-800;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
@ -1,7 +1,40 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{js,ts,jsx,tsx}", "./index.html"],
|
content: ["./src/**/*.{js,ts,jsx,tsx}", "./index.html"],
|
||||||
// safelist is used to allow classes to not be purged by tailwind
|
// safelist is used to allow classes to not be purged by tailwind
|
||||||
safelist: ["alert-info", "alert-success", "alert-warning", "alert-error"],
|
safelist: ["alert-info", "alert-success", "alert-warning", "alert-error"],
|
||||||
plugins: [require("@tailwindcss/typography")],
|
theme: {
|
||||||
|
extend: {
|
||||||
|
spacing: {
|
||||||
|
128: "32rem",
|
||||||
|
144: "36rem",
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
"4xl": "2rem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
darkMode: "class",
|
||||||
|
plugins: [
|
||||||
|
require("@tailwindcss/typography"),
|
||||||
|
require("@tailwindcss/forms"),
|
||||||
|
require("daisyui"),
|
||||||
|
],
|
||||||
|
daisyui: {
|
||||||
|
themes: [
|
||||||
|
{
|
||||||
|
light: {
|
||||||
|
primary: "#38bdf8",
|
||||||
|
secondary: "#10b981",
|
||||||
|
accent: "#f59e0b",
|
||||||
|
neutral: "#e5e7eb",
|
||||||
|
"base-100": "#f3f4f6",
|
||||||
|
info: "#67e8f9",
|
||||||
|
success: "#6ee7b7",
|
||||||
|
warning: "#fde047",
|
||||||
|
error: "#f87171",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"dark",
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user