Big comeback to DaisyUI, removed other stuff
This commit is contained in:
parent
82e0b21c06
commit
8ea78084c9
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
@ -10,9 +10,14 @@
|
||||
"preview": "vite preview --host"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@hookform/resolvers": "^3.3.2",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tanstack/react-table": "^8.10.7",
|
||||
"appwrite": "^13.0.0",
|
||||
"axios": "^1.6.1",
|
||||
"daisyui": "latest",
|
||||
"echarts": "^5.4.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -38,7 +43,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier": "^3.1.0",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"typescript": "^5.2.2",
|
||||
"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 {
|
||||
plugins: {
|
||||
"postcss-import": {},
|
||||
"tailwindcss/nesting": {},
|
||||
tailwindcss: {},
|
||||
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;
|
||||
//Main configuration, static data, mostly used here
|
||||
export const main = {
|
||||
@ -11,3 +12,7 @@ export const about = {
|
||||
program_description: `This is a ${main.program_name} for <purpose>.`,
|
||||
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";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<div className="space-x-4 space-y-6 m-4">
|
||||
<div className="px-5 mt-6">
|
||||
<h1 className="text-center ">
|
||||
{main.program_name} v{main.program_version}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc magna massa,
|
||||
ornare quis interdum a, cursus in quam. Quisque risus libero, cursus eget
|
||||
eros vitae, aliquam placerat velit. Vivamus luctus eros id sagittis luctus.
|
||||
Pellentesque felis nulla, rhoncus viverra nunc vitae, viverra aliquam ante.
|
||||
Ut feugiat mattis tempor.
|
||||
</>
|
||||
</h1>
|
||||
<div>
|
||||
<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 App from "./features/App";
|
||||
import inDev from "./utils/inDebug";
|
||||
import { Global, css } from "@emotion/react";
|
||||
|
||||
setupAxiosInterceptors();
|
||||
inDev(() => console.log(viteEnv));
|
||||
@ -11,6 +12,11 @@ inDev(() => console.log(viteEnv));
|
||||
// Hook react to the HTML element with id="root"
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<Global
|
||||
styles={css`
|
||||
// You can add more global styles here
|
||||
`}
|
||||
/>
|
||||
<App />
|
||||
</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;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
@apply text-neutral-800;
|
||||
}
|
||||
html.dark {
|
||||
@apply text-neutral-50;
|
||||
@apply bg-neutral-800;
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
@ -1,7 +1,40 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./src/**/*.{js,ts,jsx,tsx}", "./index.html"],
|
||||
// safelist is used to allow classes to not be purged by tailwind
|
||||
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