From e0785a58e6037a32a5a0bcd4f3719ff4ffc53544 Mon Sep 17 00:00:00 2001 From: david Date: Mon, 5 May 2025 19:11:44 +0200 Subject: [PATCH] . --- apps/web/package.json | 11 +- apps/web/postcss.config.mjs | 2 +- apps/web/src/App.tsx | 7 +- apps/web/src/lib/api/get-api-authorization.ts | 9 + apps/web/src/lib/api/index.ts | 1 + .../{axiosInstance.ts => axios-instance.ts} | 2 +- ...ctions.ts => create-axios-auth-actions.ts} | 2 +- ...vider.ts => create-axios-data-provider.ts} | 4 +- ...pInterceptors.ts => setup-interceptors.ts} | 0 apps/web/src/lib/hooks/index.ts | 5 +- .../datasource-context.tsx} | 2 +- .../datasource.ts} | 0 .../web/src/lib/hooks/use-datasource/index.ts | 1 + apps/web/src/lib/hooks/use-theme/index.ts | 1 + .../useTheme.tsx => use-theme/use-theme.tsx} | 0 .../use-unsaved-changes-notifier/index.ts | 1 + .../use-unsaved-changes-notifier.tsx | 73 + .../use-warn-about-change.tsx | 10 + .../warn-about-change-context.tsx | 9 + .../warn-about-change-provider.tsx | 47 + apps/web/src/lib/hooks/useDataSource/index.ts | 1 - apps/web/src/lib/hooks/useTheme/index.ts | 1 - apps/web/src/lib/types.d.ts | 3 + apps/web/src/main.tsx | 15 +- docs/README.md | 2 +- package.json | 2 +- packages/{ui => shadcn-ui}/components.json | 11 +- packages/shadcn-ui/package.json | 80 + packages/{ui => shadcn-ui}/postcss.config.mjs | 0 .../shadcn-ui/src/components/accordion.tsx | 66 + .../shadcn-ui/src/components/alert-dialog.tsx | 157 ++ packages/shadcn-ui/src/components/alert.tsx | 66 + .../shadcn-ui/src/components/aspect-ratio.tsx | 11 + packages/shadcn-ui/src/components/avatar.tsx | 53 + packages/shadcn-ui/src/components/badge.tsx | 46 + .../shadcn-ui/src/components/breadcrumb.tsx | 109 + packages/shadcn-ui/src/components/button.tsx | 59 + .../shadcn-ui/src/components/calendar.tsx | 75 + packages/shadcn-ui/src/components/card.tsx | 92 + .../shadcn-ui/src/components/carousel.tsx | 241 +++ packages/shadcn-ui/src/components/chart.tsx | 353 +++ .../shadcn-ui/src/components/checkbox.tsx | 32 + .../shadcn-ui/src/components/collapsible.tsx | 33 + packages/shadcn-ui/src/components/command.tsx | 177 ++ .../shadcn-ui/src/components/context-menu.tsx | 252 +++ packages/shadcn-ui/src/components/dialog.tsx | 135 ++ packages/shadcn-ui/src/components/drawer.tsx | 132 ++ .../src/components/dropdown-menu.tsx | 257 +++ packages/shadcn-ui/src/components/form.tsx | 167 ++ .../shadcn-ui/src/components/hover-card.tsx | 44 + packages/shadcn-ui/src/components/index.tsx | 43 + .../shadcn-ui/src/components/input-otp.tsx | 77 + packages/shadcn-ui/src/components/input.tsx | 21 + packages/shadcn-ui/src/components/label.tsx | 24 + packages/shadcn-ui/src/components/menubar.tsx | 276 +++ .../src/components/navigation-menu.tsx | 168 ++ .../shadcn-ui/src/components/pagination.tsx | 127 ++ packages/shadcn-ui/src/components/popover.tsx | 48 + .../shadcn-ui/src/components/progress.tsx | 31 + .../shadcn-ui/src/components/radio-group.tsx | 45 + .../shadcn-ui/src/components/resizable.tsx | 56 + .../shadcn-ui/src/components/scroll-area.tsx | 58 + packages/shadcn-ui/src/components/select.tsx | 185 ++ .../shadcn-ui/src/components/separator.tsx | 28 + packages/shadcn-ui/src/components/sheet.tsx | 139 ++ packages/shadcn-ui/src/components/sidebar.tsx | 726 +++++++ .../shadcn-ui/src/components/skeleton.tsx | 13 + packages/shadcn-ui/src/components/slider.tsx | 63 + packages/shadcn-ui/src/components/sonner.tsx | 25 + packages/shadcn-ui/src/components/switch.tsx | 31 + packages/shadcn-ui/src/components/table.tsx | 116 + packages/shadcn-ui/src/components/tabs.tsx | 66 + .../shadcn-ui/src/components/textarea.tsx | 18 + .../shadcn-ui/src/components/toggle-group.tsx | 73 + packages/shadcn-ui/src/components/toggle.tsx | 47 + .../src/components/tooltip.tsx | 2 +- packages/shadcn-ui/src/hooks/use-mobile.ts | 19 + packages/{ui => shadcn-ui}/src/lib/utils.ts | 0 .../{ui => shadcn-ui}/src/styles/globals.css | 5 - packages/shadcn-ui/tsconfig.json | 11 + packages/typescript-config/react-library.json | 13 +- packages/ui/package.json | 23 +- packages/ui/src/components/button.tsx | 56 - packages/ui/src/components/custom-dialog.tsx | 55 + packages/ui/src/components/index.tsx | 3 + .../src/components/loading-overlay/index.tsx | 1 + .../loading-indicator.module.css | 24 + .../loading-overlay/loading-indicator.tsx | 54 + .../loading-overlay/loading-overlay.tsx | 26 + .../loading-overlay/loading-spin-icon.tsx | 27 + .../ui/src/components/tailwind-indicator.tsx | 14 + packages/ui/tsconfig.json | 5 + pnpm-lock.yaml | 1928 ++++++++++++++++- 93 files changed, 7508 insertions(+), 121 deletions(-) create mode 100644 apps/web/src/lib/api/get-api-authorization.ts create mode 100644 apps/web/src/lib/api/index.ts rename apps/web/src/lib/axios/{axiosInstance.ts => axios-instance.ts} (96%) rename apps/web/src/lib/axios/{createAxiosAuthActions.ts => create-axios-auth-actions.ts} (97%) rename apps/web/src/lib/axios/{createAxiosDataProvider.ts => create-axios-data-provider.ts} (99%) rename apps/web/src/lib/axios/{setupInterceptors.ts => setup-interceptors.ts} (100%) rename apps/web/src/lib/hooks/{useDataSource/DataSourceContext.tsx => use-datasource/datasource-context.tsx} (87%) rename apps/web/src/lib/hooks/{useDataSource/DataSource.ts => use-datasource/datasource.ts} (100%) create mode 100644 apps/web/src/lib/hooks/use-datasource/index.ts create mode 100644 apps/web/src/lib/hooks/use-theme/index.ts rename apps/web/src/lib/hooks/{useTheme/useTheme.tsx => use-theme/use-theme.tsx} (100%) create mode 100644 apps/web/src/lib/hooks/use-unsaved-changes-notifier/index.ts create mode 100644 apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx create mode 100644 apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-warn-about-change.tsx create mode 100644 apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-context.tsx create mode 100644 apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-provider.tsx delete mode 100644 apps/web/src/lib/hooks/useDataSource/index.ts delete mode 100644 apps/web/src/lib/hooks/useTheme/index.ts create mode 100644 apps/web/src/lib/types.d.ts rename packages/{ui => shadcn-ui}/components.json (56%) create mode 100644 packages/shadcn-ui/package.json rename packages/{ui => shadcn-ui}/postcss.config.mjs (100%) create mode 100644 packages/shadcn-ui/src/components/accordion.tsx create mode 100644 packages/shadcn-ui/src/components/alert-dialog.tsx create mode 100644 packages/shadcn-ui/src/components/alert.tsx create mode 100644 packages/shadcn-ui/src/components/aspect-ratio.tsx create mode 100644 packages/shadcn-ui/src/components/avatar.tsx create mode 100644 packages/shadcn-ui/src/components/badge.tsx create mode 100644 packages/shadcn-ui/src/components/breadcrumb.tsx create mode 100644 packages/shadcn-ui/src/components/button.tsx create mode 100644 packages/shadcn-ui/src/components/calendar.tsx create mode 100644 packages/shadcn-ui/src/components/card.tsx create mode 100644 packages/shadcn-ui/src/components/carousel.tsx create mode 100644 packages/shadcn-ui/src/components/chart.tsx create mode 100644 packages/shadcn-ui/src/components/checkbox.tsx create mode 100644 packages/shadcn-ui/src/components/collapsible.tsx create mode 100644 packages/shadcn-ui/src/components/command.tsx create mode 100644 packages/shadcn-ui/src/components/context-menu.tsx create mode 100644 packages/shadcn-ui/src/components/dialog.tsx create mode 100644 packages/shadcn-ui/src/components/drawer.tsx create mode 100644 packages/shadcn-ui/src/components/dropdown-menu.tsx create mode 100644 packages/shadcn-ui/src/components/form.tsx create mode 100644 packages/shadcn-ui/src/components/hover-card.tsx create mode 100644 packages/shadcn-ui/src/components/index.tsx create mode 100644 packages/shadcn-ui/src/components/input-otp.tsx create mode 100644 packages/shadcn-ui/src/components/input.tsx create mode 100644 packages/shadcn-ui/src/components/label.tsx create mode 100644 packages/shadcn-ui/src/components/menubar.tsx create mode 100644 packages/shadcn-ui/src/components/navigation-menu.tsx create mode 100644 packages/shadcn-ui/src/components/pagination.tsx create mode 100644 packages/shadcn-ui/src/components/popover.tsx create mode 100644 packages/shadcn-ui/src/components/progress.tsx create mode 100644 packages/shadcn-ui/src/components/radio-group.tsx create mode 100644 packages/shadcn-ui/src/components/resizable.tsx create mode 100644 packages/shadcn-ui/src/components/scroll-area.tsx create mode 100644 packages/shadcn-ui/src/components/select.tsx create mode 100644 packages/shadcn-ui/src/components/separator.tsx create mode 100644 packages/shadcn-ui/src/components/sheet.tsx create mode 100644 packages/shadcn-ui/src/components/sidebar.tsx create mode 100644 packages/shadcn-ui/src/components/skeleton.tsx create mode 100644 packages/shadcn-ui/src/components/slider.tsx create mode 100644 packages/shadcn-ui/src/components/sonner.tsx create mode 100644 packages/shadcn-ui/src/components/switch.tsx create mode 100644 packages/shadcn-ui/src/components/table.tsx create mode 100644 packages/shadcn-ui/src/components/tabs.tsx create mode 100644 packages/shadcn-ui/src/components/textarea.tsx create mode 100644 packages/shadcn-ui/src/components/toggle-group.tsx create mode 100644 packages/shadcn-ui/src/components/toggle.tsx rename packages/{ui => shadcn-ui}/src/components/tooltip.tsx (97%) create mode 100644 packages/shadcn-ui/src/hooks/use-mobile.ts rename packages/{ui => shadcn-ui}/src/lib/utils.ts (100%) rename packages/{ui => shadcn-ui}/src/styles/globals.css (96%) create mode 100644 packages/shadcn-ui/tsconfig.json delete mode 100644 packages/ui/src/components/button.tsx create mode 100644 packages/ui/src/components/custom-dialog.tsx create mode 100644 packages/ui/src/components/index.tsx create mode 100644 packages/ui/src/components/loading-overlay/index.tsx create mode 100644 packages/ui/src/components/loading-overlay/loading-indicator.module.css create mode 100644 packages/ui/src/components/loading-overlay/loading-indicator.tsx create mode 100644 packages/ui/src/components/loading-overlay/loading-overlay.tsx create mode 100644 packages/ui/src/components/loading-overlay/loading-spin-icon.tsx create mode 100644 packages/ui/src/components/tailwind-indicator.tsx diff --git a/apps/web/package.json b/apps/web/package.json index 7e53ce8f..0ffd0c62 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,7 +13,8 @@ "format": "biome format --write" }, "dependencies": { - "@repo/ui": "workspace:*", + "@repo/shadcn-ui": "workspace:*", + "@repo/rdx-ui": "workspace:*", "@tanstack/react-query": "^5.74.11", "axios": "^1.9.0", "i18next": "^25.0.2", @@ -23,7 +24,9 @@ "react-hook-form": "^7.55.0", "react-hook-form-persist": "^3.0.0", "react-i18next": "^15.0.1", - "react-secure-storage": "^1.3.2" + "react-router-dom": "^6.26.0", + "react-secure-storage": "^1.3.2", + "vite-plugin-html": "^3.2.2" }, "devDependencies": { "@biomejs/biome": "1.9.4", @@ -35,6 +38,8 @@ "@vitejs/plugin-react": "^4.4.1", "globals": "^16.0.0", "typescript": "~5.8.3", - "vite": "^6.3.4" + "vite": "^6.3.4", + "vite-plugin-robots": "^1.0.5", + "vite-plugin-static-copy": "^2.3.1" } } diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs index 039248f7..95c17d3a 100644 --- a/apps/web/postcss.config.mjs +++ b/apps/web/postcss.config.mjs @@ -1 +1 @@ -export { default } from "@repo/ui/postcss.config"; +export { default } from "../../packages/shadcn-ui/postcss.config.mjs"; diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index aed5dd80..07914bc2 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,12 +1,15 @@ -import { Button } from "@repo/ui/components/button"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { Suspense, useState } from "react"; -import "@repo/ui/globals.css"; +import { Button, Toaster, TooltipProvider } from "@repo/shadcn-ui/components"; import { I18nextProvider } from "react-i18next"; +import { DataSourceProvider, ThemeProvider, UnsavedWarnProvider } from "./lib/hooks"; import { i18n } from "./locales"; +import "@repo/shadcn-ui/globals.css"; +import { LoadingOverlay, TailwindIndicator } from "@repo/rdx-ui/components"; + function App() { const queryClient = new QueryClient({ defaultOptions: { diff --git a/apps/web/src/lib/api/get-api-authorization.ts b/apps/web/src/lib/api/get-api-authorization.ts new file mode 100644 index 00000000..d481ddf8 --- /dev/null +++ b/apps/web/src/lib/api/get-api-authorization.ts @@ -0,0 +1,9 @@ +import secureLocalStorage from "react-secure-storage"; + +export const getApiAuthorization = () => { + const authInfo: ILogin_Response_DTO = secureLocalStorage.getItem( + "uecko.auth" + ) as ILogin_Response_DTO; + + return authInfo && authInfo.token ? `Bearer ${authInfo.token}` : ""; +}; diff --git a/apps/web/src/lib/api/index.ts b/apps/web/src/lib/api/index.ts new file mode 100644 index 00000000..84574298 --- /dev/null +++ b/apps/web/src/lib/api/index.ts @@ -0,0 +1 @@ +export * from "./get-api-authorization"; diff --git a/apps/web/src/lib/axios/axiosInstance.ts b/apps/web/src/lib/axios/axios-instance.ts similarity index 96% rename from apps/web/src/lib/axios/axiosInstance.ts rename to apps/web/src/lib/axios/axios-instance.ts index 8e9f987d..fe3cdfd8 100644 --- a/apps/web/src/lib/axios/axiosInstance.ts +++ b/apps/web/src/lib/axios/axios-instance.ts @@ -1,5 +1,5 @@ import axiosClient from "axios"; -import { setupInterceptorsTo } from "./setupInterceptors"; +import { setupInterceptorsTo } from "./setup-interceptors"; // extend the AxiosRequestConfig interface and add two optional options raw and silent. I // https://dev.to/mperon/axios-error-handling-like-a-boss-333d diff --git a/apps/web/src/lib/axios/createAxiosAuthActions.ts b/apps/web/src/lib/axios/create-axios-auth-actions.ts similarity index 97% rename from apps/web/src/lib/axios/createAxiosAuthActions.ts rename to apps/web/src/lib/axios/create-axios-auth-actions.ts index 7a670a66..8184e980 100644 --- a/apps/web/src/lib/axios/createAxiosAuthActions.ts +++ b/apps/web/src/lib/axios/create-axios-auth-actions.ts @@ -1,5 +1,5 @@ import secureLocalStorage from "react-secure-storage"; -import { createAxiosInstance } from "./axiosInstance"; +import { createAxiosInstance } from "./axios-instance"; export const createAxiosAuthActions = ( apiUrl: string, diff --git a/apps/web/src/lib/axios/createAxiosDataProvider.ts b/apps/web/src/lib/axios/create-axios-data-provider.ts similarity index 99% rename from apps/web/src/lib/axios/createAxiosDataProvider.ts rename to apps/web/src/lib/axios/create-axios-data-provider.ts index 0772953a..0adcff51 100644 --- a/apps/web/src/lib/axios/createAxiosDataProvider.ts +++ b/apps/web/src/lib/axios/create-axios-data-provider.ts @@ -14,8 +14,8 @@ import { ISortItemDataProviderParam, IUpdateOneDataProviderParams, IUploadFileDataProviderParam, -} from "../hooks/useDataSource/DataSource"; -import { createAxiosInstance, defaultAxiosRequestConfig } from "./axiosInstance"; +} from "../hooks/use-datasource/datasource"; +import { createAxiosInstance, defaultAxiosRequestConfig } from "./axios-instance"; export const createAxiosDataProvider = ( apiUrl: string, diff --git a/apps/web/src/lib/axios/setupInterceptors.ts b/apps/web/src/lib/axios/setup-interceptors.ts similarity index 100% rename from apps/web/src/lib/axios/setupInterceptors.ts rename to apps/web/src/lib/axios/setup-interceptors.ts diff --git a/apps/web/src/lib/hooks/index.ts b/apps/web/src/lib/hooks/index.ts index 0f2be407..b1a92548 100644 --- a/apps/web/src/lib/hooks/index.ts +++ b/apps/web/src/lib/hooks/index.ts @@ -1,2 +1,3 @@ -export * from "./useDataSource"; -export * from "./useTheme"; +export * from "./use-datasource"; +export * from "./use-theme"; +export * from "./use-unsaved-changes-notifier"; diff --git a/apps/web/src/lib/hooks/useDataSource/DataSourceContext.tsx b/apps/web/src/lib/hooks/use-datasource/datasource-context.tsx similarity index 87% rename from apps/web/src/lib/hooks/useDataSource/DataSourceContext.tsx rename to apps/web/src/lib/hooks/use-datasource/datasource-context.tsx index 2f1945b2..60d099de 100644 --- a/apps/web/src/lib/hooks/useDataSource/DataSourceContext.tsx +++ b/apps/web/src/lib/hooks/use-datasource/datasource-context.tsx @@ -1,5 +1,5 @@ import { type PropsWithChildren, createContext } from "react"; -import { type IDataSource } from "./DataSource"; +import { type IDataSource } from "./datasource"; export const DataSourceContext = createContext(undefined); diff --git a/apps/web/src/lib/hooks/useDataSource/DataSource.ts b/apps/web/src/lib/hooks/use-datasource/datasource.ts similarity index 100% rename from apps/web/src/lib/hooks/useDataSource/DataSource.ts rename to apps/web/src/lib/hooks/use-datasource/datasource.ts diff --git a/apps/web/src/lib/hooks/use-datasource/index.ts b/apps/web/src/lib/hooks/use-datasource/index.ts new file mode 100644 index 00000000..8844e491 --- /dev/null +++ b/apps/web/src/lib/hooks/use-datasource/index.ts @@ -0,0 +1 @@ +export * from "./datasource-context"; diff --git a/apps/web/src/lib/hooks/use-theme/index.ts b/apps/web/src/lib/hooks/use-theme/index.ts new file mode 100644 index 00000000..767b68bf --- /dev/null +++ b/apps/web/src/lib/hooks/use-theme/index.ts @@ -0,0 +1 @@ +export * from "./use-theme"; diff --git a/apps/web/src/lib/hooks/useTheme/useTheme.tsx b/apps/web/src/lib/hooks/use-theme/use-theme.tsx similarity index 100% rename from apps/web/src/lib/hooks/useTheme/useTheme.tsx rename to apps/web/src/lib/hooks/use-theme/use-theme.tsx diff --git a/apps/web/src/lib/hooks/use-unsaved-changes-notifier/index.ts b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/index.ts new file mode 100644 index 00000000..223210e0 --- /dev/null +++ b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/index.ts @@ -0,0 +1 @@ +export * from "./warn-about-change-provider"; diff --git a/apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx new file mode 100644 index 00000000..0c3b7ad6 --- /dev/null +++ b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx @@ -0,0 +1,73 @@ +import { t } from "i18next"; +import { useCallback, useEffect } from "react"; +import { useBlocker } from "react-router-dom"; +import { useWarnAboutChange } from "./use-warn-about-change"; + +export type UnsavedChangesNotifierProps = { + title?: string; + subtitle?: string; + confirmText?: string; + cancelText?: string; + onConfirm?: () => void; + onCancel?: () => void; + type?: "success" | "error" | "warning" | "info"; +}; + +export const useUnsavedChangesNotifier = ({ + isDirty = false, + title = t("hooks.use_unsaved_changes_notifier.title"), + subtitle = t("hooks.use_unsaved_changes_notifier.subtitle"), + confirmText = t("hooks.use_unsaved_changes_notifier.confirm_text"), + cancelText = t("hooks.use_unsaved_changes_notifier.cancel_text"), + onConfirm, + onCancel, + type = "warning", +}: UnsavedChangesNotifierProps & { isDirty?: boolean }) => { + const blocker = useBlocker(isDirty); + const { show } = useWarnAboutChange(); + + const confirm = useCallback(() => { + if (!isDirty) return Promise.resolve(true); + + return new Promise((resolve) => { + show({ + title, + subtitle, + confirmText, + cancelText, + type, + onConfirm: () => { + resolve(true); + onConfirm?.(); + }, + onCancel: () => { + resolve(false); + onCancel?.(); + }, + }); + }); + }, [cancelText, confirmText, isDirty, onCancel, onConfirm, show, subtitle, title, type]); + + useEffect(() => { + if (blocker.state === "blocked") { + confirm().then((result) => { + if (result) blocker.proceed(); + else blocker.reset(); + }); + } + }, [blocker, confirm]); + + useEffect(() => { + if (isDirty) { + window.onbeforeunload = () => subtitle; + } + + return () => { + window.onbeforeunload = null; + }; + }, [isDirty, subtitle]); + + return { + confirm, + }; +}; diff --git a/apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-warn-about-change.tsx b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-warn-about-change.tsx new file mode 100644 index 00000000..2355b0ec --- /dev/null +++ b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/use-warn-about-change.tsx @@ -0,0 +1,10 @@ +import { useContext } from "react"; +import { UnsavedWarnContext } from "./warn-about-change-context"; + +export const useWarnAboutChange = () => { + const context = useContext(UnsavedWarnContext); + if (context === null) + throw new Error("useWarnAboutChange must be used within a UnsavedWarnProvider"); + + return context; +}; diff --git a/apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-context.tsx b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-context.tsx new file mode 100644 index 00000000..f8cfcd7c --- /dev/null +++ b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-context.tsx @@ -0,0 +1,9 @@ +import { createContext } from "react"; +import type { NullOr } from "../../types"; +import type { UnsavedChangesNotifierProps } from "./use-unsaved-changes-notifier"; + +export interface IUnsavedWarnContextState { + show: (options: NullOr) => void; +} + +export const UnsavedWarnContext = createContext>(null); diff --git a/apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-provider.tsx b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-provider.tsx new file mode 100644 index 00000000..3766b70e --- /dev/null +++ b/apps/web/src/lib/hooks/use-unsaved-changes-notifier/warn-about-change-provider.tsx @@ -0,0 +1,47 @@ +import { CustomDialog } from "@repo/rdx-ui/components"; +import { type PropsWithChildren, useCallback, useMemo, useState } from "react"; +import type { NullOr } from "../../types"; +import type { UnsavedChangesNotifierProps } from "./use-unsaved-changes-notifier"; +import { UnsavedWarnContext } from "./warn-about-change-context"; + +export const UnsavedWarnProvider = ({ children }: PropsWithChildren) => { + const [confirm, setConfirm] = useState>(null); + + const [open, toggle] = useState(false); + + const show = useCallback((confirmOptions: NullOr) => { + setConfirm(confirmOptions); + toggle(true); + }, []); + + const onConfirm = () => { + confirm?.onConfirm?.(); + toggle(false); + }; + + const onCancel = () => { + confirm?.onCancel?.(); + toggle(false); + }; + + const value = useMemo(() => ({ show }), [show]); + + return ( + + {children} + { + //console.log("onCancel"); + onCancel(); + }} + onConfirm={() => onConfirm()} + title={confirm?.title} + description={confirm?.subtitle} + confirmLabel={confirm?.confirmText} + cancelLabel={confirm?.cancelText} + isOpen={open} + /> + + ); +}; diff --git a/apps/web/src/lib/hooks/useDataSource/index.ts b/apps/web/src/lib/hooks/useDataSource/index.ts deleted file mode 100644 index 7f181e4f..00000000 --- a/apps/web/src/lib/hooks/useDataSource/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./DataSourceContext"; diff --git a/apps/web/src/lib/hooks/useTheme/index.ts b/apps/web/src/lib/hooks/useTheme/index.ts deleted file mode 100644 index 0fe58730..00000000 --- a/apps/web/src/lib/hooks/useTheme/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./useTheme"; diff --git a/apps/web/src/lib/types.d.ts b/apps/web/src/lib/types.d.ts new file mode 100644 index 00000000..317759de --- /dev/null +++ b/apps/web/src/lib/types.d.ts @@ -0,0 +1,3 @@ +export type NullOr = T | null; +export type UndefinedOr = T | undefined; +export type EmptyOr = NullOr>; diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 0b872751..b25d859b 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -2,8 +2,13 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "./App.tsx"; -createRoot(document.getElementById("factuges")!).render( - - - -); +const rootElement = document.getElementById("factuges"); +if (rootElement) { + createRoot(rootElement).render( + + + + ); +} else { + console.error("Root element with id 'factuges' not found."); +} diff --git a/docs/README.md b/docs/README.md index 4b760734..74e069eb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,7 +18,7 @@ This Turborepo includes the following packages/apps: - `docs`: a [Next.js](https://nextjs.org/) app - `web`: another [Next.js](https://nextjs.org/) app -- `@repo/ui`: a stub React component library shared by both `web` and `docs` applications +- `@repo/shadcn-ui`: a stub React component library shared by both `web` and `docs` applications - `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`) - `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo diff --git a/package.json b/package.json index d6fa0413..dfc51c6b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "dev:client": "turbo dev --filter=client", "format-and-lint": "biome check .", "format-and-lint:fix": "biome check . --write", - "ui:add": "pnpm --filter @repo/ui ui:add", + "ui:add": "pnpm --filter @repo/shadcn-ui ui:add", "create:package": "ts-node scripts/create-package.ts", "volta:install": "curl https://get.volta.sh | bash" }, diff --git a/packages/ui/components.json b/packages/shadcn-ui/components.json similarity index 56% rename from packages/ui/components.json rename to packages/shadcn-ui/components.json index 87eb4dd7..47ec4567 100644 --- a/packages/ui/components.json +++ b/packages/shadcn-ui/components.json @@ -5,16 +5,17 @@ "tsx": true, "tailwind": { "config": "", + "prefix": "", "css": "src/styles/globals.css", "baseColor": "neutral", "cssVariables": true }, "iconLibrary": "lucide", "aliases": { - "components": "@repo/ui/components", - "utils": "@repo/ui/lib/utils", - "hooks": "@repo/ui/hooks", - "lib": "@repo/ui/lib", - "ui": "@repo/ui/components" + "components": "@repo/shadcn-ui/components", + "utils": "@repo/shadcn-ui/lib/utils", + "hooks": "@repo/shadcn-ui/hooks", + "lib": "@repo/shadcn-ui/lib", + "ui": "@repo/shadcn-ui/components" } } diff --git a/packages/shadcn-ui/package.json b/packages/shadcn-ui/package.json new file mode 100644 index 00000000..2e633e4e --- /dev/null +++ b/packages/shadcn-ui/package.json @@ -0,0 +1,80 @@ +{ + "name": "@repo/shadcn-ui", + "version": "0.0.0", + "type": "module", + "private": true, + "scripts": { + "lint": "biome lint --fix", + "ui:add": "pnpm dlx shadcn@latest add" + }, + "dependencies": { + "@hookform/resolvers": "^5.0.1", + "@radix-ui/react-accordion": "^1.2.8", + "@radix-ui/react-alert-dialog": "^1.1.11", + "@radix-ui/react-aspect-ratio": "^1.1.4", + "@radix-ui/react-avatar": "^1.1.7", + "@radix-ui/react-checkbox": "^1.2.3", + "@radix-ui/react-collapsible": "^1.1.8", + "@radix-ui/react-context-menu": "^2.2.12", + "@radix-ui/react-dialog": "^1.1.11", + "@radix-ui/react-dropdown-menu": "^2.1.12", + "@radix-ui/react-hover-card": "^1.1.11", + "@radix-ui/react-label": "^2.1.4", + "@radix-ui/react-menubar": "^1.1.12", + "@radix-ui/react-navigation-menu": "^1.2.10", + "@radix-ui/react-popover": "^1.1.11", + "@radix-ui/react-progress": "^1.1.4", + "@radix-ui/react-radio-group": "^1.3.4", + "@radix-ui/react-scroll-area": "^1.2.6", + "@radix-ui/react-select": "^2.2.2", + "@radix-ui/react-separator": "^1.1.4", + "@radix-ui/react-slider": "^1.3.2", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-switch": "^1.2.2", + "@radix-ui/react-tabs": "^1.1.9", + "@radix-ui/react-toggle": "^1.1.6", + "@radix-ui/react-toggle-group": "^1.1.7", + "@radix-ui/react-tooltip": "^1.2.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.6.0", + "input-otp": "^1.4.2", + "lucide-react": "^0.503.0", + "next-themes": "^0.4.6", + "react": "^19.1.0", + "react-day-picker": "8.10.1", + "react-dom": "^19.1.0", + "react-hook-form": "^7.56.2", + "react-resizable-panels": "^3.0.1", + "recharts": "^2.15.3", + "sonner": "^2.0.3", + "tailwind-merge": "^3.2.0", + "tw-animate-css": "^1.2.8", + "vaul": "^1.1.2", + "zod": "^3.24.3" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@repo/typescript-config": "workspace:*", + "@tailwindcss/postcss": "^4.1.5", + "@turbo/gen": "^2.5.2", + "@types/node": "^22.15.3", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.3", + "tailwindcss": "^4.1.5", + "typescript": "^5.8.3" + }, + "exports": { + "./globals.css": "./src/styles/globals.css", + "./postcss.config": "./postcss.config.mjs", + "./components": "./src/components/index.tsx", + "./components/*": "./src/components/*.tsx", + "./lib/*": "./src/lib/*.ts", + "./hooks/*": [ + "./src/hooks/*.tsx", + "./src/hooks/*.ts" + ] + } +} diff --git a/packages/ui/postcss.config.mjs b/packages/shadcn-ui/postcss.config.mjs similarity index 100% rename from packages/ui/postcss.config.mjs rename to packages/shadcn-ui/postcss.config.mjs diff --git a/packages/shadcn-ui/src/components/accordion.tsx b/packages/shadcn-ui/src/components/accordion.tsx new file mode 100644 index 00000000..e3b868fe --- /dev/null +++ b/packages/shadcn-ui/src/components/accordion.tsx @@ -0,0 +1,66 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDownIcon } from "lucide-react" + +import { cn } from "@repo/shadcn-ui/lib/utils" + +function Accordion({ + ...props +}: React.ComponentProps) { + return +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/packages/shadcn-ui/src/components/alert-dialog.tsx b/packages/shadcn-ui/src/components/alert-dialog.tsx new file mode 100644 index 00000000..793cc755 --- /dev/null +++ b/packages/shadcn-ui/src/components/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@repo/shadcn-ui/lib/utils" +import { buttonVariants } from "@repo/shadcn-ui/components/button" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/packages/shadcn-ui/src/components/alert.tsx b/packages/shadcn-ui/src/components/alert.tsx new file mode 100644 index 00000000..cb11ebb3 --- /dev/null +++ b/packages/shadcn-ui/src/components/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@repo/shadcn-ui/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/packages/shadcn-ui/src/components/aspect-ratio.tsx b/packages/shadcn-ui/src/components/aspect-ratio.tsx new file mode 100644 index 00000000..3df3fd02 --- /dev/null +++ b/packages/shadcn-ui/src/components/aspect-ratio.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return +} + +export { AspectRatio } diff --git a/packages/shadcn-ui/src/components/avatar.tsx b/packages/shadcn-ui/src/components/avatar.tsx new file mode 100644 index 00000000..227995b0 --- /dev/null +++ b/packages/shadcn-ui/src/components/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@repo/shadcn-ui/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/packages/shadcn-ui/src/components/badge.tsx b/packages/shadcn-ui/src/components/badge.tsx new file mode 100644 index 00000000..cfa636ae --- /dev/null +++ b/packages/shadcn-ui/src/components/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@repo/shadcn-ui/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/packages/shadcn-ui/src/components/breadcrumb.tsx b/packages/shadcn-ui/src/components/breadcrumb.tsx new file mode 100644 index 00000000..686dcea8 --- /dev/null +++ b/packages/shadcn-ui/src/components/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@repo/shadcn-ui/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return