diff --git a/packages/app-vite-react/package.json b/packages/app-vite-react/package.json
index 681dc439..3ec8f0ae 100644
--- a/packages/app-vite-react/package.json
+++ b/packages/app-vite-react/package.json
@@ -11,7 +11,7 @@
"build": "tsc && vite build --emptyOutDir",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"start": "serve -s dist",
- "loadTranslationTypes": "i18next-resources-for-ts interface -i ./src/public/locales/en -o ./src/@types/resources.d.ts"
+ "loadTranslationTypes": "i18next-resources-for-ts interface -i ./public/locales/en -o ./src/lib/i18next/@types/resources.d.ts"
},
"dependencies": {
"@emotion/react": "^11.13.3",
diff --git a/packages/app-vite-react/src/public/locales/en/translation.json b/packages/app-vite-react/public/locales/en/translation.json
similarity index 100%
rename from packages/app-vite-react/src/public/locales/en/translation.json
rename to packages/app-vite-react/public/locales/en/translation.json
diff --git a/packages/app-vite-react/src/public/locales/hr/translation.json b/packages/app-vite-react/public/locales/hr/translation.json
similarity index 100%
rename from packages/app-vite-react/src/public/locales/hr/translation.json
rename to packages/app-vite-react/public/locales/hr/translation.json
diff --git a/packages/app-vite-react/src/public/png/logo.png b/packages/app-vite-react/public/png/logo.png
similarity index 100%
rename from packages/app-vite-react/src/public/png/logo.png
rename to packages/app-vite-react/public/png/logo.png
diff --git a/packages/app-vite-react/src/ReactApp.tsx b/packages/app-vite-react/src/ReactApp.tsx
index 4a0560d5..38de2831 100644
--- a/packages/app-vite-react/src/ReactApp.tsx
+++ b/packages/app-vite-react/src/ReactApp.tsx
@@ -4,18 +4,17 @@ import type { RouteObject } from "react-router-dom";
import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom";
import { CssBaseline } from "@mui/material";
-import type { NavigationRoutes } from "@org/app-vite-react/config/NavigationRoute.config";
+import { type NavigationRoutes } from "@org/app-vite-react/routeTypes";
import { Layout } from "@org/app-vite-react/components/layout/Layout";
-import type { Provider } from "@org/app-vite-react/components/providers/Providers";
-import { Providers } from "@org/app-vite-react/components/providers/Providers";
-import { StylesProvider } from "@org/app-vite-react/components/providers/impl/StylesProvider";
-import { ThemeProvider } from "@org/app-vite-react/components/providers/impl/MuiThemeProvider";
-import { QueryClientProvider } from "@org/app-vite-react/components/providers/impl/QueryClientProvider";
+import { Providers, type Provider } from "@org/app-vite-react/components/providers/Providers";
+import { StylesProvider, ThemeProvider } from "@org/app-vite-react/lib/@mui";
+import { QueryClientProvider } from "@org/app-vite-react/lib/@tanstack";
import { Status404Page } from "@org/app-vite-react/app/pages/Status404";
import { RootErrorPage } from "@org/app-vite-react/app/pages/RootError";
-import { KeycloakAuthProvider } from "./components/providers";
+import { KeycloakProvider } from "@org/app-vite-react/lib/keycloak-js";
+import { initI18n } from "@org/app-vite-react/lib/i18next/i18n";
type ReactAppConfig = {
providers?: Provider[];
@@ -40,7 +39,7 @@ function convertToRoutes(data: NavigationRoutes): RouteObject[] {
export class ReactApp {
static readonly #DEFAULT_ROOT_ERROR_PAGE = ();
static readonly #COMMON_PROVIDERS = [
- KeycloakAuthProvider,
+ KeycloakProvider,
QueryClientProvider,
StylesProvider,
ThemeProvider,
@@ -58,7 +57,7 @@ export class ReactApp {
router!: ReturnType;
constructor() {
- // NOOP
+ initI18n();
}
run(config: ReactAppConfig) {
diff --git a/packages/app-vite-react/src/app/pages/Home/HomePage.tsx b/packages/app-vite-react/src/app/pages/Home/HomePage.tsx
index 92535f9e..15c03eff 100644
--- a/packages/app-vite-react/src/app/pages/Home/HomePage.tsx
+++ b/packages/app-vite-react/src/app/pages/Home/HomePage.tsx
@@ -9,7 +9,7 @@ import { ServerDatatable } from "@org/app-vite-react/components/semantics/Datata
import { DEFAULT_PAGINATION_OPTIONS } from "@org/app-vite-react/components/semantics/Datatable/types";
import { DatatableContainer } from "@org/app-vite-react/components/semantics/Datatable/components/DatatableContainer";
import { FixedBadge } from "@org/app-vite-react/app/pages/Home/FixedBadge";
-import { apiClient } from "@org/app-vite-react/setup/apiClient.setup";
+import { apiClient } from "@org/app-vite-react/lib/@ts-rest";
function buildPaginationQueryParams(paginationOptions: PaginationOptions): {
paginationOptions: string;
diff --git a/packages/app-vite-react/src/app/pages/Home/UserCreateFormButton.tsx b/packages/app-vite-react/src/app/pages/Home/UserCreateFormButton.tsx
index 1ed57c5d..1d280d80 100644
--- a/packages/app-vite-react/src/app/pages/Home/UserCreateFormButton.tsx
+++ b/packages/app-vite-react/src/app/pages/Home/UserCreateFormButton.tsx
@@ -3,7 +3,7 @@ import { useState } from "react";
import type { TODO, User } from "@org/lib-commons";
import { UserForm } from "@org/app-vite-react/app/pages/Home/UserForm";
import { Add } from "@mui/icons-material";
-import { apiClient } from "@org/app-vite-react/setup/apiClient.setup";
+import { apiClient } from "@org/app-vite-react/lib/@ts-rest";
export type UserCreateFormButtonProps = {
afterUpdate?: () => void;
diff --git a/packages/app-vite-react/src/app/routes.tsx b/packages/app-vite-react/src/app/routes.tsx
index a012bee3..d0f84d93 100644
--- a/packages/app-vite-react/src/app/routes.tsx
+++ b/packages/app-vite-react/src/app/routes.tsx
@@ -1,6 +1,6 @@
import * as icons from "@mui/icons-material";
-import type { NavigationRoutes } from "@org/app-vite-react/config/NavigationRoute.config";
+import type { NavigationRoutes } from "@org/app-vite-react/routeTypes";
import { HomePage } from "@org/app-vite-react/app/pages/Home";
export const routes: NavigationRoutes = [
diff --git a/packages/app-vite-react/src/components/inputs/InputLocaleSelect/InputLocaleSelect.tsx b/packages/app-vite-react/src/components/inputs/InputLocaleSelect/InputLocaleSelect.tsx
index aeea7d62..58e8ccfc 100644
--- a/packages/app-vite-react/src/components/inputs/InputLocaleSelect/InputLocaleSelect.tsx
+++ b/packages/app-vite-react/src/components/inputs/InputLocaleSelect/InputLocaleSelect.tsx
@@ -3,7 +3,7 @@ import { Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
import { InputIconButtonSelect } from "@org/app-vite-react/components/inputs/InputIconButtonSelect/InputIconButtonSelect";
import { sigLocale } from "@org/app-vite-react/signals/sigLocale";
-import type { Locale } from "@org/app-vite-react/config/i18n.config";
+import type { Locale } from "@org/app-vite-react/lib/i18next";
function getLocaleNativeName(locale: Locale) {
const name: string = new Intl.DisplayNames([locale], {
diff --git a/packages/app-vite-react/src/components/layout/variants/HorizontalNavVariant.tsx b/packages/app-vite-react/src/components/layout/variants/HorizontalNavVariant.tsx
index 3f82ea65..52b9c078 100644
--- a/packages/app-vite-react/src/components/layout/variants/HorizontalNavVariant.tsx
+++ b/packages/app-vite-react/src/components/layout/variants/HorizontalNavVariant.tsx
@@ -1,29 +1,17 @@
import { ChevronRight, ExpandMore } from "@mui/icons-material";
import type { Breakpoint } from "@mui/material";
-import {
- Box,
- Container,
- List,
- ListItemButton,
- ListItemIcon,
- ListItemText,
- Stack,
-} from "@mui/material";
+import * as mui from "@mui/material";
import type { TODO } from "@org/lib-commons";
import { Fragment } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import type { OriginPosition } from "@org/app-vite-react/components/navigation/ButtonHoverMenu";
import { ButtonHoverMenu } from "@org/app-vite-react/components/navigation/ButtonHoverMenu";
-import type {
- NavigationRoute,
- NavigationRouteSingle,
-} from "@org/app-vite-react/config/NavigationRoute.config";
-import { isAnyRouteActive } from "@org/app-vite-react/config/NavigationRoute.config";
-import { reactServer } from "@org/app-vite-react/setup/reactServer.setup";
+import * as RouteTypes from "@org/app-vite-react/routeTypes";
+import { reactServer } from "@org/app-vite-react/server";
export type HorizontalNavItemProps = {
- item: NavigationRoute;
+ item: RouteTypes.NavigationRoute;
dropdownPosition?: OriginPosition;
};
@@ -39,17 +27,17 @@ function HorizontalNavItem({
const { t } = useTranslation();
const navigate = useNavigate();
const hasChildren = "children" in item && item.children;
- const children: NavigationRoute[] = (hasChildren ? item.children : []) as TODO;
+ const children: RouteTypes.NavigationRoute[] = (hasChildren ? item.children : []) as TODO;
const isMainNavButton = dropdownPosition.anchorY === "bottom";
const borderRadius = isMainNavButton ? 1 : undefined;
if (hasChildren) {
- const isAnyRouteActiveInGroup = isAnyRouteActive(children);
+ const isAnyRouteActiveInGroup = RouteTypes.isAnyRouteActive(children);
return (
(
-
- {item.icon && {item.icon}}
-
-
+ {item.icon && {item.icon}}
+
+
{isMainNavButton ? : }
-
-
+
+
)}
>
{children.map((child, index) => (
@@ -90,7 +78,7 @@ function HorizontalNavItem({
);
}
- const itemSingle = item as NavigationRouteSingle;
+ const itemSingle = item as RouteTypes.NavigationRouteSingle;
if (itemSingle.hidden === true) {
return <>>;
@@ -99,7 +87,7 @@ function HorizontalNavItem({
const isSelected = location.pathname === itemSingle.path;
return (
- navigate(itemSingle.path)}
>
- {item.icon && {item.icon}}
-
-
+ {item.icon && {item.icon}}
+
+
);
}
@@ -127,22 +115,22 @@ export function HorizontalNavVariant({
hidden = false,
}: HorizontalNavVariantProps) {
return (
-
-
-
+
+
{reactServer.routes.map((item, index) => (
))}
-
-
-
+
+
+
);
}
diff --git a/packages/app-vite-react/src/components/layout/variants/SidebarNavVariant.tsx b/packages/app-vite-react/src/components/layout/variants/SidebarNavVariant.tsx
index a10e4490..3bda83f9 100644
--- a/packages/app-vite-react/src/components/layout/variants/SidebarNavVariant.tsx
+++ b/packages/app-vite-react/src/components/layout/variants/SidebarNavVariant.tsx
@@ -5,15 +5,11 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useLocation, useNavigate } from "react-router-dom";
import { Fragment } from "react/jsx-runtime";
-import type {
- NavigationRoute,
- NavigationRouteSingle,
-} from "@org/app-vite-react/config/NavigationRoute.config";
-import { isAnyRouteActive } from "@org/app-vite-react/config/NavigationRoute.config";
-import { reactServer } from "@org/app-vite-react/setup/reactServer.setup";
+import { reactServer } from "@org/app-vite-react/server";
+import * as RouteTypes from "@org/app-vite-react/routeTypes";
export type SidebarNavItemProps = {
- item: NavigationRoute;
+ item: RouteTypes.NavigationRoute;
indent?: number;
};
@@ -22,7 +18,7 @@ function SidebarNavItem({ item, indent = 0 }: SidebarNavItemProps) {
const variant = item?.variant ?? "single";
const renderChildrenPersistent = hasChildren && variant === "group";
const renderChildrenMenu = hasChildren && variant === "menu";
- const children: NavigationRoute[] = (hasChildren ? item.children : []) as TODO;
+ const children: RouteTypes.NavigationRoute[] = (hasChildren ? item.children : []) as TODO;
const [open, setOpen] = useState(false);
const navigate = useNavigate();
const location = useLocation();
@@ -60,7 +56,7 @@ function SidebarNavItem({ item, indent = 0 }: SidebarNavItemProps) {
paddingLeft: `calc(1.5rem + ${indent}rem)`,
borderTopRightRadius: "2rem",
borderBottomRightRadius: "2rem",
- backgroundColor: isAnyRouteActive(children)
+ backgroundColor: RouteTypes.isAnyRouteActive(children)
? "var(--mui-palette-action-hover)"
: undefined,
}}
@@ -79,7 +75,7 @@ function SidebarNavItem({ item, indent = 0 }: SidebarNavItemProps) {
);
}
- const itemSingle = item as NavigationRouteSingle;
+ const itemSingle = item as RouteTypes.NavigationRouteSingle;
if (itemSingle.hidden === true) {
return <>>;
diff --git a/packages/app-vite-react/src/components/providers/Providers.tsx b/packages/app-vite-react/src/components/providers/Providers.tsx
index 4c853d8b..be77c0c6 100644
--- a/packages/app-vite-react/src/components/providers/Providers.tsx
+++ b/packages/app-vite-react/src/components/providers/Providers.tsx
@@ -7,7 +7,7 @@ const nest = (children: React.ReactNode, Provider: React.FC<{ children: React.Re
{children}
);
-// Type for the props of the Providers component
+/** @hidden */
export type ProvidersProps = React.PropsWithChildren<{
list: Array>;
}>;
diff --git a/packages/app-vite-react/src/components/providers/index.ts b/packages/app-vite-react/src/components/providers/index.ts
deleted file mode 100644
index a4b76817..00000000
--- a/packages/app-vite-react/src/components/providers/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from "./impl/MuiThemeProvider";
-export * from "./impl/QueryClientProvider";
-export * from "./impl/StylesProvider";
-export * from "./impl/KeycloakAuthProvider";
-export * from "./Providers";
diff --git a/packages/app-vite-react/src/components/semantics/Header/UserMenuButton.tsx b/packages/app-vite-react/src/components/semantics/Header/UserMenuButton.tsx
index 6edec032..0116311f 100644
--- a/packages/app-vite-react/src/components/semantics/Header/UserMenuButton.tsx
+++ b/packages/app-vite-react/src/components/semantics/Header/UserMenuButton.tsx
@@ -12,7 +12,7 @@ import Settings from "@mui/icons-material/Settings";
import Logout from "@mui/icons-material/Logout";
import { type TODO } from "@org/lib-commons";
import { sigUser } from "@org/app-vite-react/signals/sigUser";
-import { sigKeycloak } from "@org/app-vite-react/signals/sigKeycloak";
+import { keycloakLogout } from "@org/app-vite-react/lib/keycloak-js";
export function UserMenuButton() {
const [anchorEl, setAnchorEl] = React.useState(null);
@@ -24,8 +24,7 @@ export function UserMenuButton() {
setAnchorEl(null);
};
const handleLogout = () => {
- sigKeycloak.value!.logout();
- //handleClose();
+ keycloakLogout();
};
return (
diff --git a/packages/app-vite-react/src/config/UiPreferences.config.ts b/packages/app-vite-react/src/config/UiPreferences.config.ts
deleted file mode 100644
index d7bb3eba..00000000
--- a/packages/app-vite-react/src/config/UiPreferences.config.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { Breakpoint } from "@mui/material";
-
-export type UiPreferences = {
- containerWidth: Breakpoint | false;
-};
diff --git a/packages/app-vite-react/src/config/i18n.config.ts b/packages/app-vite-react/src/config/i18n.config.ts
deleted file mode 100644
index fa904d61..00000000
--- a/packages/app-vite-react/src/config/i18n.config.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { TFunction } from "i18next";
-
-export type I18nTranslateFn = TFunction<"translation", undefined>;
-
-export type Locale = "hr" | "en";
diff --git a/packages/app-vite-react/src/lib/@mui/index.ts b/packages/app-vite-react/src/lib/@mui/index.ts
new file mode 100644
index 00000000..6b8bad0c
--- /dev/null
+++ b/packages/app-vite-react/src/lib/@mui/index.ts
@@ -0,0 +1,2 @@
+export * from "./providers/MuiStylesProvider";
+export * from "./providers/MuiThemeProvider";
diff --git a/packages/app-vite-react/src/components/providers/impl/StylesProvider.tsx b/packages/app-vite-react/src/lib/@mui/providers/MuiStylesProvider.tsx
similarity index 100%
rename from packages/app-vite-react/src/components/providers/impl/StylesProvider.tsx
rename to packages/app-vite-react/src/lib/@mui/providers/MuiStylesProvider.tsx
diff --git a/packages/app-vite-react/src/components/providers/impl/MuiThemeProvider.tsx b/packages/app-vite-react/src/lib/@mui/providers/MuiThemeProvider.tsx
similarity index 100%
rename from packages/app-vite-react/src/components/providers/impl/MuiThemeProvider.tsx
rename to packages/app-vite-react/src/lib/@mui/providers/MuiThemeProvider.tsx
diff --git a/packages/app-vite-react/src/components/providers/impl/QueryClientProvider.tsx b/packages/app-vite-react/src/lib/@tanstack/QueryClientProvider.tsx
similarity index 100%
rename from packages/app-vite-react/src/components/providers/impl/QueryClientProvider.tsx
rename to packages/app-vite-react/src/lib/@tanstack/QueryClientProvider.tsx
diff --git a/packages/app-vite-react/src/lib/@tanstack/index.ts b/packages/app-vite-react/src/lib/@tanstack/index.ts
new file mode 100644
index 00000000..e8807988
--- /dev/null
+++ b/packages/app-vite-react/src/lib/@tanstack/index.ts
@@ -0,0 +1 @@
+export * from "./QueryClientProvider";
diff --git a/packages/app-vite-react/src/setup/apiClient.setup.ts b/packages/app-vite-react/src/lib/@ts-rest/apiClient.ts
similarity index 91%
rename from packages/app-vite-react/src/setup/apiClient.setup.ts
rename to packages/app-vite-react/src/lib/@ts-rest/apiClient.ts
index 939ce7fc..a782be77 100644
--- a/packages/app-vite-react/src/setup/apiClient.setup.ts
+++ b/packages/app-vite-react/src/lib/@ts-rest/apiClient.ts
@@ -1,8 +1,6 @@
import { initClient } from "@ts-rest/core";
import { contracts } from "@org/lib-api-client";
-console.log(import.meta.env);
-
const API_CLIENT_URL = import.meta.env.VITE_API_CLIENT_URL;
if (!API_CLIENT_URL) {
diff --git a/packages/app-vite-react/src/lib/@ts-rest/index.ts b/packages/app-vite-react/src/lib/@ts-rest/index.ts
new file mode 100644
index 00000000..1f8c144e
--- /dev/null
+++ b/packages/app-vite-react/src/lib/@ts-rest/index.ts
@@ -0,0 +1 @@
+export * from "./apiClient";
diff --git a/packages/app-vite-react/src/@types/i18n.d.ts b/packages/app-vite-react/src/lib/i18next/@types/i18n.d.ts
similarity index 100%
rename from packages/app-vite-react/src/@types/i18n.d.ts
rename to packages/app-vite-react/src/lib/i18next/@types/i18n.d.ts
diff --git a/packages/app-vite-react/src/@types/resources.d.ts b/packages/app-vite-react/src/lib/i18next/@types/resources.d.ts
similarity index 100%
rename from packages/app-vite-react/src/@types/resources.d.ts
rename to packages/app-vite-react/src/lib/i18next/@types/resources.d.ts
diff --git a/packages/app-vite-react/src/lib/i18next/i18n.ts b/packages/app-vite-react/src/lib/i18next/i18n.ts
new file mode 100644
index 00000000..57d31330
--- /dev/null
+++ b/packages/app-vite-react/src/lib/i18next/i18n.ts
@@ -0,0 +1,21 @@
+import i18n, { type TFunction } from "i18next";
+import LanguageDetector from "i18next-browser-languagedetector";
+import Backend from "i18next-http-backend";
+import { initReactI18next } from "react-i18next";
+
+export type I18nTranslateFn = TFunction<"translation", undefined>;
+
+export type Locale = "hr" | "en";
+
+export function initI18n() {
+ i18n
+ .use(Backend)
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ fallbackLng: "en",
+ interpolation: {
+ escapeValue: false,
+ },
+ });
+}
diff --git a/packages/app-vite-react/src/lib/i18next/index.ts b/packages/app-vite-react/src/lib/i18next/index.ts
new file mode 100644
index 00000000..0c4205e0
--- /dev/null
+++ b/packages/app-vite-react/src/lib/i18next/index.ts
@@ -0,0 +1 @@
+export * from "./i18n";
diff --git a/packages/app-vite-react/src/setup/keycloakClient.setup.ts b/packages/app-vite-react/src/lib/keycloak-js/KeycloakClient.ts
similarity index 70%
rename from packages/app-vite-react/src/setup/keycloakClient.setup.ts
rename to packages/app-vite-react/src/lib/keycloak-js/KeycloakClient.ts
index f454762c..74869d3e 100644
--- a/packages/app-vite-react/src/setup/keycloakClient.setup.ts
+++ b/packages/app-vite-react/src/lib/keycloak-js/KeycloakClient.ts
@@ -5,3 +5,7 @@ export const keycloakClient = new Keycloak({
url: import.meta.env.VITE_API_KEYCLOAK_URL,
clientId: "app-vite-react",
});
+
+export async function keycloakLogout() {
+ await keycloakClient.logout();
+}
diff --git a/packages/app-vite-react/src/components/providers/impl/KeycloakAuthProvider.tsx b/packages/app-vite-react/src/lib/keycloak-js/KeycloakProvider.tsx
similarity index 61%
rename from packages/app-vite-react/src/components/providers/impl/KeycloakAuthProvider.tsx
rename to packages/app-vite-react/src/lib/keycloak-js/KeycloakProvider.tsx
index e6234e64..6c12b50f 100644
--- a/packages/app-vite-react/src/components/providers/impl/KeycloakAuthProvider.tsx
+++ b/packages/app-vite-react/src/lib/keycloak-js/KeycloakProvider.tsx
@@ -1,11 +1,11 @@
import { StrictMode, type PropsWithChildren } from "react";
import { ReactKeycloakProvider, useKeycloak } from "@react-keycloak/web";
-import { keycloakClient } from "@org/app-vite-react/setup/keycloakClient.setup";
-import { decodeUserData, sigUser } from "@org/app-vite-react/signals/sigUser";
+import { sigUser } from "@org/app-vite-react/signals/sigUser";
import { sigToken } from "@org/app-vite-react/signals/sigToken";
-import { sigKeycloak } from "@org/app-vite-react/signals/sigKeycloak";
+import { decodeKeycloakToken } from "@org/app-vite-react/lib/keycloak-js/KeycloakUser";
+import { keycloakClient } from "@org/app-vite-react/lib/keycloak-js/KeycloakClient";
-const KeycloakAuthContent = ({ children }: PropsWithChildren) => {
+const KeycloakImpl = ({ children }: PropsWithChildren) => {
const { keycloak, initialized } = useKeycloak();
if (!initialized) {
@@ -16,11 +16,10 @@ const KeycloakAuthContent = ({ children }: PropsWithChildren) => {
return Not authenticated
;
}
- sigKeycloak.value = keycloak;
return <>{children}>;
};
-export function KeycloakAuthProvider({ children }: PropsWithChildren) {
+export function KeycloakProvider({ children }: PropsWithChildren) {
return (
-
+
{children}
-
+
);
}
diff --git a/packages/app-vite-react/src/lib/keycloak-js/KeycloakUser.ts b/packages/app-vite-react/src/lib/keycloak-js/KeycloakUser.ts
new file mode 100644
index 00000000..887f2c4b
--- /dev/null
+++ b/packages/app-vite-react/src/lib/keycloak-js/KeycloakUser.ts
@@ -0,0 +1,28 @@
+import { type KeycloakTokenParsed } from "keycloak-js";
+import { jwtDecode } from "jwt-decode";
+
+export interface KeycloakUser {
+ firstName?: string;
+ lastName?: string;
+ userName?: string;
+ token?: string;
+ locale?: string;
+ companyId?: string;
+ roles: string[];
+ email?: string;
+}
+
+export function decodeKeycloakToken(keycloakToken: string): KeycloakUser {
+ const decoded = jwtDecode(keycloakToken);
+ const fromToken: KeycloakUser = {
+ userName: decoded["preferred_username"],
+ email: decoded["email"],
+ firstName: decoded["given_name"],
+ lastName: decoded["family_name"],
+ companyId: decoded["companyId"],
+ locale: decoded["locale"],
+ token: keycloakToken,
+ roles: decoded["realm_access"]?.["roles"] ?? [],
+ };
+ return fromToken;
+}
diff --git a/packages/app-vite-react/src/lib/keycloak-js/index.ts b/packages/app-vite-react/src/lib/keycloak-js/index.ts
new file mode 100644
index 00000000..e20656d3
--- /dev/null
+++ b/packages/app-vite-react/src/lib/keycloak-js/index.ts
@@ -0,0 +1,3 @@
+export * from "@org/app-vite-react/lib/keycloak-js/KeycloakClient";
+export * from "@org/app-vite-react/lib/keycloak-js/KeycloakProvider";
+export * from "@org/app-vite-react/lib/keycloak-js/KeycloakUser";
diff --git a/packages/app-vite-react/src/main.tsx b/packages/app-vite-react/src/main.tsx
index 8e63d324..88e8c58c 100644
--- a/packages/app-vite-react/src/main.tsx
+++ b/packages/app-vite-react/src/main.tsx
@@ -1,7 +1,6 @@
import "@org/app-vite-react/setup/i18n.setup";
import "@org/app-vite-react/main.css";
-import { reactServer } from "@org/app-vite-react/setup/reactServer.setup";
-
+import { reactServer } from "@org/app-vite-react/server";
import { routes } from "@org/app-vite-react/app/routes";
reactServer.run({
diff --git a/packages/app-vite-react/src/config/NavigationRoute.config.ts b/packages/app-vite-react/src/routeTypes.ts
similarity index 93%
rename from packages/app-vite-react/src/config/NavigationRoute.config.ts
rename to packages/app-vite-react/src/routeTypes.ts
index 9e6c3ce9..414fc6c7 100644
--- a/packages/app-vite-react/src/config/NavigationRoute.config.ts
+++ b/packages/app-vite-react/src/routeTypes.ts
@@ -1,7 +1,7 @@
import type { Role, User } from "@org/lib-commons";
import type { ReactNode } from "react";
import type { RouteObject } from "react-router-dom";
-import { type I18nTranslateFn } from "@org/app-vite-react/config/i18n.config";
+import { type I18nTranslateFn } from "@org/app-vite-react/lib/i18next";
export type NavigationRouteAnchorSecure = Role[] | ((user: User) => boolean | Role[]);
diff --git a/packages/app-vite-react/src/setup/reactServer.setup.ts b/packages/app-vite-react/src/server.ts
similarity index 100%
rename from packages/app-vite-react/src/setup/reactServer.setup.ts
rename to packages/app-vite-react/src/server.ts
diff --git a/packages/app-vite-react/src/setup/i18n.setup.ts b/packages/app-vite-react/src/setup/i18n.setup.ts
deleted file mode 100644
index 744078c4..00000000
--- a/packages/app-vite-react/src/setup/i18n.setup.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import i18nCore from "i18next";
-import LanguageDetector from "i18next-browser-languagedetector";
-import Backend from "i18next-http-backend";
-import { initReactI18next } from "react-i18next";
-
-i18nCore
- .use(Backend)
- .use(LanguageDetector)
- .use(initReactI18next)
- .init({
- fallbackLng: "en",
- interpolation: {
- escapeValue: false,
- },
- });
-
-export const i18n = i18nCore;
diff --git a/packages/app-vite-react/src/signals/sigKeycloak.ts b/packages/app-vite-react/src/signals/sigKeycloak.ts
deleted file mode 100644
index c554311e..00000000
--- a/packages/app-vite-react/src/signals/sigKeycloak.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { signal } from "@preact/signals-react";
-import type Keycloak from "keycloak-js";
-
-export const sigKeycloak = signal(null);
diff --git a/packages/app-vite-react/src/signals/sigLocale.ts b/packages/app-vite-react/src/signals/sigLocale.ts
index b218aec9..9f4930be 100644
--- a/packages/app-vite-react/src/signals/sigLocale.ts
+++ b/packages/app-vite-react/src/signals/sigLocale.ts
@@ -1,6 +1,6 @@
import { effect, signal } from "@preact/signals-react";
-import { i18n } from "@org/app-vite-react/setup/i18n.setup";
-import { type Locale } from "@org/app-vite-react/config/i18n.config";
+import { type Locale } from "@org/app-vite-react/lib/i18next";
+import i18n from "i18next";
export const sigLocale = signal(
(["en", "hr"].find(value => value === localStorage.getItem("locale")) ?? "en") as Locale,
diff --git a/packages/app-vite-react/src/signals/sigPreferences.ts b/packages/app-vite-react/src/signals/sigPreferences.ts
index 2bad50f9..7a5bea00 100644
--- a/packages/app-vite-react/src/signals/sigPreferences.ts
+++ b/packages/app-vite-react/src/signals/sigPreferences.ts
@@ -1,7 +1,11 @@
import { effect, signal } from "@preact/signals-react";
-import { type UiPreferences } from "@org/app-vite-react/config/UiPreferences.config";
+import type { Breakpoint } from "@mui/material";
-const DEFAULT_PREFERENCES: UiPreferences = {
+export type GuiPreferences = {
+ containerWidth: Breakpoint | false;
+};
+
+const DEFAULT_PREFERENCES: GuiPreferences = {
containerWidth: "xl",
};
@@ -22,7 +26,7 @@ function getJsonItem>>(
return { ...defaults, ...json } as T;
}
-export const sigPreferences = signal(
+export const sigPreferences = signal(
getJsonItem(PREFERENCES_KEY, DEFAULT_PREFERENCES),
);
diff --git a/packages/app-vite-react/src/signals/sigTheme.ts b/packages/app-vite-react/src/signals/sigTheme.ts
index 7cc3d165..40725a58 100644
--- a/packages/app-vite-react/src/signals/sigTheme.ts
+++ b/packages/app-vite-react/src/signals/sigTheme.ts
@@ -1,18 +1,51 @@
import { createTheme } from "@mui/material";
import { computed, signal } from "@preact/signals-react";
-const userPrefersDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
-
type ThemeOptsInternal = Parameters[0];
type ThemeOptsBase = Omit;
+/** @hidden */
export type ThemeOpts = ThemeOptsBase & {
dark: boolean;
};
-export const sigThemeOpts = signal({ dark: userPrefersDarkMode });
+/**
+ * A signal for MUI's theme options.
+ *
+ * @remarks Components or hooks subscribing to this signal will automatically re-render or react when its value changes.
+ *
+ * @see {@link https://mui.com/material-ui/customization/default-theme/ | MUI's default theme options}
+ *
+ * @default
+ * ```ts
+ * { dark: window.matchMedia("(prefers-color-scheme: dark)").matches }
+ * ```
+ *
+ * @example
+ * ```ts
+ * import { sigThemeOpts } from './path-to-sigThemeOpts';
+ * const currentValue = sigThemeOpts.value // Read
+ * sigThemeOpts.value = { dark: false } // Write
+ * ```
+ */
+export const sigThemeOpts = signal({
+ dark: window.matchMedia("(prefers-color-scheme: dark)").matches,
+});
+/**
+ * A readonly signal for MUI's theme (computed from {@link sigThemeOpts}).
+ *
+ * @remarks Components or hooks subscribing to this signal will automatically re-render or react when its value changes.
+ *
+ * @see {@link https://mui.com/material-ui/customization/theming/#createtheme-options-args-theme | MUI's createTheme API Documentation}
+ *
+ * @example
+ * ```ts
+ * import { sigTheme } from './path-to-sigTheme';
+ * const currentValue = sigTheme.value // Read
+ * ```
+ */
export const sigTheme = computed(() => {
const { dark, ...rest } = sigThemeOpts.value;
diff --git a/packages/app-vite-react/src/signals/sigUser.ts b/packages/app-vite-react/src/signals/sigUser.ts
index c5ea71dc..ad002392 100644
--- a/packages/app-vite-react/src/signals/sigUser.ts
+++ b/packages/app-vite-react/src/signals/sigUser.ts
@@ -1,35 +1,4 @@
import { signal } from "@preact/signals-react";
-import { type KeycloakTokenParsed, type KeycloakRoles } from "keycloak-js";
-import { jwtDecode } from "jwt-decode";
+import { type KeycloakUser } from "@org/app-vite-react/lib/keycloak-js";
-export interface KeycloakResourceAccess {
- [key: string]: KeycloakRoles;
-}
-
-export function decodeUserData(token: string): UserData {
- const decoded = jwtDecode(token);
- const fromToken: UserData = {
- userName: decoded["preferred_username"],
- email: decoded["email"],
- firstName: decoded["given_name"],
- lastName: decoded["family_name"],
- companyId: decoded["companyId"],
- locale: decoded["locale"],
- token: token,
- roles: decoded["realm_access"]?.["roles"] ?? [],
- };
- return fromToken;
-}
-
-export interface UserData {
- firstName?: string;
- lastName?: string;
- userName?: string;
- token?: string;
- locale?: string;
- companyId?: string;
- roles: string[];
- email?: string;
-}
-
-export const sigUser = signal(null);
+export const sigUser = signal(null);