From ff97e04ea669610ad90e7d82ea595a10dcaab7cc Mon Sep 17 00:00:00 2001 From: Igor Barcik Date: Thu, 9 Nov 2023 13:10:22 +0100 Subject: [PATCH] Completly changed startup template --- .env.development | 2 +- .env.production | 2 +- README.md | 47 ++---- bun.lockb | Bin 154330 -> 139001 bytes package.json | 5 +- src/components/Breadcrumbs.tsx | 72 ---------- src/components/Card.tsx | 35 ----- src/components/CardGrid.tsx | 13 -- .../ConfirmationDialog/ConfirmationDialog.tsx | 39 ----- .../ConfirmationDialogProvider.tsx | 52 ------- .../useConfirmationDialog.ts | 23 --- src/components/Loader.tsx | 17 --- src/components/Modal.tsx | 109 -------------- src/components/NavigationTree.tsx | 79 ---------- src/components/Notification/Notification.tsx | 136 ------------------ .../Notification/NotificationProvider.tsx | 37 ----- .../Notification/NotificationStack.tsx | 25 ---- .../Notification/NotificationType.ts | 9 -- src/components/Notification/README.md | 2 - .../Notification/useNotification.ts | 34 ----- src/configure.tsx | 122 ---------------- src/contexts/ConfirmationDialogContext.tsx | 19 --- src/features/About/AboutPage.tsx | 32 ----- src/features/App.tsx | 95 ++---------- src/features/Debugger.tsx | 5 +- src/features/Home/HomePage.tsx | 5 - src/features/Login/LoginPage.tsx | 102 ------------- src/utils/Redirect.tsx | 18 --- 28 files changed, 23 insertions(+), 1113 deletions(-) delete mode 100644 src/components/Breadcrumbs.tsx delete mode 100644 src/components/Card.tsx delete mode 100644 src/components/CardGrid.tsx delete mode 100644 src/components/ConfirmationDialog/ConfirmationDialog.tsx delete mode 100644 src/components/ConfirmationDialog/ConfirmationDialogProvider.tsx delete mode 100644 src/components/ConfirmationDialog/useConfirmationDialog.ts delete mode 100644 src/components/Loader.tsx delete mode 100644 src/components/Modal.tsx delete mode 100644 src/components/NavigationTree.tsx delete mode 100644 src/components/Notification/Notification.tsx delete mode 100644 src/components/Notification/NotificationProvider.tsx delete mode 100644 src/components/Notification/NotificationStack.tsx delete mode 100644 src/components/Notification/NotificationType.ts delete mode 100644 src/components/Notification/README.md delete mode 100644 src/components/Notification/useNotification.ts delete mode 100644 src/contexts/ConfirmationDialogContext.tsx delete mode 100644 src/features/About/AboutPage.tsx delete mode 100644 src/features/Home/HomePage.tsx delete mode 100644 src/features/Login/LoginPage.tsx delete mode 100644 src/utils/Redirect.tsx diff --git a/.env.development b/.env.development index 55315b6..e8066b5 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,2 @@ -VITE_APP_NAME=App Development +# Config avaliable on development environment VITE_API_URL=http://localhost:7082 \ No newline at end of file diff --git a/.env.production b/.env.production index b61c784..1e9012e 100644 --- a/.env.production +++ b/.env.production @@ -1,2 +1,2 @@ -VITE_APP_NAME=App Production +# Config avaliable on production VITE_API_URL=http://192.168.179.36:7082 \ No newline at end of file diff --git a/README.md b/README.md index c38c468..9e709f5 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,12 @@ -# DaisyUI-React-Starter +# TailwindElements-React-Starter -[![Build Status](https://jenkins.bigoscloud.com/job/LuPa2/lastBuild/badge/icon)](https://jenkins.bigoscloud.com/job/LuPa2/lastBuild/) -Toolstack for my UI projects. I try to use `bun`. -> It was challenging stuff to configure, but now it works like a charm... I think. - - - -Used technologies: - -| Name | Description | -|--------|---| -| [TypeScript](https://www.typescriptlang.org/) | Main Language | -| [Vite](https://vitejs.dev/) | Bundler | -| [React](https://reactjs.org/) | Framework | -| [TailwindCSS](https://tailwindcss.com/) | CSS Framework | -| [PostCSS](https://postcss.org/) | CSS Processor | -| [DaisyUI](https://daisyui.com/) | A tool for transforming CSS with JavaScript | -| [RadixUI](https://www.radix-ui.com/) | Unstyled, accessible components for building high‑quality design systems and web apps in React | -| [Zod](https://zod.dev/) | TypeScript-first schema validation with static type inference | -| [React Router](https://reactrouter.com/) | Routing. Docs are lame, use [github](https://github.com/remix-run/react-router/tree/main) | -> Planned: [React Hook Form](https://react-hook-form.com/) Forms - -Linting, formatting and code editor: - -- [VSCode](https://code.visualstudio.com/) -- [ESlint](https://eslint.org/) -- [Prettier](https://prettier.io/) - -> Always up-to-date tools rather than stable old. -> It's not intended to be shared, but you can use it if you want. - -ToDo: -[ ] Add tests -[?] Add CI/CD -[?] Add SSR (Server Side Rendering) +- [Tailwind Elements](https://tw-elements.com/) - main styling +- [react-icons](https://react-icons.github.io/react-icons/) - big icon library +- [recharts](https://echarts.apache.org/) - charts library +- [wouter](https://github.com/molefrog/wouter) - router library +- [react-hook-form](https://github.com/react-hook-form/resolvers#zod) with [zod](https://zod.dev/) resolver - forms library +- [axios](https://axios-http.com/) - http request library +- [@tanstack/react-table](https://tanstack.com/table/v8/docs/adapters/react-table) - advanced table library ## Usage @@ -41,8 +14,6 @@ ToDo: 2. run: `bun run dev` 3. To build for production, run: `bun run build` -> Use for components styling and for components - ### Contact If you have any suggestions/opinions, please let me know in issues @@ -50,3 +21,5 @@ If you have any suggestions/opinions, please let me know in issues #### Dev notes [notes](docs/notes.md) + +> UI inspiration: diff --git a/bun.lockb b/bun.lockb index d8780bdefa462bd202711e40f5cad74873b610e0..d0a65d1b60587c5b1d8818871fc0783be4050d0b 100755 GIT binary patch delta 26152 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 delta 35713 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 { - 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 ( -
  • - {routeName} -
  • - ); - }; - - const renderTextBreadcrumb = (value: string) => { - if (value === pathParams.id) return null; - const routeName = capitalizeFirstLetter(findRouteNameByPath(value)); - return ( -
  • - {routeName} -
  • - ); - }; - - if (hideBreadcrumbBar(location.pathname)) { - return ( -
    {pathnames[0].toUpperCase()}
    - ); - } - - return ( -
    -
      -
    • - Home -
    • - {pathnames.map((value, index) => { - const isLast = index === pathnames.length - 1; - return isLast - ? renderTextBreadcrumb(value) - : renderLinkBreadcrumb(value, index, pathnames); - })} -
    -
    - ); -}; - -export default Breadcrumbs; diff --git a/src/components/Card.tsx b/src/components/Card.tsx deleted file mode 100644 index 8eb8223..0000000 --- a/src/components/Card.tsx +++ /dev/null @@ -1,35 +0,0 @@ -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 ( -
    - {!!bgImage && ( -
    - {title -
    - )} -
    -

    {title}

    -

    {description}

    -
    - - Go - -
    -
    -
    - ); -} diff --git a/src/components/CardGrid.tsx b/src/components/CardGrid.tsx deleted file mode 100644 index 9956d7a..0000000 --- a/src/components/CardGrid.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { ReactNode } from "react"; - -const CardGrid = ({ children }: { children: ReactNode }) => { - return ( -
    -
    - {children} -
    -
    - ); -}; - -export default CardGrid; diff --git a/src/components/ConfirmationDialog/ConfirmationDialog.tsx b/src/components/ConfirmationDialog/ConfirmationDialog.tsx deleted file mode 100644 index 2afad34..0000000 --- a/src/components/ConfirmationDialog/ConfirmationDialog.tsx +++ /dev/null @@ -1,39 +0,0 @@ -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 && ( - - - - - )} - - ); -}; - -export default ConfirmationDialog; diff --git a/src/components/ConfirmationDialog/ConfirmationDialogProvider.tsx b/src/components/ConfirmationDialog/ConfirmationDialogProvider.tsx deleted file mode 100644 index 7b76f23..0000000 --- a/src/components/ConfirmationDialog/ConfirmationDialogProvider.tsx +++ /dev/null @@ -1,52 +0,0 @@ -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 ( - - {children} - - ); -}; - -export default ConfirmationDialogProvider; diff --git a/src/components/ConfirmationDialog/useConfirmationDialog.ts b/src/components/ConfirmationDialog/useConfirmationDialog.ts deleted file mode 100644 index 4363baf..0000000 --- a/src/components/ConfirmationDialog/useConfirmationDialog.ts +++ /dev/null @@ -1,23 +0,0 @@ -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; diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx deleted file mode 100644 index aed735a..0000000 --- a/src/components/Loader.tsx +++ /dev/null @@ -1,17 +0,0 @@ -function Loader() { - return ( - - ); -} - -export default Loader; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx deleted file mode 100644 index 7802a2a..0000000 --- a/src/components/Modal.tsx +++ /dev/null @@ -1,109 +0,0 @@ -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 ( - -
    -

    {title ?? "Hello!"}

    -

    - {subTitle ?? "Press ESC key or click the button below to close"} -

    -

    {content}

    -
    -
    - - {children} -
    -
    -
    -
    - ); -}; - -export default Modal; diff --git a/src/components/NavigationTree.tsx b/src/components/NavigationTree.tsx deleted file mode 100644 index 89d45ce..0000000 --- a/src/components/NavigationTree.tsx +++ /dev/null @@ -1,79 +0,0 @@ -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 - *
      - * - *
    - * ``` - */ -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 ( -
      -
    • - - {route.additionalProps.name} - - {route.children ? ( -
        {GenerateNavigationEntries(route.children, combinedPath)}
      - ) : null} -
    • -
    - ); - } - // 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 ( -
  • - - {route.additionalProps.name} - -
  • - ); - } - }) || <>empty navigation tree - ); - }; - - return
    {GenerateNavigationEntries(props.routes)}
    ; -} - -export default NavigationTree; diff --git a/src/components/Notification/Notification.tsx b/src/components/Notification/Notification.tsx deleted file mode 100644 index d5048e5..0000000 --- a/src/components/Notification/Notification.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { useEffect, useState } from "react"; -import { INotification, NotificationStatus } from "./NotificationType"; - -enum NotificationColorsClasses { - "info" = "alert alert-info", - "success" = "alert alert-success", - "warning" = "alert alert-warning", - "error" = "alert alert-error", -} - -const selectIcon = (status: NotificationStatus) => { - switch (status) { - case "error": - return ( - - - - ); - case "success": - return ( - - - - ); - break; - case "info": - return ( - - - - ); - break; - case "warning": - return ( - - - - ); - break; - default: - return null; - break; - } -}; - -interface NotificationProps { - notification: INotification; - removeNotification: (id: number) => void; -} - -function Notification({ notification, removeNotification }: NotificationProps) { - const [counter, setCounter] = useState( - notification.duration, - ); - const isCloseable = !!!notification.duration; - - useEffect(() => { - // Skip if counter is undefined - if (counter === undefined) return; - const intervalId = setInterval(() => { - setCounter(counter - 1); - if (counter === 0) { - removeNotification(notification.id); - } - }, 1000); - return () => { - clearInterval(intervalId); - }; - }, [counter]); - return ( -
    - {selectIcon(notification.status)} - {notification.message} - {isCloseable ? ( - - ) : null} - {isCloseable ? null : ( -
    - - - -
    - )} -
    - ); -} - -export default Notification; diff --git a/src/components/Notification/NotificationProvider.tsx b/src/components/Notification/NotificationProvider.tsx deleted file mode 100644 index c48ee17..0000000 --- a/src/components/Notification/NotificationProvider.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { useState } from "react"; -import { INotification } from "./NotificationType"; - -interface INotificationStore { - notifications: INotification[] | []; - setNotifications: React.Dispatch>; - duration?: number; -} -// Store for notifications -export const NotificationContext = React.createContext({ - notifications: [], - setNotifications: () => {}, - duration: undefined, -}); - -// Just store for notifications, logic is in useNotification hook -export const NotificationProvider: React.FC<{ children: React.ReactNode }> = ({ - children, -}) => { - // Queue for notifications - const [notifications, setNotifications] = useState | []>( - [], - ); - NotificationContext.displayName = "Notifications"; - return ( - - {children} - - ); -}; - -export default NotificationProvider; diff --git a/src/components/Notification/NotificationStack.tsx b/src/components/Notification/NotificationStack.tsx deleted file mode 100644 index cf0e9c6..0000000 --- a/src/components/Notification/NotificationStack.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Notification from "./Notification"; -import { useNotification } from "./useNotification"; -// ----------------------------------------------------------- -// Component that renders notification stack -// ----------------------------------------------------------- -function NotificationStack() { - const { notifications, removeNotification } = useNotification(); - - // It takes notification array from hook and renders it - const renderNotifications = () => { - return notifications.map((notification) => { - return ( - - ); - }); - }; - - return
    {renderNotifications()}
    ; -} - -export default NotificationStack; diff --git a/src/components/Notification/NotificationType.ts b/src/components/Notification/NotificationType.ts deleted file mode 100644 index 4064dcf..0000000 --- a/src/components/Notification/NotificationType.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type NotificationStatus = "success" | "error" | "warning" | "info"; -export interface INotificationBase { - message: string; - status: NotificationStatus; - duration?: number; -} -export interface INotification extends INotificationBase { - id: number; -} diff --git a/src/components/Notification/README.md b/src/components/Notification/README.md deleted file mode 100644 index 5fefc38..0000000 --- a/src/components/Notification/README.md +++ /dev/null @@ -1,2 +0,0 @@ - # Notification - I wrote own notification module but its little bit laggy and buggy so I don't use it. diff --git a/src/components/Notification/useNotification.ts b/src/components/Notification/useNotification.ts deleted file mode 100644 index 8d1ee63..0000000 --- a/src/components/Notification/useNotification.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useContext } from "react"; -import { NotificationContext } from "./NotificationProvider"; -import { INotification, NotificationStatus } from "./NotificationType"; -// ----------------------------------------------------------- -// Hook for adding notifications to the queue in local storage -// ----------------------------------------------------------- -export function useNotification() { - const { notifications, setNotifications } = useContext(NotificationContext); - - const addNotificationToQueue = ( - message: string, - status: NotificationStatus, - duration?: number, - ) => { - const id = notifications.length + 1; - const notification: INotification = { id, message, status, duration }; - setNotifications([...notifications, notification]); - }; - const removeNotificationFromQueue = (id: number) => { - setNotifications( - notifications.filter((notification) => notification.id !== id), - ); - }; - const clearNotificationsQueue = () => { - setNotifications([]); - }; - - return { - notifications: notifications, - addNotification: addNotificationToQueue, - removeNotification: removeNotificationFromQueue, - clearNotifications: clearNotificationsQueue, - }; -} diff --git a/src/configure.tsx b/src/configure.tsx index 5edc7fd..843d440 100644 --- a/src/configure.tsx +++ b/src/configure.tsx @@ -1,124 +1,4 @@ -/* 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"; -import LoginPage from "./features/Login/LoginPage"; -import { flatternRoutingTable } from "./utils/RoutingTableUtils"; -import Debugger from "./features/Debugger"; -//---- -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: , - 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: ( - }> - - - ), - additionalProps: { - name: "Home", - disableRedirect: false, - disableInNavbar: false, - disableBreadcrumbBar: false, - }, - }, - // about page - { - path: "about", - element: , - additionalProps: { - name: "About", - disableRedirect: false, - disableInNavbar: false, - disableBreadcrumbBar: false, - }, - children: [ - { - path: "child", - element:
    DUPA CHILD
    , - additionalProps: { - name: "About Child", - disableRedirect: false, - disableInNavbar: false, - disableBreadcrumbBar: false, - }, - }, - ], - }, - // login page - { - path: "login", - element: , - additionalProps: { - name: "Login", - disableRedirect: false, - disableInNavbar: false, - disableBreadcrumbBar: false, - }, - }, - ], - }, -]; -// --- - -if (viteEnv.DEV) { - // Add Test page - // Test page is outside of mainframe, will be rendered in root (withius App where is entire navigation frame) - navigation.push({ - path: "/Test", - element:
    Test
    , - additionalProps: { - name: "Test", - disableRedirect: false, - disableInNavbar: false, - disableBreadcrumbBar: false, - }, - }); - // Add debugger page - navigation[0].children?.push({ - path: "debug_variables", - element: , - 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 { - additionalProps: RouteObjectAdditionalProps; - children?: CustomRouteObject[]; -} -//---- //Main configuration, static data, mostly used here export const main = { program_name: viteEnv.VITE_APP_NAME, @@ -129,5 +9,3 @@ export const about = { program_description: `This is a ${main.program_name} for .`, program_authors: [{ name: "Author Name", email: "email@example.com" }], }; -//---- -export const flatRoutes = flatternRoutingTable(navigation); diff --git a/src/contexts/ConfirmationDialogContext.tsx b/src/contexts/ConfirmationDialogContext.tsx deleted file mode 100644 index 5bdbb68..0000000 --- a/src/contexts/ConfirmationDialogContext.tsx +++ /dev/null @@ -1,19 +0,0 @@ -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>; - setCustomModalStyles: React.Dispatch>; -}; -// Create the context with default values -export const ConfirmationDialogContext = createContext< - ConfirmationDialogContextType | undefined ->(undefined); -ConfirmationDialogContext.displayName = "ConfirmationDialog"; diff --git a/src/features/About/AboutPage.tsx b/src/features/About/AboutPage.tsx deleted file mode 100644 index 5ecd399..0000000 --- a/src/features/About/AboutPage.tsx +++ /dev/null @@ -1,32 +0,0 @@ -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 ( -
    -
    -
    -

    About

    -

    {about.program_description}

    -

    Authors

    -
    - {about.program_authors.map((author) => ( -
    - {author.name} -{" "} - - {author.email} - -
    - ))} -
    -
    -
    -
    - ); -} - -export default AboutPage; diff --git a/src/features/App.tsx b/src/features/App.tsx index fc136c9..d4a7bb5 100644 --- a/src/features/App.tsx +++ b/src/features/App.tsx @@ -1,94 +1,15 @@ -import { HamburgerMenuIcon, SunIcon } 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"; +import { main } from "../configure"; +import Debugger from "./Debugger"; -/** 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 [theme, setTheme] = useState("adient"); - const [openDrawer, setOpenDrawer] = useState(false); - - useEffect(() => { - document.querySelector("html")?.setAttribute("data-theme", theme); - // 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; - }, [theme, openDrawer]); - - // Function on click drawer hamburger button - const handleDrawerStatus = () => { - setOpenDrawer(!openDrawer); - }; - return (
    - - {/* Root drawer container */} - {/* Drawer opening is controlled directly via css prop and next via local useState variable */} -
    - {/* A hidden checkbox to toggle the visibility of the drawer */} - - {/* The actual drawer content */} -
    - {/* Navbar */} -
    - {/* Left side navbar */} -
    - -

    {main.program_name}

    -
    - {/* Right side navbar */} -
    -
    - -
      - THEMEs HERE -
    -
    - -
    -
    - {/* App/active_drawer content */} -
    - {/*Automatically generated breadcrumbs based on routing table from configuration file and active path*/} - {/*Get active route and find it in routing file*/} - - -
    -
    - {/* Drawer sidebar wrapper */} -
    - {/* Dark overlay on mobile devices, clickable to close drawer */} - -
      - -
    -
    -
    +

    App

    +

    App

    + +

    + {main.program_name} v{main.program_version} +

    ); } diff --git a/src/features/Debugger.tsx b/src/features/Debugger.tsx index 3388869..6c543c2 100644 --- a/src/features/Debugger.tsx +++ b/src/features/Debugger.tsx @@ -1,5 +1,4 @@ -import { useLocation } from "react-router-dom"; -import { flatRoutes } from "../configure"; +import { useLocation } from "wouter"; function Debugger() { const windowLocation = window.location.pathname; @@ -10,7 +9,6 @@ function Debugger() { Flat Routes - {JSON.stringify(flatRoutes)}
    Locations @@ -22,7 +20,6 @@ function Debugger() {
    Routes - {JSON.stringify(flatRoutes)}
    ); diff --git a/src/features/Home/HomePage.tsx b/src/features/Home/HomePage.tsx deleted file mode 100644 index 2d4d6bf..0000000 --- a/src/features/Home/HomePage.tsx +++ /dev/null @@ -1,5 +0,0 @@ -function HomePage() { - return <>HOME; -} - -export default HomePage; diff --git a/src/features/Login/LoginPage.tsx b/src/features/Login/LoginPage.tsx deleted file mode 100644 index 770d52b..0000000 --- a/src/features/Login/LoginPage.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import { useState } from "react"; -import { SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form"; -import { useNavigate } from "react-router-dom"; -import { z } from "zod"; - -type Input = { - username: string; - password: string; -}; -const InputSchema = z.object({ - username: z.string().min(4, "Username must be at least 4 characters"), - password: z - .string() - .regex(/[A-Za-z\d@$!%*#?&]{4,}/, "Minimum four characters") - .regex(/(?=.*[A-Z])/, "At least one big letter") - .regex(/(?=.*\d)/, "At least one number") - .regex(/(?=.*[@$!%*#?&])/, "At least one special character"), -}); - -function LoginPage() { - const [isSubmitting, setIsSubmitting] = useState(false); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm({ - resolver: zodResolver(InputSchema), - }); - const navigate = useNavigate(); - const onSubmit: SubmitHandler = (data) => { - if (data.username === "admin" && data.password === "A0m!n") { - console.log("Login successful!", 3); - navigate("/products"); - } else { - console.log("Wrong username or password"); - } - setIsSubmitting(false); - }; - const onError: SubmitErrorHandler = () => { - console.log("Errors in form fields"); - }; - - return ( -
    -
    -
    -
    - - {errors.username && ( - - )} -
    -
    - - {errors.password && ( - - )} -
    - -
    -
    -
    - ); -} - -export default LoginPage; diff --git a/src/utils/Redirect.tsx b/src/utils/Redirect.tsx deleted file mode 100644 index 64a35d7..0000000 --- a/src/utils/Redirect.tsx +++ /dev/null @@ -1,18 +0,0 @@ -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;