Added multi language support and started refactoring components to be functions instead of classes.
This commit is contained in:
parent
6e3ec1c243
commit
b559d9260c
8
i18n-unused.config.js
Normal file
8
i18n-unused.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
srcPaths: ["src/**/*.{ts,tsx}", "!src/components/common/ckeditor/**"],
|
||||
localesPath: "public/locales",
|
||||
defaultNamespace: "common",
|
||||
|
||||
// Match ANY t("...") call, anywhere in TS/TSX/JSX
|
||||
translationKeyMatcher: "t\\([\"']([^\"']+)[\"']\\)",
|
||||
};
|
||||
15
i18next-parser.config.js
Normal file
15
i18next-parser.config.js
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
locales: ["en"],
|
||||
namespaceSeparator: false,
|
||||
keySeparator: false,
|
||||
defaultNamespace: "common",
|
||||
output: "public/locales/$LOCALE/$NAMESPACE.json",
|
||||
createOldCatalogs: false,
|
||||
keepRemoved: false,
|
||||
lexers: {
|
||||
ts: ["JsxLexer"],
|
||||
tsx: ["JsxLexer"],
|
||||
js: ["JsxLexer"],
|
||||
jsx: ["JsxLexer"],
|
||||
},
|
||||
};
|
||||
15
package.json
15
package.json
@ -31,6 +31,8 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"date-fns": "^2.30.0",
|
||||
"html-react-parser": "^3.0.16",
|
||||
"i18next": "^22.5.1",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"joi": "^17.9.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^3.1.2",
|
||||
@ -40,13 +42,13 @@
|
||||
"react-bootstrap": "^2.7.4",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react-i18next": "^12.3.1",
|
||||
"react-router-dom": "^6.10.0",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-toastify": "^9.1.2",
|
||||
"react-toggle": "^4.1.3",
|
||||
"runtime-env-cra": "^0.2.4",
|
||||
"sass": "^1.62.0",
|
||||
"typescript": "^4.7.4",
|
||||
"web-vitals": "^3.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
@ -56,7 +58,11 @@
|
||||
"start": "concurrently \"npm run start-react\" \"npm run watch-css\" ",
|
||||
"build": "react-app-rewired build",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "react-scripts eject"
|
||||
"eject": "react-scripts eject",
|
||||
"i18n:extract": "i18next \"src/**/*.{ts,tsx,js,jsx}\" \"!src/components/common/ckeditor/**\" --config i18next-parser.config.js",
|
||||
"i18n:unused": "i18n-unused display-unused",
|
||||
"i18n:missed": "i18n-unused display-missed",
|
||||
"i18n:check": "npm run i18n:extract && npm run i18n:unused"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
@ -79,6 +85,9 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.2.3",
|
||||
"@types/react-toggle": "^4.0.3",
|
||||
"react-app-rewired": "^2.2.1"
|
||||
"i18n-unused": "^0.19.0",
|
||||
"i18next-parser": "^9.3.0",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
28
public/locales/en/common.json
Normal file
28
public/locales/en/common.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"Admin": "Admin",
|
||||
"AuditLog": "Audit Logs",
|
||||
"AuditLogs": "Audit Logs",
|
||||
"BlockedIPAddresses": "Blocked IP addresses",
|
||||
"BlockedIPs": "Blocked IPs",
|
||||
"ClientDomainManager": "Client Domain Manager",
|
||||
"ClientDomains": "Client Domains",
|
||||
"CustomFieldManager": "Custom Field Manager",
|
||||
"CustomFields": "Custom Fields",
|
||||
"e-print": "e-print",
|
||||
"e-suite": "e-suite",
|
||||
"ErrorLogs": "Error Logs",
|
||||
"ExceptionLogs": "Exception Logs",
|
||||
"Forms": "Forms",
|
||||
"FormTemplateManager": "Form Template Manager",
|
||||
"Glossaries": "Glossaries",
|
||||
"GlossaryManager": "Glossary Manager",
|
||||
"Home": "Home",
|
||||
"Sequence": "Sequence",
|
||||
"SequenceManager": "Sequence Manager",
|
||||
"SiteManager": "Site Manager",
|
||||
"SpecificationManager": "Specification Manager",
|
||||
"SsoManager": "Sso Manager",
|
||||
"Support": "Support",
|
||||
"UserManager": "User Manager",
|
||||
"Users": "Users"
|
||||
}
|
||||
449
src/App.tsx
449
src/App.tsx
@ -2,6 +2,7 @@ import React, { useEffect } from "react";
|
||||
import { Routes, Route, Navigate, useNavigate } from "react-router-dom";
|
||||
import { Helmet, HelmetProvider, HtmlProps } from "react-helmet-async";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import config from "./config.json";
|
||||
import authentication from "./modules/frame/services/authenticationService";
|
||||
import ForgotPassword from "./modules/frame/components/ForgotPassword";
|
||||
@ -42,69 +43,363 @@ import BlockedIPs from "./modules/blockedIPs/blockedIPs";
|
||||
import ErrorLogs from "./modules/errorLogs/errorLogs";
|
||||
import SsoManager from "./modules/manager/ssoManager/ssoManager";
|
||||
import SsoProviderDetails from "./modules/manager/ssoManager/SsoProviderDetails";
|
||||
import { Namespaces } from "./i18n";
|
||||
|
||||
function GetSecureRoutes() {
|
||||
const profileRoute = window.__RUNTIME_CONFIG__.EXTERNAL_LOGIN ? <Route path="/profile" element={<Redirect to="/account/profile"/>}/>
|
||||
: <Route path="/profile" element={<Mainframe><Profile /></Mainframe>}/>;
|
||||
const { t } = useTranslation<typeof Namespaces.Common>();
|
||||
const profileRoute = window.__RUNTIME_CONFIG__.EXTERNAL_LOGIN ? (
|
||||
<Route path="/profile" element={<Redirect to="/account/profile" />} />
|
||||
) : (
|
||||
<Route
|
||||
path="/profile"
|
||||
element={
|
||||
<Mainframe>
|
||||
<Profile />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Route path="/audit/:auditId" element={<Mainframe title="Audit logs"><HOCAudit /></Mainframe>}/>
|
||||
<Route path="/audit" element={<Mainframe title="Audit logs"><HOCAudit /></Mainframe>} />
|
||||
<Route path="/blockedIPs" element={<Mainframe title="Blocked IP addresses"><BlockedIPs /></Mainframe>} />
|
||||
<Route path="/exceptionlogs" element={<Mainframe title="Exception Logs"><ErrorLogs /></Mainframe>} />
|
||||
<Route
|
||||
path="/audit/:auditId"
|
||||
element={
|
||||
<Mainframe title={t("AuditLogs")}>
|
||||
<HOCAudit />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/audit"
|
||||
element={
|
||||
<Mainframe title={t("AuditLogs")}>
|
||||
<HOCAudit />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/blockedIPs"
|
||||
element={
|
||||
<Mainframe title={t("BlockedIPAddresses")}>
|
||||
<BlockedIPs />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/exceptionlogs"
|
||||
element={
|
||||
<Mainframe title={t("ExceptionLogs")}>
|
||||
<ErrorLogs />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/specifications/:organisationId/:siteId/add" element={<Mainframe title="Specification Manager"><SpecificationsDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/specifications/:organisationId/:siteId/:specificationId" element={<Mainframe title="Specification Manager"><SpecificationsDetails editMode={true}/></Mainframe>}/>
|
||||
<Route path="/specifications/:organisationId/:siteId" element={<Mainframe title="Specification Manager"><Specifications /></Mainframe>}/>
|
||||
<Route
|
||||
path="/specifications/:organisationId/:siteId/add"
|
||||
element={
|
||||
<Mainframe title={t("SpecificationManager")}>
|
||||
<SpecificationsDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/specifications/:organisationId/:siteId/:specificationId"
|
||||
element={
|
||||
<Mainframe title={t("SpecificationManager")}>
|
||||
<SpecificationsDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/specifications/:organisationId/:siteId"
|
||||
element={
|
||||
<Mainframe title={t("SpecificationManager")}>
|
||||
<Specifications />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/site/:organisationId/add" element={<Mainframe title="Site Manager"><SiteDetails editMode={false} /></Mainframe>}/>
|
||||
<Route path="/site/:organisationId/:siteId" element={<Mainframe title="Site Manager"><SiteDetails editMode={true} /></Mainframe>}/>
|
||||
<Route path="/site/:organisationId" element={<Mainframe title="Site Manager"><Sites/></Mainframe>}/>
|
||||
<Route
|
||||
path="/site/:organisationId/add"
|
||||
element={
|
||||
<Mainframe title={t("SiteManager")}>
|
||||
<SiteDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/site/:organisationId/:siteId"
|
||||
element={
|
||||
<Mainframe title={t("SiteManager")}>
|
||||
<SiteDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/site/:organisationId"
|
||||
element={
|
||||
<Mainframe title={t("SiteManager")}>
|
||||
<Sites />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route path="/site/" element={<Navigate replace to="/404" />} />
|
||||
|
||||
<Route path="/organisations" element={<Mainframe title="e-print"><Organisations /></Mainframe>}/>
|
||||
<Route path="/organisations/add" element={<Mainframe title="e-print"><HOCOrganisationsDetails editMode={false} /></Mainframe>}/>
|
||||
<Route path="/organisations/:organisationId" element={<Mainframe title="e-print"><HOCOrganisationsDetails editMode={true}/></Mainframe>}/>
|
||||
<Route
|
||||
path="/organisations"
|
||||
element={
|
||||
<Mainframe title={t("e-print")}>
|
||||
<Organisations />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/organisations/add"
|
||||
element={
|
||||
<Mainframe title={t("e-print")}>
|
||||
<HOCOrganisationsDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/organisations/:organisationId"
|
||||
element={
|
||||
<Mainframe title={t("e-print")}>
|
||||
<HOCOrganisationsDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/glossaries/add/" element={<Navigate replace to="/404" />} />
|
||||
<Route path="/glossaries/add/:glossaryId" element={<Mainframe title="Glossary Manager"><HOCGlossariesDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/glossaries/edit/" element={<Navigate replace to="/404" />}/>
|
||||
<Route path="/glossaries/edit/:glossaryId" element={<Mainframe title="Glossary Manager"><HOCGlossariesDetails editMode={true}/></Mainframe>}/>
|
||||
<Route path="/glossaries" element={<Mainframe title="Glossary Manager"><HOCGlossaries /></Mainframe>}/>
|
||||
<Route path="/glossaries/:glossaryId" element={<Mainframe title="Glossary Manager"><HOCGlossaries /></Mainframe>}/>
|
||||
<Route
|
||||
path="/glossaries/add/:glossaryId"
|
||||
element={
|
||||
<Mainframe title={t("GlossaryManager")}>
|
||||
<HOCGlossariesDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/glossaries/edit/"
|
||||
element={<Navigate replace to="/404" />}
|
||||
/>
|
||||
<Route
|
||||
path="/glossaries/edit/:glossaryId"
|
||||
element={
|
||||
<Mainframe title={t("GlossaryManager")}>
|
||||
<HOCGlossariesDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/glossaries"
|
||||
element={
|
||||
<Mainframe title={t("GlossaryManager")}>
|
||||
<HOCGlossaries />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/glossaries/:glossaryId"
|
||||
element={
|
||||
<Mainframe title={t("GlossaryManager")}>
|
||||
<HOCGlossaries />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/forms/add" element={<Mainframe title="Form Template Manager"><HOCFormsDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/forms/edit/:formId" element={<Mainframe title="Form Template Manager"><HOCFormsDetails editMode={true} /></Mainframe>}/>
|
||||
<Route path="/forms" element={<Mainframe title="Form Template Manager"><Forms /></Mainframe>}/>
|
||||
<Route
|
||||
path="/forms/add"
|
||||
element={
|
||||
<Mainframe title={t("FormTemplateManager")}>
|
||||
<HOCFormsDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/forms/edit/:formId"
|
||||
element={
|
||||
<Mainframe title={t("FormTemplateManager")}>
|
||||
<HOCFormsDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/forms"
|
||||
element={
|
||||
<Mainframe title={t("FormTemplateManager")}>
|
||||
<Forms />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/customfields/add" element={<Mainframe title="Custom Field Manager"><HOCCustomFieldDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/customfields/edit/:customFieldId" element={<Mainframe title="Custom Field Manager"><HOCCustomFieldDetails editMode={true}/></Mainframe>}/>
|
||||
<Route path="/customfields" element={<Mainframe title="Custom Field Manager"><CustomFields /></Mainframe>}/>
|
||||
<Route
|
||||
path="/customfields/add"
|
||||
element={
|
||||
<Mainframe title={t("CustomFieldManager")}>
|
||||
<HOCCustomFieldDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/customfields/edit/:customFieldId"
|
||||
element={
|
||||
<Mainframe title={t("CustomFieldManager")}>
|
||||
<HOCCustomFieldDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/customfields"
|
||||
element={
|
||||
<Mainframe title={t("CustomFieldManager")}>
|
||||
<CustomFields />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/sequence/add" element={<Mainframe title="Sequence Manager"><HOCSequenceDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/sequence/edit/:sequenceId" element={<Mainframe title="Sequence Manager"><HOCSequenceDetails editMode={true}/></Mainframe>}/>
|
||||
<Route path="/sequence" element={<Mainframe title="Sequence Manager"><Sequence /></Mainframe>}/>
|
||||
<Route
|
||||
path="/sequence/add"
|
||||
element={
|
||||
<Mainframe title={t("SequenceManager")}>
|
||||
<HOCSequenceDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/sequence/edit/:sequenceId"
|
||||
element={
|
||||
<Mainframe title={t("SequenceManager")}>
|
||||
<HOCSequenceDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/sequence"
|
||||
element={
|
||||
<Mainframe title={t("SequenceManager")}>
|
||||
<Sequence />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/domains/add" element={<Mainframe title="Client Domain Manager"><DomainsDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/domains/edit/:domainId/addRole" element={<Mainframe title="Client Domain Manager"><RolesDetails editMode={false} /></Mainframe>}/>
|
||||
<Route path="/domains/edit/:domainId/editRole/:roleId" element={<Mainframe title="Client Domain Manager"><RolesDetails editMode={true} /></Mainframe>}/>
|
||||
<Route path="/domains/edit/:domainId/editRole/:roleId/addUserToRole" element={<Mainframe title="Client Domain Manager"><AddUserToRole editMode={false} /></Mainframe>}/>
|
||||
<Route path="/domains/edit/:domainId" element={<Mainframe title="Client Domain Manager"><DomainsDetails editMode={true} /></Mainframe>}/>
|
||||
<Route path="/domains" element={<Mainframe title="Client Domain Manager"><Domains /></Mainframe>}/>
|
||||
<Route
|
||||
path="/domains/add"
|
||||
element={
|
||||
<Mainframe title={t("ClientDomainManager")}>
|
||||
<DomainsDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/domains/edit/:domainId/addRole"
|
||||
element={
|
||||
<Mainframe title={t("ClientDomainManager")}>
|
||||
<RolesDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/domains/edit/:domainId/editRole/:roleId"
|
||||
element={
|
||||
<Mainframe title={t("ClientDomainManager")}>
|
||||
<RolesDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/domains/edit/:domainId/editRole/:roleId/addUserToRole"
|
||||
element={
|
||||
<Mainframe title={t("ClientDomainManager")}>
|
||||
<AddUserToRole editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/domains/edit/:domainId"
|
||||
element={
|
||||
<Mainframe title={t("ClientDomainManager")}>
|
||||
<DomainsDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/domains"
|
||||
element={
|
||||
<Mainframe title={t("ClientDomainManager")}>
|
||||
<Domains />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/users/add" element={<Mainframe title="User Manager"><UserDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/users/edit/:userId" element={<Mainframe title="User Manager"><UserDetails editMode={true} /></Mainframe>}/>
|
||||
<Route path="/users" element={<Mainframe title="User Manager"><Users /></Mainframe>}/>
|
||||
|
||||
<Route path="/ssoManager" element={<Mainframe title="Sso Manager"><SsoManager /></Mainframe>}/>
|
||||
<Route path="/ssoManager/add" element={<Mainframe title="Sso Manager"><SsoProviderDetails editMode={false}/></Mainframe>}/>
|
||||
<Route path="/ssoManager/edit/:ssoProviderId" element={<Mainframe title="Sso Manager"><SsoProviderDetails editMode={true} /></Mainframe>}/>
|
||||
<Route
|
||||
path="/users/add"
|
||||
element={
|
||||
<Mainframe title={t("UserManager")}>
|
||||
<UserDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/users/edit/:userId"
|
||||
element={
|
||||
<Mainframe title={t("UserManager")}>
|
||||
<UserDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/users"
|
||||
element={
|
||||
<Mainframe title={t("UserManager")}>
|
||||
<Users />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/ssoManager"
|
||||
element={
|
||||
<Mainframe title={t("SsoManager")}>
|
||||
<SsoManager />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/ssoManager/add"
|
||||
element={
|
||||
<Mainframe title={t("SsoManager")}>
|
||||
<SsoProviderDetails editMode={false} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/ssoManager/edit/:ssoProviderId"
|
||||
element={
|
||||
<Mainframe title={t("SsoManager")}>
|
||||
<SsoProviderDetails editMode={true} />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
|
||||
{profileRoute}
|
||||
<Route path="/logout" element={<Mainframe><Logout /></Mainframe>}/>
|
||||
<Route path="/" element={<Mainframe title="e-suite"><HomePage /></Mainframe>}/>
|
||||
<Route
|
||||
path="/logout"
|
||||
element={
|
||||
<Mainframe>
|
||||
<Logout />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<Mainframe title={t("e-suite")}>
|
||||
<HomePage />
|
||||
</Mainframe>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -120,11 +415,10 @@ function App() {
|
||||
|
||||
if (authentication.tokenExpired()) {
|
||||
navigate("/login");
|
||||
authentication.logout()
|
||||
authentication.logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: any) {
|
||||
} catch (e: any) {
|
||||
console.log(e);
|
||||
}
|
||||
}, 10 * 1000);
|
||||
@ -133,18 +427,34 @@ function App() {
|
||||
|
||||
const isSignedIn = authentication.getCurrentUser() != null;
|
||||
|
||||
const secureRoutes = isSignedIn ? GetSecureRoutes() : <Route path="/" element={<Navigate to="/login" />} />;
|
||||
const secureRoutes = isSignedIn ? (
|
||||
GetSecureRoutes()
|
||||
) : (
|
||||
<Route path="/" element={<Navigate to="/login" />} />
|
||||
);
|
||||
|
||||
var htmlAttributes : HtmlProps = {
|
||||
'data-bs-theme' : theme.getPreferredTheme()
|
||||
}
|
||||
var htmlAttributes: HtmlProps = {
|
||||
"data-bs-theme": theme.getPreferredTheme(),
|
||||
};
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", () => {
|
||||
window.location.reload();
|
||||
})
|
||||
});
|
||||
|
||||
const loginRoute = window.__RUNTIME_CONFIG__.EXTERNAL_LOGIN ? <Route path="/login" element={<Redirect to="/account/login"/>}/>
|
||||
: <Route path="/login" element={<LoginFrame><LoginForm /></LoginFrame>} />;
|
||||
const loginRoute = window.__RUNTIME_CONFIG__.EXTERNAL_LOGIN ? (
|
||||
<Route path="/login" element={<Redirect to="/account/login" />} />
|
||||
) : (
|
||||
<Route
|
||||
path="/login"
|
||||
element={
|
||||
<LoginFrame>
|
||||
<LoginForm />
|
||||
</LoginFrame>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<HelmetProvider>
|
||||
@ -155,11 +465,32 @@ function App() {
|
||||
<Routes>
|
||||
<Route path="/env" element={<EnvPage />} />
|
||||
{loginRoute}
|
||||
<Route path="/forgot-password" element={<LoginFrame><ForgotPassword /></LoginFrame>} />
|
||||
<Route path="/404" element={<LoginFrame><NotFound /></LoginFrame>} />
|
||||
<Route path="/emailuseraction/:token" element={<LoginFrame><EmailUserAction /></LoginFrame>} />
|
||||
<Route
|
||||
path="/forgot-password"
|
||||
element={
|
||||
<LoginFrame>
|
||||
<ForgotPassword />
|
||||
</LoginFrame>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/404"
|
||||
element={
|
||||
<LoginFrame>
|
||||
<NotFound />
|
||||
</LoginFrame>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/emailuseraction/:token"
|
||||
element={
|
||||
<LoginFrame>
|
||||
<EmailUserAction />
|
||||
</LoginFrame>
|
||||
}
|
||||
/>
|
||||
{secureRoutes}
|
||||
<Route path="*" element={<Navigate replace to="/404"/>} />
|
||||
<Route path="*" element={<Navigate replace to="/404" />} />
|
||||
</Routes>
|
||||
<ToastContainer />
|
||||
</main>
|
||||
|
||||
@ -12,46 +12,53 @@ interface AutocompleteState {
|
||||
filteredOptions: Option[];
|
||||
}
|
||||
|
||||
export default class Autocomplete extends React.PureComponent<AutocompleteProps, AutocompleteState> {
|
||||
export default class Autocomplete extends React.PureComponent<
|
||||
AutocompleteProps,
|
||||
AutocompleteState
|
||||
> {
|
||||
private inputRef;
|
||||
constructor(props: AutocompleteProps) {
|
||||
super(props);
|
||||
this.state = { filteredOptions: [] }
|
||||
this.state = { filteredOptions: [] };
|
||||
this.inputRef = React.createRef<HTMLDivElement>();
|
||||
}
|
||||
|
||||
|
||||
private filterOptions(filterTerm: string) {
|
||||
if (filterTerm !== "") {
|
||||
let filtered = this.props.options?.filter(x => x.name.toLowerCase().includes(filterTerm.toLowerCase())) ?? [];
|
||||
filtered = filtered.filter(x => !this.props.selectedOptions?.some(y => x._id === y._id));
|
||||
let filtered =
|
||||
this.props.options?.filter((x) =>
|
||||
x.name.toLowerCase().includes(filterTerm.toLowerCase()),
|
||||
) ?? [];
|
||||
filtered = filtered.filter(
|
||||
(x) => !this.props.selectedOptions?.some((y) => x._id === y._id),
|
||||
);
|
||||
this.setState({
|
||||
filteredOptions: filtered
|
||||
filteredOptions: filtered,
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.setState({
|
||||
filteredOptions: []
|
||||
filteredOptions: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private showOptions() {
|
||||
let filtered = this.props.options?.filter(x => x.name) ?? [];
|
||||
filtered = filtered.filter(x => !this.props.selectedOptions?.some(y => x._id === y._id));
|
||||
let filtered = this.props.options?.filter((x) => x.name) ?? [];
|
||||
filtered = filtered.filter(
|
||||
(x) => !this.props.selectedOptions?.some((y) => x._id === y._id),
|
||||
);
|
||||
this.setState({
|
||||
filteredOptions: filtered
|
||||
filteredOptions: filtered,
|
||||
});
|
||||
}
|
||||
|
||||
private hideAutocomplete = (event: any) => {
|
||||
if (event.target.classList.contains('autocomplete-text-input'))
|
||||
return;
|
||||
if (event.target.classList.contains("autocomplete-text-input")) return;
|
||||
|
||||
this.setState({
|
||||
filteredOptions: []
|
||||
filteredOptions: [],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private handleBlur = (event: React.FocusEvent) => {
|
||||
setTimeout(() => {
|
||||
@ -67,11 +74,11 @@ export default class Autocomplete extends React.PureComponent<AutocompleteProps,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('click', this.hideAutocomplete);
|
||||
document.addEventListener("click", this.hideAutocomplete);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.hideAutocomplete);
|
||||
document.removeEventListener("click", this.hideAutocomplete);
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -79,7 +86,8 @@ export default class Autocomplete extends React.PureComponent<AutocompleteProps,
|
||||
const { filteredOptions } = this.state;
|
||||
|
||||
return (
|
||||
<div className="autocomplete"
|
||||
<div
|
||||
className="autocomplete"
|
||||
ref={this.inputRef}
|
||||
onBlur={this.handleBlur}
|
||||
tabIndex={-1}
|
||||
@ -87,32 +95,32 @@ export default class Autocomplete extends React.PureComponent<AutocompleteProps,
|
||||
<input
|
||||
className="autocomplete-text-input"
|
||||
type="text"
|
||||
onChange={(e) => { this.filterOptions(e.target.value) }}
|
||||
onFocus={(e) => { this.showOptions() }}
|
||||
onChange={(e) => {
|
||||
this.filterOptions(e.target.value);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
this.showOptions();
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
{filteredOptions.length > 0 && (
|
||||
<ul className="autocomplete-options">
|
||||
{filteredOptions.map((x, i) =>
|
||||
<li
|
||||
key={x._id}
|
||||
value={x._id}
|
||||
tabIndex={0}
|
||||
>
|
||||
{filteredOptions.map((x, i) => (
|
||||
<li key={x._id} value={x._id} tabIndex={0}>
|
||||
<button
|
||||
className="autocomplete-option text-left"
|
||||
onClick={(e) => {
|
||||
onSelect(x);
|
||||
this.setState({ filteredOptions: [] })
|
||||
this.setState({ filteredOptions: [] });
|
||||
}}
|
||||
>
|
||||
{x.name}
|
||||
</button>
|
||||
</li>
|
||||
)}
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import react, { SyntheticEvent } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import React, { SyntheticEvent } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export enum ButtonType{
|
||||
export enum ButtonType {
|
||||
none,
|
||||
primary,
|
||||
secondary,
|
||||
@ -11,40 +11,44 @@ export enum ButtonType{
|
||||
info,
|
||||
light,
|
||||
dark,
|
||||
link
|
||||
link,
|
||||
}
|
||||
|
||||
export interface ButtonProps<T>{
|
||||
export interface ButtonProps<T> {
|
||||
testid?: string;
|
||||
className?: string;
|
||||
id?:string;
|
||||
name?:string;
|
||||
id?: string;
|
||||
name?: string;
|
||||
keyValue?: T;
|
||||
children: React.ReactNode;
|
||||
buttonType : ButtonType;
|
||||
buttonType: ButtonType;
|
||||
disabled?: boolean;
|
||||
to?: string;
|
||||
onClick?: ( keyValue : T | undefined ) => void;
|
||||
onClick?: (keyValue: T | undefined) => void;
|
||||
}
|
||||
|
||||
class Button<T> extends react.Component<ButtonProps<T>> {
|
||||
Click = (e : SyntheticEvent) => {
|
||||
const {keyValue, onClick} = this.props;
|
||||
|
||||
if (onClick)
|
||||
{
|
||||
function Button<T>({
|
||||
testid,
|
||||
className,
|
||||
id,
|
||||
name,
|
||||
keyValue,
|
||||
children,
|
||||
buttonType,
|
||||
disabled,
|
||||
to,
|
||||
onClick,
|
||||
}: ButtonProps<T>) {
|
||||
const handleClick = (e: SyntheticEvent) => {
|
||||
if (onClick) {
|
||||
e.preventDefault();
|
||||
onClick(keyValue);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id, className, children, buttonType, disabled, name, to, testid } = this.props;
|
||||
};
|
||||
|
||||
let classNames = "";
|
||||
|
||||
switch (buttonType)
|
||||
{
|
||||
switch (buttonType) {
|
||||
case ButtonType.primary:
|
||||
classNames = "btn btn-primary";
|
||||
break;
|
||||
@ -73,21 +77,34 @@ class Button<T> extends react.Component<ButtonProps<T>> {
|
||||
classNames = "btn btn-link";
|
||||
break;
|
||||
case ButtonType.none:
|
||||
classNames = "btn btn-default"
|
||||
classNames = "btn btn-default";
|
||||
break;
|
||||
}
|
||||
|
||||
if (className !== undefined)
|
||||
{
|
||||
if (className) {
|
||||
classNames += " " + className;
|
||||
}
|
||||
|
||||
if (to !== undefined){
|
||||
return <Link data-testid={testid} id={id} className={classNames} to={to} >{children}</Link>
|
||||
if (to) {
|
||||
return (
|
||||
<Link data-testid={testid} id={id} className={classNames} to={to}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return <button data-testid={testid} id={id} className={classNames} name={name} disabled={disabled} onClick={this.Click}>{children}</button>;
|
||||
}
|
||||
return (
|
||||
<button
|
||||
data-testid={testid}
|
||||
id={id}
|
||||
className={classNames}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
||||
@ -1,57 +1,57 @@
|
||||
import react from 'react';
|
||||
import Button, { ButtonType } from './Button';
|
||||
import React, { useState, useCallback } from "react";
|
||||
import Button, { ButtonType } from "./Button";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Namespaces } from "../../i18n";
|
||||
|
||||
export interface ConfirmButtonProps<T>{
|
||||
delayMS? : number;
|
||||
buttonType : ButtonType;
|
||||
export interface ConfirmButtonProps<T> {
|
||||
delayMS?: number;
|
||||
buttonType: ButtonType;
|
||||
keyValue: T;
|
||||
children: React.ReactNode;
|
||||
confirmMessage?: React.ReactNode;
|
||||
onClick?: ( keyValue? : T ) => void;
|
||||
onClick?: (keyValue?: T) => void;
|
||||
}
|
||||
|
||||
export interface ConfirmButtonState{
|
||||
firstClick : boolean
|
||||
}
|
||||
function ConfirmButton<T>({
|
||||
delayMS = 5000,
|
||||
buttonType,
|
||||
keyValue,
|
||||
children,
|
||||
confirmMessage,
|
||||
onClick,
|
||||
}: ConfirmButtonProps<T>) {
|
||||
const [firstClick, setFirstClick] = useState(false);
|
||||
const t = useTranslation<typeof Namespaces.Common>();
|
||||
|
||||
class ConfirmButton<T> extends react.Component<ConfirmButtonProps<T>, ConfirmButtonState > {
|
||||
state : ConfirmButtonState = {
|
||||
firstClick : false
|
||||
}
|
||||
|
||||
FirstClick = () => {
|
||||
const firstClick = true;
|
||||
this.setState({firstClick});
|
||||
|
||||
let { delayMS } = this.props;
|
||||
if (delayMS === undefined)
|
||||
delayMS = 5000;
|
||||
const handleFirstClick = useCallback(() => {
|
||||
setFirstClick(true);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(`updating state`)
|
||||
const firstClick = false;
|
||||
this.setState({firstClick});
|
||||
setFirstClick(false);
|
||||
}, delayMS);
|
||||
}
|
||||
}, [delayMS]);
|
||||
|
||||
SecondClick = () => {
|
||||
const {keyValue, onClick} = this.props;
|
||||
|
||||
if (onClick)
|
||||
const handleSecondClick = useCallback(() => {
|
||||
if (onClick) {
|
||||
onClick(keyValue);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { buttonType, children, confirmMessage } = this.props;
|
||||
const { firstClick } = this.state;
|
||||
}, [onClick, keyValue]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!firstClick && <Button buttonType={buttonType} onClick={this.FirstClick}>{children}</Button>}
|
||||
{firstClick && <Button buttonType={ButtonType.danger} onClick={this.SecondClick}>{confirmMessage!==undefined?confirmMessage:"Are you sure?"}</Button>}
|
||||
{!firstClick && (
|
||||
<Button buttonType={buttonType} onClick={handleFirstClick}>
|
||||
{children}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{firstClick && (
|
||||
<Button buttonType={ButtonType.danger} onClick={handleSecondClick}>
|
||||
{confirmMessage ?? <>t("Are you sure?")</>}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfirmButton;
|
||||
29
src/i18n.ts
Normal file
29
src/i18n.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// /src/i18n.ts
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import HttpBackend from "i18next-http-backend";
|
||||
import { determineInitialLanguage } from "./modules/frame/services/lanugageService";
|
||||
|
||||
export const Namespaces = {
|
||||
Common: "common",
|
||||
Frame: "frame",
|
||||
} as const;
|
||||
|
||||
export type Namespace = (typeof Namespaces)[keyof typeof Namespaces];
|
||||
|
||||
i18n
|
||||
.use(HttpBackend) // load translations from /public/locales
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
lng: determineInitialLanguage(),
|
||||
fallbackLng: "en",
|
||||
defaultNS: "common",
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
backend: {
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@ -1,17 +1,19 @@
|
||||
import "./i18n";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement,
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
||||
@ -1,92 +1,120 @@
|
||||
import * as React from "react";
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
import authentication from "../services/authenticationService";
|
||||
import '../../../Sass/_leftMenu.scss';
|
||||
import { faCog, faCogs, faHome, faPrint } from "@fortawesome/pro-thin-svg-icons";
|
||||
import "../../../Sass/_leftMenu.scss";
|
||||
import {
|
||||
faCog,
|
||||
faCogs,
|
||||
faHome,
|
||||
faPrint,
|
||||
} from "@fortawesome/pro-thin-svg-icons";
|
||||
import LeftMenuItem from "./LeftMenuItem";
|
||||
import LeftMenuSubMenu, { LOCLeftMenuSubMenu } from "./LeftMenuSubMenu";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Namespaces } from "../../../i18n";
|
||||
|
||||
interface LeftMenuProps {
|
||||
const LeftMenu: React.FC = () => {
|
||||
const { t } = useTranslation<typeof Namespaces.Common>();
|
||||
|
||||
}
|
||||
const [openMenuItem, setOpenMenuItem] = useState<LOCLeftMenuSubMenu>();
|
||||
|
||||
interface LeftMenuState {
|
||||
openMenuItem? : LOCLeftMenuSubMenu;
|
||||
}
|
||||
// Close menus when clicking outside
|
||||
useEffect(() => {
|
||||
const handleClick = () => setOpenMenuItem(undefined);
|
||||
document.body.addEventListener("click", handleClick, true);
|
||||
return () => document.body.removeEventListener("click", handleClick, true);
|
||||
}, []);
|
||||
|
||||
class LeftMenu extends React.Component<LeftMenuProps, LeftMenuState> {
|
||||
state : LeftMenuState = {
|
||||
openMenuItem : undefined
|
||||
}
|
||||
const handleClick = useCallback((menuItem: LOCLeftMenuSubMenu) => {
|
||||
setOpenMenuItem((current) => (current === menuItem ? undefined : menuItem));
|
||||
}, []);
|
||||
|
||||
componentDidMount(): void {
|
||||
document.body.addEventListener('click', () => {
|
||||
this.setState( { openMenuItem : undefined })
|
||||
}, true);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<LeftMenuProps>, prevState: Readonly<LeftMenuState>, snapshot?: any): void {
|
||||
if (prevState === this.state)
|
||||
{
|
||||
this.setState( { openMenuItem : undefined })
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = (menuItem: LOCLeftMenuSubMenu) => {
|
||||
const { openMenuItem } = this.state;
|
||||
|
||||
const newMenuItem = openMenuItem === menuItem ? undefined : menuItem;
|
||||
|
||||
this.setState( { openMenuItem : newMenuItem })
|
||||
};
|
||||
|
||||
render() {
|
||||
// Access checks
|
||||
const viewOrganisation = authentication.hasAccess("ViewOrganisation");
|
||||
|
||||
const viewUser = authentication.hasAccess("ViewUser" );
|
||||
const viewDomain = authentication.hasAccess("ViewDomain" );
|
||||
const viewUser = authentication.hasAccess("ViewUser");
|
||||
const viewDomain = authentication.hasAccess("ViewDomain");
|
||||
const viewGlossary = authentication.hasAccess("ViewGlossary");
|
||||
const viewFormTemplate = authentication.hasAccess("ViewFormTemplate");
|
||||
const viewField = authentication.hasAccess("ViewField");
|
||||
const viewSequence = authentication.hasAccess("ViewSequence");
|
||||
const viewSsoManager = authentication.hasAccess("ViewSsoProviders");
|
||||
|
||||
const viewAdmin = viewUser || viewDomain || viewGlossary || viewFormTemplate || viewField || viewSequence;
|
||||
const viewAdmin =
|
||||
viewUser ||
|
||||
viewDomain ||
|
||||
viewGlossary ||
|
||||
viewFormTemplate ||
|
||||
viewField ||
|
||||
viewSequence;
|
||||
|
||||
const viewAuditLog = authentication.hasAccess("ViewAuditLog");
|
||||
const viewBlockedIPAddresses = authentication.hasAccess("ViewBlockedIPAddresses");
|
||||
const viewBlockedIPAddresses = authentication.hasAccess(
|
||||
"ViewBlockedIPAddresses",
|
||||
);
|
||||
const viewErrorLogs = authentication.hasAccess("ViewErrorLogs");
|
||||
|
||||
const viewSupport = viewAuditLog || viewBlockedIPAddresses || viewErrorLogs;
|
||||
|
||||
const { openMenuItem } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="leftMenu">
|
||||
<LeftMenuItem to="/" icon={faHome} label="Home" />
|
||||
{viewOrganisation && <LeftMenuItem to="/organisations" icon={faPrint} label="e-print" />}
|
||||
{viewAdmin && <LeftMenuSubMenu openMenu={openMenuItem} icon={faCog} label="Admin" onClick={this.handleClick} >
|
||||
{viewUser && <LeftMenuItem to="/users" label="Users"/>}
|
||||
{viewDomain && <LeftMenuItem to="/domains" label="Client Domains"/>}
|
||||
{viewGlossary && <LeftMenuItem to="/glossaries" label="Glossaries"/>}
|
||||
{viewFormTemplate && <LeftMenuItem to="/forms" label="Forms"/>}
|
||||
{viewField && <LeftMenuItem to="/customfields" label="Custom Fields"/>}
|
||||
{viewSequence && <LeftMenuItem to="/sequence" label="Sequence"/>}
|
||||
{viewSsoManager && <LeftMenuItem to="/ssoManager" label="Sso Manager"/>}
|
||||
<LeftMenuItem to="/" icon={faHome} label={t("Home")} />
|
||||
|
||||
{viewOrganisation && (
|
||||
<LeftMenuItem to="/organisations" icon={faPrint} label="e-print" />
|
||||
)}
|
||||
|
||||
{viewAdmin && (
|
||||
<LeftMenuSubMenu
|
||||
openMenu={openMenuItem}
|
||||
icon={faCog}
|
||||
label={t("Admin")}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{viewUser && <LeftMenuItem to="/users" label={t("Users")} />}
|
||||
{viewDomain && (
|
||||
<LeftMenuItem to="/domains" label={t("ClientDomains")} />
|
||||
)}
|
||||
{viewGlossary && (
|
||||
<LeftMenuItem to="/glossaries" label={t("Glossaries")} />
|
||||
)}
|
||||
{viewFormTemplate && (
|
||||
<LeftMenuItem to="/forms" label={t("Forms")} />
|
||||
)}
|
||||
{viewField && (
|
||||
<LeftMenuItem to="/customfields" label={t("CustomFields")} />
|
||||
)}
|
||||
{viewSequence && (
|
||||
<LeftMenuItem to="/sequence" label={t("Sequence")} />
|
||||
)}
|
||||
{viewSsoManager && (
|
||||
<LeftMenuItem to="/ssoManager" label={t("SsoManager")} />
|
||||
)}
|
||||
</LeftMenuSubMenu>
|
||||
}
|
||||
{viewSupport && <LeftMenuSubMenu openMenu={openMenuItem} icon={faCogs} label="Support" onClick={this.handleClick} >
|
||||
{viewAuditLog && <LeftMenuItem to="/audit" label="Audit Log"/>}
|
||||
{viewBlockedIPAddresses && <LeftMenuItem to="/blockedips" label="Blocked IPs"/>}
|
||||
{viewErrorLogs && <LeftMenuItem to="/exceptionlogs" label="Error Logs"/>}
|
||||
</LeftMenuSubMenu>}
|
||||
)}
|
||||
|
||||
{viewSupport && (
|
||||
<LeftMenuSubMenu
|
||||
openMenu={openMenuItem}
|
||||
icon={faCogs}
|
||||
label={t("Support")}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{viewAuditLog && <LeftMenuItem to="/audit" label={t("AuditLog")} />}
|
||||
{viewBlockedIPAddresses && (
|
||||
<LeftMenuItem to="/blockedips" label={t("BlockedIPs")} />
|
||||
)}
|
||||
{viewErrorLogs && (
|
||||
<LeftMenuItem to="/exceptionlogs" label={t("ErrorLogs")} />
|
||||
)}
|
||||
</LeftMenuSubMenu>
|
||||
)}
|
||||
</div>
|
||||
{openMenuItem && <div className="subbar">{openMenuItem.props.children}</div>}
|
||||
|
||||
{openMenuItem && (
|
||||
<div className="subbar">{openMenuItem.props.children}</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default LeftMenu;
|
||||
|
||||
@ -4,23 +4,20 @@ import LeftMenu from "./LeftMenu";
|
||||
|
||||
import "../../../Sass/_frame.scss";
|
||||
|
||||
|
||||
type MainFrameProps = {
|
||||
title?: string;
|
||||
title?: string | undefined | null;
|
||||
children?: React.ReactNode; // 👈️ type children
|
||||
};
|
||||
|
||||
const Mainframe = (props: MainFrameProps): JSX.Element => {
|
||||
return (
|
||||
<div className="frame">
|
||||
<TopMenu title={props.title} />
|
||||
<TopMenu title={props.title ? props.title : undefined} />
|
||||
<div className="frame-row">
|
||||
<div className="frame-leftMenu">
|
||||
<LeftMenu />
|
||||
</div>
|
||||
<div className="frame-workArea">
|
||||
{props.children}
|
||||
</div>
|
||||
<div className="frame-workArea">{props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -5,4 +5,5 @@ export default interface JwtToken {
|
||||
email: string;
|
||||
domainid: bigint;
|
||||
securityPrivileges: [];
|
||||
language?: string;
|
||||
}
|
||||
|
||||
21
src/modules/frame/services/lanugageService.ts
Normal file
21
src/modules/frame/services/lanugageService.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { getCurrentUser } from "./authenticationService";
|
||||
|
||||
export function determineInitialLanguage() {
|
||||
// 1. JWT preference
|
||||
const currentUser = getCurrentUser();
|
||||
if (currentUser !== null) {
|
||||
const jwtLang = currentUser.language || null;
|
||||
if (jwtLang) return jwtLang;
|
||||
}
|
||||
|
||||
// 2. LocalStorage
|
||||
//const storedLang = localStorage.getItem("appLanguage");
|
||||
//if (storedLang) return storedLang;
|
||||
|
||||
// 3. Browser
|
||||
const browserLang = navigator.language?.split("-")[0];
|
||||
if (browserLang) return browserLang;
|
||||
|
||||
// 4. Default
|
||||
return "en";
|
||||
}
|
||||
@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
@ -20,8 +16,5 @@
|
||||
"noEmit": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"src/types"
|
||||
]
|
||||
"include": ["src", "src/types", "i18next-parser.config.js"]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user