From 6cb7b831e8e09342a4193a408700576eb8f8f0db Mon Sep 17 00:00:00 2001 From: David Arranz Date: Thu, 6 Jun 2024 13:05:54 +0200 Subject: [PATCH] . --- .vscode/settings.json | 5 +- client/.env.development | 2 + client/.env.production | 0 client/.eslintrc.cjs | 16 + client/.gitignore | 24 ++ client/components.json | 17 + client/index.html | 18 + client/package.json | 80 +++++ client/postcss.config.js | 7 + client/public/vite.svg | 1 + client/src/App.css | 42 +++ client/src/App.tsx | 43 +++ client/src/Routes.tsx | 64 ++++ client/src/Typography.tsx | 130 +++++++ client/src/app/auth/AuthLayout.tsx | 11 + client/src/app/auth/index.ts | 1 + client/src/assets/react.svg | 1 + client/src/components/Container/Container.tsx | 31 ++ client/src/components/Container/index.ts | 1 + client/src/components/Forms/FormGroup.tsx | 67 ++++ client/src/components/Forms/FormLabel.tsx | 26 ++ .../src/components/Forms/FormMoneyField.tsx | 137 ++++++++ client/src/components/Forms/FormProps.tsx | 27 ++ .../components/Forms/FormTextAreaField.tsx | 70 ++++ client/src/components/Forms/FormTextField.tsx | 120 +++++++ client/src/components/Forms/index.ts | 5 + .../LoadingIndicator.module.css | 24 ++ .../LoadingIndicator/LoadingIndicator.tsx | 55 +++ .../LoadingSpinIcon/LoadingSpinIcon.tsx | 31 ++ .../LoadingIndicator/LoadingSpinIcon/index.ts | 1 + .../src/components/LoadingIndicator/index.ts | 1 + .../LoadingOverlay/LoadingOverlay.module.css | 8 + .../LoadingOverlay/LoadingOverlay.tsx | 21 ++ client/src/components/LoadingOverlay/index.ts | 1 + .../ProtectedRoute/ProtectedRoute.tsx | 28 ++ client/src/components/ProtectedRoute/index.ts | 1 + .../TailwindIndicator/TailwindIndicator.tsx | 16 + .../src/components/TailwindIndicator/index.ts | 1 + client/src/components/UeckoLogo/UeckoLogo.tsx | 9 + client/src/components/UeckoLogo/index.ts | 0 client/src/components/index.ts | 6 + client/src/index.css | 64 ++++ client/src/lib/axios/HttpError.ts | 207 +++++++++++ client/src/lib/axios/axiosInstance.ts | 120 +++++++ .../src/lib/axios/createAxiosAuthActions.ts | 66 ++++ .../src/lib/axios/createAxiosDataProvider.ts | 309 +++++++++++++++++ .../src/lib/axios/createJSONDataProvider.ts | 65 ++++ client/src/lib/axios/index.ts | 2 + client/src/lib/axios/setupInterceptors.ts | 104 ++++++ client/src/lib/helpers/index.ts | 0 client/src/lib/hooks/index.ts | 15 + client/src/lib/hooks/useAuth/AuthActions.ts | 41 +++ client/src/lib/hooks/useAuth/AuthContext.tsx | 51 +++ client/src/lib/hooks/useAuth/index.ts | 5 + client/src/lib/hooks/useAuth/useAuth.tsx | 8 + .../src/lib/hooks/useAuth/useIsLoggedIn.tsx | 16 + client/src/lib/hooks/useAuth/useLogin.tsx | 36 ++ client/src/lib/hooks/useBreadcrumbs.tsx | 19 ++ .../src/lib/hooks/useDataSource/DataSource.ts | 82 +++++ .../hooks/useDataSource/DataSourceContext.tsx | 11 + client/src/lib/hooks/useDataSource/index.ts | 10 + .../lib/hooks/useDataSource/useDataSource.tsx | 10 + .../src/lib/hooks/useDataSource/useList.tsx | 78 +++++ .../src/lib/hooks/useDataSource/useMany.tsx | 37 ++ client/src/lib/hooks/useDataSource/useOne.tsx | 47 +++ .../src/lib/hooks/useDataSource/useRemove.tsx | 30 ++ .../lib/hooks/useDataSource/useRemoveMany.tsx | 13 + client/src/lib/hooks/useDataSource/useSave.ts | 43 +++ client/src/lib/hooks/useDataTable/helper.ts | 192 +++++++++++ client/src/lib/hooks/useDataTable/index.ts | 3 + .../lib/hooks/useDataTable/useDataTable.tsx | 321 ++++++++++++++++++ .../useDataTable/useDataTableColumns.tsx | 42 +++ .../hooks/useDataTable/useQueryDataTable.tsx | 127 +++++++ .../src/lib/hooks/useLoadingOvertime/index.ts | 1 + .../useLoadingOvertime/useLoadingOvertime.ts | 104 ++++++ client/src/lib/hooks/useMounted/index.ts | 1 + client/src/lib/hooks/useMounted/useMounted.ts | 13 + client/src/lib/hooks/usePagination/index.ts | 2 + .../lib/hooks/usePagination/usePagination.tsx | 50 +++ .../usePagination/usePaginationParams.tsx | 52 +++ .../src/lib/hooks/useQueryKey/KeyBuilder.ts | 181 ++++++++++ client/src/lib/hooks/useQueryKey/index.ts | 5 + .../lib/hooks/useRemoveAlertDialog/index.ts | 1 + .../useRemoveAlertDialog.tsx | 107 ++++++ client/src/lib/hooks/useResizeObserver.tsx | 32 ++ client/src/lib/hooks/useTheme/index.ts | 1 + client/src/lib/hooks/useTheme/useTheme.tsx | 72 ++++ client/src/lib/hooks/useUrlId/index.ts | 1 + client/src/lib/hooks/useUrlId/useUrlId.tsx | 6 + client/src/lib/hooks/useWindowResize.tsx | 17 + client/src/lib/utils.ts | 6 + client/src/main.tsx | 10 + client/src/pages/ErrorPage.tsx | 51 +++ client/src/pages/LoginPage.tsx | 155 +++++++++ client/src/spanish-joi-messages.json | 93 +++++ client/src/ui/accordion.tsx | 56 +++ client/src/ui/alert-dialog.tsx | 139 ++++++++ client/src/ui/alert.tsx | 59 ++++ client/src/ui/aspect-ratio.tsx | 5 + client/src/ui/autosize-textarea.tsx | 73 ++++ client/src/ui/avatar.tsx | 48 +++ client/src/ui/badge.tsx | 36 ++ client/src/ui/breadcrumb.tsx | 115 +++++++ client/src/ui/button.tsx | 56 +++ client/src/ui/calendar.tsx | 64 ++++ client/src/ui/card.tsx | 79 +++++ client/src/ui/carousel.tsx | 260 ++++++++++++++ client/src/ui/checkbox.tsx | 28 ++ client/src/ui/collapsible.tsx | 9 + client/src/ui/command.tsx | 153 +++++++++ client/src/ui/context-menu.tsx | 198 +++++++++++ client/src/ui/dialog.tsx | 120 +++++++ client/src/ui/drawer.tsx | 116 +++++++ client/src/ui/dropdown-menu.tsx | 198 +++++++++++ client/src/ui/form.tsx | 177 ++++++++++ client/src/ui/hover-card.tsx | 27 ++ client/src/ui/index.ts | 43 +++ client/src/ui/input-otp.tsx | 69 ++++ client/src/ui/input.tsx | 25 ++ client/src/ui/label.tsx | 24 ++ client/src/ui/menubar.tsx | 234 +++++++++++++ client/src/ui/navigation-menu.tsx | 128 +++++++ client/src/ui/page-header.tsx | 71 ++++ client/src/ui/pagination.tsx | 117 +++++++ client/src/ui/popover.tsx | 29 ++ client/src/ui/progress.tsx | 26 ++ client/src/ui/radio-group.tsx | 42 +++ client/src/ui/resizable.tsx | 43 +++ client/src/ui/scroll-area.tsx | 46 +++ client/src/ui/select.tsx | 158 +++++++++ client/src/ui/separator.tsx | 29 ++ client/src/ui/sheet.tsx | 138 ++++++++ client/src/ui/skeleton.tsx | 15 + client/src/ui/slider.tsx | 26 ++ client/src/ui/switch.tsx | 27 ++ client/src/ui/table.tsx | 117 +++++++ client/src/ui/tabs.tsx | 53 +++ client/src/ui/textarea.tsx | 24 ++ client/src/ui/toast.tsx | 127 +++++++ client/src/ui/toaster.tsx | 33 ++ client/src/ui/toggle-group.tsx | 58 ++++ client/src/ui/toggle.tsx | 43 +++ client/src/ui/tooltip.tsx | 27 ++ client/src/ui/use-autosize-textarea.tsx | 40 +++ client/src/ui/use-toast.ts | 193 +++++++++++ client/src/vite-env.d.ts | 1 + client/tailwind.config.js | 108 ++++++ client/tsconfig.eslint.json | 12 + client/tsconfig.json | 33 ++ client/tsconfig.node.json | 11 + client/vite.config.ts | 34 ++ server/package.json | 5 +- .../login/presenter/Login.presenter.ts | 2 +- 153 files changed, 8506 insertions(+), 5 deletions(-) create mode 100644 client/.env.development create mode 100644 client/.env.production create mode 100644 client/.eslintrc.cjs create mode 100644 client/.gitignore create mode 100644 client/components.json create mode 100644 client/index.html create mode 100644 client/package.json create mode 100644 client/postcss.config.js create mode 100644 client/public/vite.svg create mode 100644 client/src/App.css create mode 100644 client/src/App.tsx create mode 100644 client/src/Routes.tsx create mode 100644 client/src/Typography.tsx create mode 100644 client/src/app/auth/AuthLayout.tsx create mode 100644 client/src/app/auth/index.ts create mode 100644 client/src/assets/react.svg create mode 100644 client/src/components/Container/Container.tsx create mode 100644 client/src/components/Container/index.ts create mode 100644 client/src/components/Forms/FormGroup.tsx create mode 100644 client/src/components/Forms/FormLabel.tsx create mode 100644 client/src/components/Forms/FormMoneyField.tsx create mode 100644 client/src/components/Forms/FormProps.tsx create mode 100644 client/src/components/Forms/FormTextAreaField.tsx create mode 100644 client/src/components/Forms/FormTextField.tsx create mode 100644 client/src/components/Forms/index.ts create mode 100644 client/src/components/LoadingIndicator/LoadingIndicator.module.css create mode 100644 client/src/components/LoadingIndicator/LoadingIndicator.tsx create mode 100644 client/src/components/LoadingIndicator/LoadingSpinIcon/LoadingSpinIcon.tsx create mode 100644 client/src/components/LoadingIndicator/LoadingSpinIcon/index.ts create mode 100644 client/src/components/LoadingIndicator/index.ts create mode 100644 client/src/components/LoadingOverlay/LoadingOverlay.module.css create mode 100644 client/src/components/LoadingOverlay/LoadingOverlay.tsx create mode 100644 client/src/components/LoadingOverlay/index.ts create mode 100644 client/src/components/ProtectedRoute/ProtectedRoute.tsx create mode 100644 client/src/components/ProtectedRoute/index.ts create mode 100644 client/src/components/TailwindIndicator/TailwindIndicator.tsx create mode 100644 client/src/components/TailwindIndicator/index.ts create mode 100644 client/src/components/UeckoLogo/UeckoLogo.tsx create mode 100644 client/src/components/UeckoLogo/index.ts create mode 100644 client/src/components/index.ts create mode 100644 client/src/index.css create mode 100644 client/src/lib/axios/HttpError.ts create mode 100644 client/src/lib/axios/axiosInstance.ts create mode 100644 client/src/lib/axios/createAxiosAuthActions.ts create mode 100644 client/src/lib/axios/createAxiosDataProvider.ts create mode 100644 client/src/lib/axios/createJSONDataProvider.ts create mode 100644 client/src/lib/axios/index.ts create mode 100644 client/src/lib/axios/setupInterceptors.ts create mode 100644 client/src/lib/helpers/index.ts create mode 100644 client/src/lib/hooks/index.ts create mode 100644 client/src/lib/hooks/useAuth/AuthActions.ts create mode 100644 client/src/lib/hooks/useAuth/AuthContext.tsx create mode 100644 client/src/lib/hooks/useAuth/index.ts create mode 100644 client/src/lib/hooks/useAuth/useAuth.tsx create mode 100644 client/src/lib/hooks/useAuth/useIsLoggedIn.tsx create mode 100644 client/src/lib/hooks/useAuth/useLogin.tsx create mode 100644 client/src/lib/hooks/useBreadcrumbs.tsx create mode 100644 client/src/lib/hooks/useDataSource/DataSource.ts create mode 100644 client/src/lib/hooks/useDataSource/DataSourceContext.tsx create mode 100644 client/src/lib/hooks/useDataSource/index.ts create mode 100644 client/src/lib/hooks/useDataSource/useDataSource.tsx create mode 100644 client/src/lib/hooks/useDataSource/useList.tsx create mode 100644 client/src/lib/hooks/useDataSource/useMany.tsx create mode 100644 client/src/lib/hooks/useDataSource/useOne.tsx create mode 100644 client/src/lib/hooks/useDataSource/useRemove.tsx create mode 100644 client/src/lib/hooks/useDataSource/useRemoveMany.tsx create mode 100644 client/src/lib/hooks/useDataSource/useSave.ts create mode 100644 client/src/lib/hooks/useDataTable/helper.ts create mode 100644 client/src/lib/hooks/useDataTable/index.ts create mode 100644 client/src/lib/hooks/useDataTable/useDataTable.tsx create mode 100644 client/src/lib/hooks/useDataTable/useDataTableColumns.tsx create mode 100644 client/src/lib/hooks/useDataTable/useQueryDataTable.tsx create mode 100644 client/src/lib/hooks/useLoadingOvertime/index.ts create mode 100644 client/src/lib/hooks/useLoadingOvertime/useLoadingOvertime.ts create mode 100644 client/src/lib/hooks/useMounted/index.ts create mode 100644 client/src/lib/hooks/useMounted/useMounted.ts create mode 100644 client/src/lib/hooks/usePagination/index.ts create mode 100644 client/src/lib/hooks/usePagination/usePagination.tsx create mode 100644 client/src/lib/hooks/usePagination/usePaginationParams.tsx create mode 100644 client/src/lib/hooks/useQueryKey/KeyBuilder.ts create mode 100644 client/src/lib/hooks/useQueryKey/index.ts create mode 100644 client/src/lib/hooks/useRemoveAlertDialog/index.ts create mode 100644 client/src/lib/hooks/useRemoveAlertDialog/useRemoveAlertDialog.tsx create mode 100644 client/src/lib/hooks/useResizeObserver.tsx create mode 100644 client/src/lib/hooks/useTheme/index.ts create mode 100644 client/src/lib/hooks/useTheme/useTheme.tsx create mode 100644 client/src/lib/hooks/useUrlId/index.ts create mode 100644 client/src/lib/hooks/useUrlId/useUrlId.tsx create mode 100644 client/src/lib/hooks/useWindowResize.tsx create mode 100644 client/src/lib/utils.ts create mode 100644 client/src/main.tsx create mode 100644 client/src/pages/ErrorPage.tsx create mode 100644 client/src/pages/LoginPage.tsx create mode 100644 client/src/spanish-joi-messages.json create mode 100644 client/src/ui/accordion.tsx create mode 100644 client/src/ui/alert-dialog.tsx create mode 100644 client/src/ui/alert.tsx create mode 100644 client/src/ui/aspect-ratio.tsx create mode 100644 client/src/ui/autosize-textarea.tsx create mode 100644 client/src/ui/avatar.tsx create mode 100644 client/src/ui/badge.tsx create mode 100644 client/src/ui/breadcrumb.tsx create mode 100644 client/src/ui/button.tsx create mode 100644 client/src/ui/calendar.tsx create mode 100644 client/src/ui/card.tsx create mode 100644 client/src/ui/carousel.tsx create mode 100644 client/src/ui/checkbox.tsx create mode 100644 client/src/ui/collapsible.tsx create mode 100644 client/src/ui/command.tsx create mode 100644 client/src/ui/context-menu.tsx create mode 100644 client/src/ui/dialog.tsx create mode 100644 client/src/ui/drawer.tsx create mode 100644 client/src/ui/dropdown-menu.tsx create mode 100644 client/src/ui/form.tsx create mode 100644 client/src/ui/hover-card.tsx create mode 100644 client/src/ui/index.ts create mode 100644 client/src/ui/input-otp.tsx create mode 100644 client/src/ui/input.tsx create mode 100644 client/src/ui/label.tsx create mode 100644 client/src/ui/menubar.tsx create mode 100644 client/src/ui/navigation-menu.tsx create mode 100644 client/src/ui/page-header.tsx create mode 100644 client/src/ui/pagination.tsx create mode 100644 client/src/ui/popover.tsx create mode 100644 client/src/ui/progress.tsx create mode 100644 client/src/ui/radio-group.tsx create mode 100644 client/src/ui/resizable.tsx create mode 100644 client/src/ui/scroll-area.tsx create mode 100644 client/src/ui/select.tsx create mode 100644 client/src/ui/separator.tsx create mode 100644 client/src/ui/sheet.tsx create mode 100644 client/src/ui/skeleton.tsx create mode 100644 client/src/ui/slider.tsx create mode 100644 client/src/ui/switch.tsx create mode 100644 client/src/ui/table.tsx create mode 100644 client/src/ui/tabs.tsx create mode 100644 client/src/ui/textarea.tsx create mode 100644 client/src/ui/toast.tsx create mode 100644 client/src/ui/toaster.tsx create mode 100644 client/src/ui/toggle-group.tsx create mode 100644 client/src/ui/toggle.tsx create mode 100644 client/src/ui/tooltip.tsx create mode 100644 client/src/ui/use-autosize-textarea.tsx create mode 100644 client/src/ui/use-toast.ts create mode 100644 client/src/vite-env.d.ts create mode 100644 client/tailwind.config.js create mode 100644 client/tsconfig.eslint.json create mode 100644 client/tsconfig.json create mode 100644 client/tsconfig.node.json create mode 100644 client/vite.config.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 1471927..041ce2b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,8 @@ "prettier.useEditorConfig": false, "prettier.useTabs": false, "prettier.configPath": ".prettierrc", - "asciidoc.antora.enableAntoraSupport": true + "asciidoc.antora.enableAntoraSupport": true, + + // other vscode settings + "tailwindCSS.rootFontSize": 16 // <- your root font size here } diff --git a/client/.env.development b/client/.env.development new file mode 100644 index 0000000..6521d95 --- /dev/null +++ b/client/.env.development @@ -0,0 +1,2 @@ +VITE_API_URL=http://127.0.0.1:4001/api/v1 +VITE_API_KEY=e175f809ba71fb2765ad5e60f9d77596-es19 \ No newline at end of file diff --git a/client/.env.production b/client/.env.production new file mode 100644 index 0000000..e69de29 diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs new file mode 100644 index 0000000..59014c1 --- /dev/null +++ b/client/.eslintrc.cjs @@ -0,0 +1,16 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "@typescript-eslint/no-explicit-any": "warn", + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, +}; diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/components.json b/client/components.json new file mode 100644 index 0000000..497ae3c --- /dev/null +++ b/client/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "src/", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..a51a4e4 --- /dev/null +++ b/client/index.html @@ -0,0 +1,18 @@ + + + + + + + + + Uecko + + + + +
+ + + + \ No newline at end of file diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..608a98f --- /dev/null +++ b/client/package.json @@ -0,0 +1,80 @@ +{ + "name": "@uecko-presupuestador/client", + "private": true, + "version": "1.0.0", + "author": "Rodax Software ", + "type": "module", + "scripts": { + "dev": "vite --host", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@hookform/resolvers": "^3.5.0", + "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-aspect-ratio": "^1.0.3", + "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-context-menu": "^2.1.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-hover-card": "^1.0.7", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.0.4", + "@radix-ui/react-navigation-menu": "^1.1.4", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-scroll-area": "^1.0.5", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-toggle": "^1.0.3", + "@radix-ui/react-toggle-group": "^1.0.4", + "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/react-query": "^5.39.0", + "axios": "^1.7.2", + "class-variance-authority": "^0.7.0", + "cmdk": "^1.0.0", + "date-fns": "^3.6.0", + "joi": "^17.13.1", + "lucide-react": "^0.379.0", + "react": "^18.2.0", + "react-beautiful-dnd": "^13.1.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.2.0", + "react-hook-form": "^7.51.5", + "react-resizable-panels": "^2.0.19", + "react-router-dom": "^6.23.1", + "react-secure-storage": "^1.3.2", + "react-toastify": "^10.0.5", + "react-wrap-balancer": "^1.1.1" + }, + "devDependencies": { + "@tanstack/react-query-devtools": "^5.39.0", + "@types/node": "^20.14.0", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.19", + "clsx": "^2.1.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.38", + "postcss-import": "^16.1.0", + "tailwind-merge": "^2.3.0", + "tailwindcss": "^3.4.3", + "typescript": "^5.2.2", + "vite": "^5.2.12" + } +} diff --git a/client/postcss.config.js b/client/postcss.config.js new file mode 100644 index 0000000..4e85a41 --- /dev/null +++ b/client/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + "postcss-import": {}, + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/client/public/vite.svg b/client/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/client/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/App.css b/client/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/client/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..091b9a6 --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,43 @@ +import { AuthProvider, ThemeProvider } from "@/lib/hooks"; +import { TooltipProvider } from "@/ui"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { Suspense } from "react"; +import { ToastContainer } from "react-toastify"; +import { Routes } from "./Routes"; +import { LoadingOverlay, TailwindIndicator } from "./components"; +import { createAxiosDataProvider } from "./lib/axios"; +import { createAxiosAuthActions } from "./lib/axios/createAxiosAuthActions"; +import { DataSourceProvider } from "./lib/hooks/useDataSource/DataSourceContext"; + +function App() { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + staleTime: 10000, // Specify a staleTime to only fetch when the data is older than a certain amount of time + }, + }, + }); + + return ( + + + + + + }> + + + + + + + + + + + ); +} + +export default App; diff --git a/client/src/Routes.tsx b/client/src/Routes.tsx new file mode 100644 index 0000000..5591968 --- /dev/null +++ b/client/src/Routes.tsx @@ -0,0 +1,64 @@ +import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { ProtectedRoute } from "./components"; +import { AuthContextState, useAuth } from "./lib/hooks"; +import { LoginPage } from "./pages/LoginPage"; + +export const Routes = () => { + const { isLoggedIn } = useAuth() as AuthContextState; + + // Define public routes accessible to all users + const routesForPublic = [ + { + path: "/service", + element:
Service Page
, + }, + { + path: "/about-us", + element:
About Us
, + }, + ]; + + // Define routes accessible only to authenticated users + const routesForAuthenticatedOnly = [ + { + path: "/admin", + element: , // Wrap the component in ProtectedRoute + children: [ + { + path: "", + element:
Dashboard
, + }, + { + path: "profile", + element:
User Profile
, + }, + { + path: "logout", + element:
Logout
, + }, + ], + }, + ]; + + // Define routes accessible only to non-authenticated users + const routesForNotAuthenticatedOnly = [ + { + path: "/", + element:
Home Page
, + }, + { + path: "/login", + Component: LoginPage, + }, + ]; + + // Combine and conditionally include routes based on authentication status + const router = createBrowserRouter([ + ...routesForPublic, + ...(!isLoggedIn ? routesForNotAuthenticatedOnly : []), + ...routesForAuthenticatedOnly, + ]); + + // Provide the router configuration using RouterProvider + return ; +}; diff --git a/client/src/Typography.tsx b/client/src/Typography.tsx new file mode 100644 index 0000000..6c03be7 --- /dev/null +++ b/client/src/Typography.tsx @@ -0,0 +1,130 @@ +import { Container } from "./components/Container"; + +export function TypographyDemo() { + return ( + +

The Joke Tax Chronicles

+

+ Once upon a time, in a far-off land, there was a very lazy king who + spent all day lounging on his throne. One day, his advisors came to him + with a problem: the kingdom was running out of money. +

+
+        
+          <article class="prose"> <h1>Garlic bread with cheese: What
+          the science tells us</h1> <p> For years parents have
+          espoused the health benefits of eating garlic bread with cheese to
+          their children, with the food earning such an iconic status in our
+          culture that kids will often dress up as warm, cheesy loaf for
+          Halloween. </p> <p> But a recent study shows that the
+          celebrated appetizer may be linked to a series of rabies cases
+          springing up around the country. </p> <!-- ... -->
+          </article>
+        
+      
+

The King's Plan

+

+ The king thought long and hard, and finally came up with{" "} + + a brilliant plan + + : he would tax the jokes in the kingdom. +

+
+ "After all," he said, "everyone enjoys a good joke, so it's only fair + that they should pay for the privilege." +
+

+ The Joke Tax +

+

+ The king's subjects were not amused. They grumbled and complained, but + the king was firm: +

+
    +
  • 1st level of puns: 5 gold coins
  • +
  • 2nd level of jokes: 10 gold coins
  • +
  • 3rd level of one-liners : 20 gold coins
  • +
+

+ As a result, people stopped telling jokes, and the kingdom fell into a + gloom. But there was one person who refused to let the king's + foolishness get him down: a court jester named Jokester. +

+

+ Jokester's Revolt +

+

+ Jokester began sneaking into the castle in the middle of the night and + leaving jokes all over the place: under the king's pillow, in his soup, + even in the royal toilet. The king was furious, but he couldn't seem to + stop Jokester. +

+

+ And then, one day, the people of the kingdom discovered that the jokes + left by Jokester were so funny that they couldn't help but laugh. And + once they started laughing, they couldn't stop. +

+

+ The People's Rebellion +

+

+ The people of the kingdom, feeling uplifted by the laughter, started to + tell jokes and puns again, and soon the entire kingdom was in on the + joke. +

+
+ + + + + + + + + + + + + + + + + + + + + +
+ King's Treasury + + People's happiness +
+ Empty + + Overflowing +
+ Modest + + Satisfied +
+ Full + + Ecstatic +
+
+

+ The king, seeing how much happier his subjects were, realized the error + of his ways and repealed the joke tax. Jokester was declared a hero, and + the kingdom lived happily ever after. +

+

+ The moral of the story is: never underestimate the power of a good laugh + and always be careful of bad ideas. +

+
+ ); +} diff --git a/client/src/app/auth/AuthLayout.tsx b/client/src/app/auth/AuthLayout.tsx new file mode 100644 index 0000000..9c2280f --- /dev/null +++ b/client/src/app/auth/AuthLayout.tsx @@ -0,0 +1,11 @@ +import { createAxiosDataProvider } from "@/lib/axios"; +import { Outlet } from "react-router-dom"; + +export const AuthLayout = (): JSX.Element => { + const authDataProvider = createAxiosDataProvider(import.meta.env.VITE_API_URL); + return ( + + + + ); +}; diff --git a/client/src/app/auth/index.ts b/client/src/app/auth/index.ts new file mode 100644 index 0000000..30e3bd9 --- /dev/null +++ b/client/src/app/auth/index.ts @@ -0,0 +1 @@ +export * from "./AuthLayout"; diff --git a/client/src/assets/react.svg b/client/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/client/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Container/Container.tsx b/client/src/components/Container/Container.tsx new file mode 100644 index 0000000..bbbd0b2 --- /dev/null +++ b/client/src/components/Container/Container.tsx @@ -0,0 +1,31 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import React from "react"; + +import { cn } from "@/lib/utils"; + +export const containerVariants = cva("p-6", { + variants: { + variant: { + full: "w-full", + boxed: "container max-w-3xl lg:max-w-5xl mx-auto", + }, + }, + defaultVariants: { + variant: "full", + }, +}); + +export interface ContainerProps + extends React.HTMLAttributes, + VariantProps { + as?: React.ElementType; +} + +export const Container = ({ + className, + as: Comp = "article", + variant, + ...props +}: ContainerProps) => ; + +Container.displayName = "Container"; diff --git a/client/src/components/Container/index.ts b/client/src/components/Container/index.ts new file mode 100644 index 0000000..a75e925 --- /dev/null +++ b/client/src/components/Container/index.ts @@ -0,0 +1 @@ +export * from "./Container"; diff --git a/client/src/components/Forms/FormGroup.tsx b/client/src/components/Forms/FormGroup.tsx new file mode 100644 index 0000000..0167fea --- /dev/null +++ b/client/src/components/Forms/FormGroup.tsx @@ -0,0 +1,67 @@ +import { cn } from "@/lib/utils"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/ui"; +import React, { useMemo } from "react"; + +interface FormGroupProps + extends React.HTMLAttributes, + React.HTMLAttributes { + description?: string; + actions?: JSX.Element; + footerActions?: JSX.Element; +} + +export const FormGroup = React.forwardRef( + ( + { className, title, description, actions, footerActions, children }, + ref + ) => { + const id = React.useId(); + const hasHeader = useMemo( + () => title || description || actions, + [title, description, actions] + ); + return ( + + {hasHeader && ( + +
+ {title && ( + + {title} + + )} + {description && ( + + {description} + + )} +
+ {actions &&
{actions}
} +
+ )} + {children} + {footerActions && ( + + {footerActions} + + )} +
+ ); + } +); + +FormGroup.displayName = "FormGroup"; diff --git a/client/src/components/Forms/FormLabel.tsx b/client/src/components/Forms/FormLabel.tsx new file mode 100644 index 0000000..c637db1 --- /dev/null +++ b/client/src/components/Forms/FormLabel.tsx @@ -0,0 +1,26 @@ +import * as UI from "@/ui"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import React from "react"; +import { FormInputProps } from "./FormProps"; + +export interface FormLabelProps { + label: string; + hint?: string; +} + +export const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + FormLabelProps & + Pick +>(({ label, hint, required, ...props }, ref) => { + const _hint = hint ? hint : required ? "obligatorio" : undefined; + return ( + + {label} + {_hint && {_hint}} + + ); +}); + +FormLabel.displayName = "FormLabel"; diff --git a/client/src/components/Forms/FormMoneyField.tsx b/client/src/components/Forms/FormMoneyField.tsx new file mode 100644 index 0000000..33c527b --- /dev/null +++ b/client/src/components/Forms/FormMoneyField.tsx @@ -0,0 +1,137 @@ +import { cn } from "@/lib/utils"; +import { + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, + Input, +} from "@/ui"; + +import { createElement } from "react"; +import { FormTextFieldProps } from "./FormTextField"; + +type FormMoneyFieldProps = Omit; + +// Spanish currency config +const moneyFormatter = Intl.NumberFormat("es-ES", { + currency: "EUR", + currencyDisplay: "symbol", + currencySign: "standard", + style: "currency", + minimumFractionDigits: 2, + maximumFractionDigits: 2, +}); + +export function FormMoneyField({ + label, + placeholder, + hint, + description, + required, + className, + leadIcon, + trailIcon, + button, + disabled, + errors, + name, + control, +}: FormMoneyFieldProps) { + /*const initialValue = props.form.getValues()[props.name] + ? moneyFormatter.format(props.form.getValues()[props.name]) + : ""; + + const [value, setValue] = useReducer((_: any, next: string) => { + const digits = next.replace(/\D/g, ""); + return moneyFormatter.format(Number(digits) / 100); + }, initialValue); + + function handleChange(realChangeFn: Function, formattedValue: string) { + const digits = formattedValue.replace(/\D/g, ""); + const realValue = Number(digits) / 100; + realChangeFn(realValue); + }*/ + + //const error = Boolean(errors && errors[name]); + + const transform = { + input: (value: any) => + isNaN(value) || value === 0 ? "" : moneyFormatter.format(value), + output: (event: React.ChangeEvent) => { + const output = parseInt(event.target.value, 10); + return isNaN(output) ? 0 : output; + }, + }; + + return ( + { + return ( + + {label && } +
+
+ {leadIcon && ( +
+ {createElement( + leadIcon, + { + className: "h-5 w-5 text-muted-foreground", + "aria-hidden": true, + }, + null, + )} +
+ )} + + + field.onChange(transform.output(e))} + value={transform.input(field.value)} + /> + + {trailIcon && ( +
+ {createElement( + trailIcon, + { + className: "h-5 w-5 text-muted-foreground", + "aria-hidden": true, + }, + null, + )} +
+ )} +
+ {button && <>{createElement(button)}} +
+ {description && {description}} + +
+ ); + }} + /> + ); +} diff --git a/client/src/components/Forms/FormProps.tsx b/client/src/components/Forms/FormProps.tsx new file mode 100644 index 0000000..5555871 --- /dev/null +++ b/client/src/components/Forms/FormProps.tsx @@ -0,0 +1,27 @@ +import { ReactNode } from "react"; + +export interface FormInputProps { + placeholder?: string; + description?: string; + required?: boolean; + className?: string; +} + +export interface FormInputWithIconProps { + leadIcon?: React.ElementType; + trailIcon?: React.ElementType; +} + +export interface FormButtonInputProps { + onClick?: (event: React.SyntheticEvent) => void; +} + +export interface FormInputWithButtonProps { + withButton: boolean; + onButtonClick: (event: React.SyntheticEvent) => void; +} + +export interface FormDialogInputProps { + content: ReactNode; + onClose?: (event: React.SyntheticEvent, payload: unknown) => void; +} diff --git a/client/src/components/Forms/FormTextAreaField.tsx b/client/src/components/Forms/FormTextAreaField.tsx new file mode 100644 index 0000000..ab95f19 --- /dev/null +++ b/client/src/components/Forms/FormTextAreaField.tsx @@ -0,0 +1,70 @@ +import { cn } from "@/lib/utils"; +import { + AutosizeTextarea, + FormControl, + FormDescription, + FormField, + FormItem, + FormMessage, + Textarea, +} from "@/ui"; + +import { + FieldErrors, + FieldPath, + FieldValues, + UseControllerProps, +} from "react-hook-form"; +import { FormLabel, FormLabelProps } from "./FormLabel"; + +export const FormTextAreaField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + label, + hint, + placeholder, + description, + autoSize, + className, + ...props +}: { + autoSize?: boolean; + placeholder?: string; + description?: string; + className?: string; +} & Partial & + UseControllerProps & { + errors?: FieldErrors; + }) => { + return ( + ( + + {label && } + + {autoSize ? ( + + ) : ( +