diff --git a/.gitignore b/.gitignore
index 9316a3d34..a0aaf9e00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -171,6 +171,7 @@ static_root
node_modules
static/js/ui.js*
coverage
+frontend/dist
# local django configuration
airone/settings.py
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 302bc1c81..61404816b 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,31 +1,11 @@
-import { ThemeProvider } from "@mui/material/styles";
-import React, { FC } from "react";
+import React from "react";
import { createRoot } from "react-dom/client";
-import { AironeSnackbarProvider } from "AironeSnackbarProvider";
-import { AppRouter } from "AppRouter";
-import { CheckTermsService } from "CheckTermsService";
-import { ErrorHandler } from "ErrorHandler";
-import { theme } from "Theme";
-import "i18n/config";
-
-const App: FC = () => {
- return (
-
-
-
-
-
-
-
-
-
- );
-};
+import { AppBase } from "AppBase";
const container = document.getElementById("app");
if (container == null) {
throw new Error("failed to initializer React app.");
}
const root = createRoot(container);
-root.render();
+root.render();
diff --git a/frontend/src/AppBase.tsx b/frontend/src/AppBase.tsx
new file mode 100644
index 000000000..570ba7436
--- /dev/null
+++ b/frontend/src/AppBase.tsx
@@ -0,0 +1,30 @@
+import { ThemeProvider } from "@mui/material/styles";
+import React, { FC } from "react";
+
+import { AironeSnackbarProvider } from "AironeSnackbarProvider";
+import { CheckTermsService } from "CheckTermsService";
+import { ErrorHandler } from "ErrorHandler";
+import { theme } from "Theme";
+import { AppRouter } from "routes/AppRouter";
+import "i18n/config";
+
+interface Props {
+ customRoutes?: {
+ path: string;
+ element: React.ReactNode;
+ }[];
+}
+
+export const AppBase: FC = ({ customRoutes }) => {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/ErrorHandler.tsx b/frontend/src/ErrorHandler.tsx
index 8a268ee48..0864db0a0 100644
--- a/frontend/src/ErrorHandler.tsx
+++ b/frontend/src/ErrorHandler.tsx
@@ -22,7 +22,7 @@ import {
NonTermsServiceAgreement,
} from "./services/Exceptions";
-import { topPath } from "Routes";
+import { topPath } from "routes/Routes";
const ErrorDescription = styled(Box)(({ theme }) => ({
marginTop: theme.spacing(2),
diff --git a/frontend/src/components/acl/index.ts b/frontend/src/components/acl/index.ts
new file mode 100644
index 000000000..1b8facd79
--- /dev/null
+++ b/frontend/src/components/acl/index.ts
@@ -0,0 +1,2 @@
+export * from "./ACLForm";
+export * from "./ACLHistoryList";
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/common/Header.tsx
similarity index 99%
rename from frontend/src/components/Header.tsx
rename to frontend/src/components/common/Header.tsx
index b05d7f9cb..c56ec9431 100644
--- a/frontend/src/components/Header.tsx
+++ b/frontend/src/components/common/Header.tsx
@@ -23,8 +23,11 @@ import React, { FC, MouseEvent, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { useInterval } from "react-use";
-import { useTranslation } from "../hooks/useTranslation";
+import { useTranslation } from "../../hooks/useTranslation";
+import { SearchBox } from "components/common/SearchBox";
+import { useSimpleSearch } from "hooks/useSimpleSearch";
+import { aironeApiClient } from "repository/AironeApiClient";
import {
advancedSearchPath,
entitiesPath,
@@ -36,10 +39,7 @@ import {
triggersPath,
userPath,
usersPath,
-} from "Routes";
-import { SearchBox } from "components/common/SearchBox";
-import { useSimpleSearch } from "hooks/useSimpleSearch";
-import { aironeApiClient } from "repository/AironeApiClient";
+} from "routes/Routes";
import {
JobOperations,
JobRefreshIntervalMilliSec,
diff --git a/frontend/src/components/common/PageHeader.tsx b/frontend/src/components/common/PageHeader.tsx
index 3fb577c7e..a022e72b3 100644
--- a/frontend/src/components/common/PageHeader.tsx
+++ b/frontend/src/components/common/PageHeader.tsx
@@ -4,7 +4,7 @@ import { styled } from "@mui/material/styles";
import React, { FC } from "react";
import { Link } from "react-router-dom";
-import { jobsPath } from "Routes";
+import { jobsPath } from "routes/Routes";
const Frame = styled(Box)({
width: "100%",
diff --git a/frontend/src/components/common/index.ts b/frontend/src/components/common/index.ts
new file mode 100644
index 000000000..e729c5815
--- /dev/null
+++ b/frontend/src/components/common/index.ts
@@ -0,0 +1,15 @@
+export * from "./AironeBreadcrumbs";
+export * from "./AironeModal";
+export * from "./AironeTableHeadCell";
+export * from "./AironeTableHeadRow";
+export * from "./AutocompleteWithAllSelector";
+export * from "./ClipboardCopyButton";
+export * from "./Confirmable";
+export * from "./FlexBox";
+export * from "./ImportForm";
+export * from "./Loading";
+export * from "./PageHeader";
+export * from "./PaginationFooter";
+export * from "./RateLimitedClickable";
+export * from "./SearchBox";
+export * from "./SubmitButton";
diff --git a/frontend/src/components/entity/EntityBreadcrumbs.tsx b/frontend/src/components/entity/EntityBreadcrumbs.tsx
index cfb9ae8d5..216132623 100644
--- a/frontend/src/components/entity/EntityBreadcrumbs.tsx
+++ b/frontend/src/components/entity/EntityBreadcrumbs.tsx
@@ -4,9 +4,9 @@ import { Typography } from "@mui/material";
import React, { FC } from "react";
import { Link } from "react-router-dom";
-import { topPath, entitiesPath, entityEntriesPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { FlexBox } from "components/common/FlexBox";
+import { topPath, entitiesPath, entityEntriesPath } from "routes/Routes";
interface Props {
entity?: EntityDetail;
diff --git a/frontend/src/components/entity/EntityControlMenu.tsx b/frontend/src/components/entity/EntityControlMenu.tsx
index 27a2924ac..3312e0cff 100644
--- a/frontend/src/components/entity/EntityControlMenu.tsx
+++ b/frontend/src/components/entity/EntityControlMenu.tsx
@@ -12,6 +12,8 @@ import { Link, useNavigate } from "react-router-dom";
import { RateLimitedClickable } from "../common/RateLimitedClickable";
+import { Confirmable } from "components/common/Confirmable";
+import { aironeApiClient } from "repository/AironeApiClient";
import {
aclPath,
entityHistoryPath,
@@ -21,9 +23,7 @@ import {
topPath,
entityEntriesPath,
aclHistoryPath,
-} from "Routes";
-import { Confirmable } from "components/common/Confirmable";
-import { aironeApiClient } from "repository/AironeApiClient";
+} from "routes/Routes";
type ExportFormatType = "YAML" | "CSV";
diff --git a/frontend/src/components/entity/EntityList.tsx b/frontend/src/components/entity/EntityList.tsx
index ba453b822..4b4188287 100644
--- a/frontend/src/components/entity/EntityList.tsx
+++ b/frontend/src/components/entity/EntityList.tsx
@@ -6,9 +6,9 @@ import { Link } from "react-router-dom";
import { EntityListCard } from "./EntityListCard";
-import { newEntityPath } from "Routes";
import { PaginationFooter } from "components/common/PaginationFooter";
import { SearchBox } from "components/common/SearchBox";
+import { newEntityPath } from "routes/Routes";
import { EntityList as ConstEntityList } from "services/Constants";
import { normalizeToMatch } from "services/StringUtil";
diff --git a/frontend/src/components/entity/EntityListCard.tsx b/frontend/src/components/entity/EntityListCard.tsx
index 341f55b65..706eab77c 100644
--- a/frontend/src/components/entity/EntityListCard.tsx
+++ b/frontend/src/components/entity/EntityListCard.tsx
@@ -15,9 +15,9 @@ import { Link } from "react-router-dom";
import { EntityControlMenu } from "./EntityControlMenu";
-import { entityEntriesPath } from "Routes";
import { ClipboardCopyButton } from "components/common/ClipboardCopyButton";
import { EntryImportModal } from "components/entry/EntryImportModal";
+import { entityEntriesPath } from "routes/Routes";
const EntityNote = styled(Typography)(({ theme }) => ({
color: theme.palette.text.secondary,
diff --git a/frontend/src/components/entity/entityForm/AttributeField.tsx b/frontend/src/components/entity/entityForm/AttributeField.tsx
index 1d439f13e..4a447f7fc 100644
--- a/frontend/src/components/entity/entityForm/AttributeField.tsx
+++ b/frontend/src/components/entity/entityForm/AttributeField.tsx
@@ -25,7 +25,7 @@ import { Link } from "react-router-dom";
import { AttributeNoteModal } from "./AttributeNoteModal";
import { Schema } from "./EntityFormSchema";
-import { aclPath } from "Routes";
+import { aclPath } from "routes/Routes";
import { AttributeTypes } from "services/Constants";
const StyledBox = styled(Box)(({ theme }) => ({
diff --git a/frontend/src/components/entity/index.ts b/frontend/src/components/entity/index.ts
new file mode 100644
index 000000000..2e12250f0
--- /dev/null
+++ b/frontend/src/components/entity/index.ts
@@ -0,0 +1,4 @@
+// FIXME rethink export scope
+
+export * from "./EntityBreadcrumbs";
+export * from "./EntityControlMenu";
diff --git a/frontend/src/components/entry/AttributeValue.tsx b/frontend/src/components/entry/AttributeValue.tsx
index f67785bb4..e6ac8ba79 100644
--- a/frontend/src/components/entry/AttributeValue.tsx
+++ b/frontend/src/components/entry/AttributeValue.tsx
@@ -12,7 +12,7 @@ import * as React from "react";
import { FC } from "react";
import { Link } from "react-router-dom";
-import { groupsPath, rolePath, entryDetailsPath } from "Routes";
+import { groupsPath, rolePath, entryDetailsPath } from "routes/Routes";
const StyledBox = styled(Box)(() => ({
display: "flex",
diff --git a/frontend/src/components/entry/EntryBreadcrumbs.tsx b/frontend/src/components/entry/EntryBreadcrumbs.tsx
index 6dcad33f6..ffcdb24da 100644
--- a/frontend/src/components/entry/EntryBreadcrumbs.tsx
+++ b/frontend/src/components/entry/EntryBreadcrumbs.tsx
@@ -4,14 +4,14 @@ import { Typography } from "@mui/material";
import React, { FC } from "react";
import { Link } from "react-router-dom";
+import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
+import { FlexBox } from "components/common/FlexBox";
import {
topPath,
entitiesPath,
entityEntriesPath,
entryDetailsPath,
-} from "Routes";
-import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
-import { FlexBox } from "components/common/FlexBox";
+} from "routes/Routes";
interface Props {
entry?: EntryRetrieve;
diff --git a/frontend/src/components/entry/EntryControlMenu.tsx b/frontend/src/components/entry/EntryControlMenu.tsx
index cbfa5e671..a6aa102e8 100644
--- a/frontend/src/components/entry/EntryControlMenu.tsx
+++ b/frontend/src/components/entry/EntryControlMenu.tsx
@@ -11,6 +11,8 @@ import { useSnackbar } from "notistack";
import React, { FC } from "react";
import { Link, useNavigate } from "react-router-dom";
+import { Confirmable } from "components/common/Confirmable";
+import { aironeApiClient } from "repository/AironeApiClient";
import {
entryEditPath,
aclPath,
@@ -20,9 +22,7 @@ import {
topPath,
entryDetailsPath,
aclHistoryPath,
-} from "Routes";
-import { Confirmable } from "components/common/Confirmable";
-import { aironeApiClient } from "repository/AironeApiClient";
+} from "routes/Routes";
interface EntryControlProps {
entityId: number;
diff --git a/frontend/src/components/entry/EntryHistoryList.tsx b/frontend/src/components/entry/EntryHistoryList.tsx
index 584e408d4..3085f1f45 100644
--- a/frontend/src/components/entry/EntryHistoryList.tsx
+++ b/frontend/src/components/entry/EntryHistoryList.tsx
@@ -16,10 +16,10 @@ import { useNavigate } from "react-router-dom";
import { AttributeValue } from "./AttributeValue";
-import { showEntryHistoryPath, topPath } from "Routes";
import { Confirmable } from "components/common/Confirmable";
import { PaginationFooter } from "components/common/PaginationFooter";
import { aironeApiClient } from "repository/AironeApiClient";
+import { showEntryHistoryPath, topPath } from "routes/Routes";
import { EntryHistoryList as ConstEntryHistoryList } from "services/Constants";
import { formatDateTime } from "services/DateUtil";
diff --git a/frontend/src/components/entry/EntryList.tsx b/frontend/src/components/entry/EntryList.tsx
index 9efd49e8f..e4fc8a3cd 100644
--- a/frontend/src/components/entry/EntryList.tsx
+++ b/frontend/src/components/entry/EntryList.tsx
@@ -5,13 +5,13 @@ import { Link, useNavigate, useLocation } from "react-router-dom";
import { EntryListCard } from "./EntryListCard";
-import { newEntryPath } from "Routes";
import { Loading } from "components/common/Loading";
import { PaginationFooter } from "components/common/PaginationFooter";
import { SearchBox } from "components/common/SearchBox";
import { useAsyncWithThrow } from "hooks/useAsyncWithThrow";
import { usePage } from "hooks/usePage";
import { aironeApiClient } from "repository/AironeApiClient";
+import { newEntryPath } from "routes/Routes";
import { EntryList as ConstEntryList } from "services/Constants";
import { normalizeToMatch } from "services/StringUtil";
diff --git a/frontend/src/components/entry/EntryListCard.tsx b/frontend/src/components/entry/EntryListCard.tsx
index 217bf9ce9..689f3cc12 100644
--- a/frontend/src/components/entry/EntryListCard.tsx
+++ b/frontend/src/components/entry/EntryListCard.tsx
@@ -12,9 +12,9 @@ import { styled } from "@mui/material/styles";
import React, { FC, useState } from "react";
import { Link } from "react-router-dom";
-import { entryDetailsPath } from "Routes";
import { ClipboardCopyButton } from "components/common/ClipboardCopyButton";
import { EntryControlMenu } from "components/entry/EntryControlMenu";
+import { entryDetailsPath } from "routes/Routes";
const StyledCard = styled(Card)(({}) => ({
height: "100%",
diff --git a/frontend/src/components/entry/EntryReferral.tsx b/frontend/src/components/entry/EntryReferral.tsx
index 7b6ef3458..15e13ed4f 100644
--- a/frontend/src/components/entry/EntryReferral.tsx
+++ b/frontend/src/components/entry/EntryReferral.tsx
@@ -18,8 +18,8 @@ import { aironeApiClient } from "../../repository/AironeApiClient";
import { EntryReferralList } from "../../services/Constants";
import { normalizeToMatch } from "../../services/StringUtil";
-import { entryDetailsPath } from "Routes";
import { SearchBox } from "components/common/SearchBox";
+import { entryDetailsPath } from "routes/Routes";
const ReferralCount = styled(Typography)(({}) => ({
fontSize: "16px",
diff --git a/frontend/src/components/entry/RestorableEntryList.tsx b/frontend/src/components/entry/RestorableEntryList.tsx
index 9b89a4095..08cb07d36 100644
--- a/frontend/src/components/entry/RestorableEntryList.tsx
+++ b/frontend/src/components/entry/RestorableEntryList.tsx
@@ -24,7 +24,6 @@ import { useNavigate, useLocation } from "react-router-dom";
import { EntryAttributes } from "./EntryAttributes";
-import { restoreEntryPath, topPath } from "Routes";
import { Confirmable } from "components/common/Confirmable";
import { Loading } from "components/common/Loading";
import { PaginationFooter } from "components/common/PaginationFooter";
@@ -32,6 +31,7 @@ import { SearchBox } from "components/common/SearchBox";
import { useAsyncWithThrow } from "hooks/useAsyncWithThrow";
import { usePage } from "hooks/usePage";
import { aironeApiClient } from "repository/AironeApiClient";
+import { restoreEntryPath, topPath } from "routes/Routes";
import { EntryList as ConstEntryList } from "services/Constants";
import { formatDateTime } from "services/DateUtil";
import { normalizeToMatch } from "services/StringUtil";
diff --git a/frontend/src/components/entry/SearchResults.tsx b/frontend/src/components/entry/SearchResults.tsx
index e9cedc382..3db06e890 100644
--- a/frontend/src/components/entry/SearchResults.tsx
+++ b/frontend/src/components/entry/SearchResults.tsx
@@ -21,9 +21,9 @@ import { Link } from "react-router-dom";
import { SearchResultsTableHead } from "./SearchResultsTableHead";
-import { entryDetailsPath } from "Routes";
import { PaginationFooter } from "components/common/PaginationFooter";
import { AttributeValue } from "components/entry/AttributeValue";
+import { entryDetailsPath } from "routes/Routes";
import { AdvancedSerarchResultList } from "services/Constants";
import { AttrsFilter } from "services/entry/AdvancedSearch";
diff --git a/frontend/src/components/entry/index.ts b/frontend/src/components/entry/index.ts
new file mode 100644
index 000000000..95ef966d8
--- /dev/null
+++ b/frontend/src/components/entry/index.ts
@@ -0,0 +1,10 @@
+// FIXME rethink export scope
+
+export * from "./CopyForm";
+export * from "./EntryBreadcrumbs";
+export * from "./EntryControlMenu";
+export * from "./EntryImportModal";
+export * from "./EntryListCard";
+
+export * from "./entryForm/EntryFormSchema";
+export * from "./entryForm/AttributeValueField";
diff --git a/frontend/src/components/group/GroupControlMenu.tsx b/frontend/src/components/group/GroupControlMenu.tsx
index d02ab5fd9..a07eb6f50 100644
--- a/frontend/src/components/group/GroupControlMenu.tsx
+++ b/frontend/src/components/group/GroupControlMenu.tsx
@@ -11,9 +11,9 @@ import { useSnackbar } from "notistack";
import React, { FC, useCallback } from "react";
import { Link, useNavigate } from "react-router-dom";
-import { groupPath, groupsPath, topPath } from "Routes";
import { Confirmable } from "components/common/Confirmable";
import { aironeApiClient } from "repository/AironeApiClient";
+import { groupPath, groupsPath, topPath } from "routes/Routes";
interface Props {
groupId: number;
diff --git a/frontend/src/components/group/GroupTreeItem.tsx b/frontend/src/components/group/GroupTreeItem.tsx
index 9329737db..dec29c371 100644
--- a/frontend/src/components/group/GroupTreeItem.tsx
+++ b/frontend/src/components/group/GroupTreeItem.tsx
@@ -3,8 +3,8 @@ import { Checkbox, IconButton, ListItem, Typography } from "@mui/material";
import React, { FC } from "react";
import { Link } from "react-router-dom";
-import { groupPath } from "../../Routes";
import { GroupTree } from "../../repository/AironeApiClient";
+import { groupPath } from "../../routes/Routes";
import { ServerContext } from "../../services/ServerContext";
const CHILDREN_INDENT_WIDTH = 16;
diff --git a/frontend/src/components/group/GroupTreeRoot.tsx b/frontend/src/components/group/GroupTreeRoot.tsx
index fb1a02cb4..110a8c6c4 100644
--- a/frontend/src/components/group/GroupTreeRoot.tsx
+++ b/frontend/src/components/group/GroupTreeRoot.tsx
@@ -10,8 +10,8 @@ import { styled } from "@mui/material/styles";
import React, { FC } from "react";
import { Link } from "react-router-dom";
-import { groupPath } from "../../Routes";
import { GroupTree } from "../../repository/AironeApiClient";
+import { groupPath } from "../../routes/Routes";
import { ServerContext } from "../../services/ServerContext";
import { GroupTreeItem } from "./GroupTreeItem";
diff --git a/frontend/src/components/index.ts b/frontend/src/components/index.ts
new file mode 100644
index 000000000..7f7111ce1
--- /dev/null
+++ b/frontend/src/components/index.ts
@@ -0,0 +1,4 @@
+export * from "./acl";
+export * from "./common";
+export * from "./entity";
+export * from "./entry";
diff --git a/frontend/src/components/job/JobList.tsx b/frontend/src/components/job/JobList.tsx
index 10e68a81e..39f69b3ec 100644
--- a/frontend/src/components/job/JobList.tsx
+++ b/frontend/src/components/job/JobList.tsx
@@ -15,8 +15,8 @@ import { styled } from "@mui/material/styles";
import React, { FC, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
-import { entityEntriesPath } from "../../Routes";
import { aironeApiClient } from "../../repository/AironeApiClient";
+import { entityEntriesPath } from "../../routes/Routes";
import { JobOperations, JobStatuses } from "../../services/Constants";
import { formatDateTime } from "../../services/DateUtil";
import { jobOperationLabel, jobStatusLabel } from "../../services/JobUtil";
diff --git a/frontend/src/components/role/RoleList.tsx b/frontend/src/components/role/RoleList.tsx
index ccb32fd2d..5ade09824 100644
--- a/frontend/src/components/role/RoleList.tsx
+++ b/frontend/src/components/role/RoleList.tsx
@@ -25,7 +25,7 @@ import { aironeApiClient } from "../../repository/AironeApiClient";
import { Confirmable } from "../common/Confirmable";
import { Loading } from "../common/Loading";
-import { rolePath, rolesPath, topPath } from "Routes";
+import { rolePath, rolesPath, topPath } from "routes/Routes";
const StyledList = styled(List)(() => ({
padding: "0",
diff --git a/frontend/src/components/user/UserControlMenu.tsx b/frontend/src/components/user/UserControlMenu.tsx
index b19463e89..fc9eb5ef3 100644
--- a/frontend/src/components/user/UserControlMenu.tsx
+++ b/frontend/src/components/user/UserControlMenu.tsx
@@ -14,10 +14,10 @@ import { useNavigate } from "react-router-dom";
import { UserPasswordFormModal } from "./UserPasswordFormModal";
-import { topPath } from "Routes";
-import { usersPath } from "Routes";
import { Confirmable } from "components/common/Confirmable";
import { aironeApiClient } from "repository/AironeApiClient";
+import { topPath } from "routes/Routes";
+import { usersPath } from "routes/Routes";
interface UserControlProps {
user: UserList;
diff --git a/frontend/src/components/user/UserList.tsx b/frontend/src/components/user/UserList.tsx
index 0cf8c557d..bf5f93235 100644
--- a/frontend/src/components/user/UserList.tsx
+++ b/frontend/src/components/user/UserList.tsx
@@ -17,7 +17,6 @@ import { Link, useNavigate, useLocation } from "react-router-dom";
import { UserControlMenu } from "./UserControlMenu";
-import { newUserPath, userPath } from "Routes";
import { ClipboardCopyButton } from "components/common/ClipboardCopyButton";
import { Loading } from "components/common/Loading";
import { PaginationFooter } from "components/common/PaginationFooter";
@@ -25,6 +24,7 @@ import { SearchBox } from "components/common/SearchBox";
import { useAsyncWithThrow } from "hooks/useAsyncWithThrow";
import { usePage } from "hooks/usePage";
import { aironeApiClient } from "repository/AironeApiClient";
+import { newUserPath, userPath } from "routes/Routes";
import { UserList as ConstUserList } from "services/Constants";
import { ServerContext } from "services/ServerContext";
import { normalizeToMatch } from "services/StringUtil";
diff --git a/frontend/src/components/user/UserPasswordFormModal.tsx b/frontend/src/components/user/UserPasswordFormModal.tsx
index 406fda701..701e783ed 100644
--- a/frontend/src/components/user/UserPasswordFormModal.tsx
+++ b/frontend/src/components/user/UserPasswordFormModal.tsx
@@ -7,7 +7,7 @@ import { useNavigate } from "react-router-dom";
import { aironeApiClient } from "../../repository/AironeApiClient";
import { AironeModal } from "../common/AironeModal";
-import { loginPath, topPath, usersPath } from "Routes";
+import { loginPath, topPath, usersPath } from "routes/Routes";
import { ServerContext } from "services/ServerContext";
const PasswordField = styled(Box)(({ theme }) => ({
diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts
new file mode 100644
index 000000000..f6fd9a665
--- /dev/null
+++ b/frontend/src/hooks/index.ts
@@ -0,0 +1,7 @@
+export * from "./useAsyncWithThrow";
+export * from "./useFormNotification";
+export * from "./usePage";
+export * from "./usePrompt";
+export * from "./useSimpleSearch";
+export * from "./useTranslation";
+export * from "./useTypedParams";
diff --git a/frontend/src/hooks/useSimpleSearch.tsx b/frontend/src/hooks/useSimpleSearch.tsx
index e7f00aaf6..cccc05260 100644
--- a/frontend/src/hooks/useSimpleSearch.tsx
+++ b/frontend/src/hooks/useSimpleSearch.tsx
@@ -1,7 +1,7 @@
import { useCallback, useMemo } from "react";
import { useNavigate, useLocation } from "react-router-dom";
-import { topPath } from "../Routes";
+import { topPath } from "../routes/Routes";
type Query = string | undefined;
diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts
new file mode 100644
index 000000000..5c62e04f5
--- /dev/null
+++ b/frontend/src/i18n/index.ts
@@ -0,0 +1 @@
+export * from "./config";
diff --git a/frontend/src/index.ts b/frontend/src/index.ts
new file mode 100644
index 000000000..afc23e301
--- /dev/null
+++ b/frontend/src/index.ts
@@ -0,0 +1,9 @@
+export * from "./AppBase";
+
+export * from "./components";
+export * from "./hooks";
+export * from "./i18n";
+export * from "./pages";
+export * from "./repository";
+export * from "./routes";
+export * from "./services";
diff --git a/frontend/src/pages/ACLEditPage.tsx b/frontend/src/pages/ACLEditPage.tsx
index 1ffb9b161..c9b46a363 100644
--- a/frontend/src/pages/ACLEditPage.tsx
+++ b/frontend/src/pages/ACLEditPage.tsx
@@ -12,7 +12,6 @@ import { useNavigate } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { editEntityPath, entityEntriesPath, entryDetailsPath } from "Routes";
import { ACLForm } from "components/acl/ACLForm";
import { Schema, schema } from "components/acl/aclForm/ACLFormSchema";
import { Loading } from "components/common/Loading";
@@ -23,6 +22,11 @@ import { EntryBreadcrumbs } from "components/entry/EntryBreadcrumbs";
import { usePrompt } from "hooks/usePrompt";
import { useTypedParams } from "hooks/useTypedParams";
import { aironeApiClient } from "repository/AironeApiClient";
+import {
+ editEntityPath,
+ entityEntriesPath,
+ entryDetailsPath,
+} from "routes/Routes";
export const ACLEditPage: FC = () => {
const navigate = useNavigate();
diff --git a/frontend/src/pages/AdvancedSearchPage.tsx b/frontend/src/pages/AdvancedSearchPage.tsx
index a34917ad6..cd489244e 100644
--- a/frontend/src/pages/AdvancedSearchPage.tsx
+++ b/frontend/src/pages/AdvancedSearchPage.tsx
@@ -22,8 +22,8 @@ import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
import { aironeApiClient } from "../repository/AironeApiClient";
import { formatAdvancedSearchParams } from "../services/entry/AdvancedSearch";
-import { advancedSearchResultPath, topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
+import { advancedSearchResultPath, topPath } from "routes/Routes";
const StyledFlexBox = styled(Box)({
display: "flex",
diff --git a/frontend/src/pages/AdvancedSearchResultsPage.tsx b/frontend/src/pages/AdvancedSearchResultsPage.tsx
index 5d3dac569..b3d8c6bfe 100644
--- a/frontend/src/pages/AdvancedSearchResultsPage.tsx
+++ b/frontend/src/pages/AdvancedSearchResultsPage.tsx
@@ -13,7 +13,6 @@ import { Link, useLocation } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { advancedSearchPath, topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { Confirmable } from "components/common/Confirmable";
import { CenterAlignedBox } from "components/common/FlexBox";
@@ -24,6 +23,7 @@ import { AdvancedSearchModal } from "components/entry/AdvancedSearchModal";
import { SearchResults } from "components/entry/SearchResults";
import { usePage } from "hooks/usePage";
import { aironeApiClient } from "repository/AironeApiClient";
+import { advancedSearchPath, topPath } from "routes/Routes";
import { AdvancedSerarchResultList } from "services/Constants";
import { extractAdvancedSearchParams } from "services/entry/AdvancedSearch";
diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx
index f2aaed085..1edc5b4d0 100644
--- a/frontend/src/pages/DashboardPage.tsx
+++ b/frontend/src/pages/DashboardPage.tsx
@@ -6,12 +6,12 @@ import { Link, useNavigate, useLocation } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { entityEntriesPath, entryDetailsPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { Loading } from "components/common/Loading";
import { SearchBox } from "components/common/SearchBox";
import { useSimpleSearch } from "hooks/useSimpleSearch";
import { aironeApiClient } from "repository/AironeApiClient";
+import { entityEntriesPath, entryDetailsPath } from "routes/Routes";
const StyledContainer = styled(Container)({
marginTop: "16px",
diff --git a/frontend/src/pages/EntityEditPage.test.tsx b/frontend/src/pages/EntityEditPage.test.tsx
index 4ee1e473b..49b68d91f 100644
--- a/frontend/src/pages/EntityEditPage.test.tsx
+++ b/frontend/src/pages/EntityEditPage.test.tsx
@@ -12,7 +12,7 @@ import { setupServer } from "msw/node";
import React from "react";
import { createMemoryRouter, RouterProvider } from "react-router-dom";
-import { editEntityPath } from "../Routes";
+import { editEntityPath } from "../routes/Routes";
import { EntityEditPage } from "./EntityEditPage";
diff --git a/frontend/src/pages/EntityEditPage.tsx b/frontend/src/pages/EntityEditPage.tsx
index fb937f1b0..01d1433c5 100644
--- a/frontend/src/pages/EntityEditPage.tsx
+++ b/frontend/src/pages/EntityEditPage.tsx
@@ -5,7 +5,6 @@ import React, { FC, useEffect } from "react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
-import { entitiesPath, entityEntriesPath } from "Routes";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
import { SubmitButton } from "components/common/SubmitButton";
@@ -17,6 +16,7 @@ import { useFormNotification } from "hooks/useFormNotification";
import { usePrompt } from "hooks/usePrompt";
import { useTypedParams } from "hooks/useTypedParams";
import { aironeApiClient } from "repository/AironeApiClient";
+import { entitiesPath, entityEntriesPath } from "routes/Routes";
import {
extractAPIException,
isResponseError,
diff --git a/frontend/src/pages/EntityListPage.tsx b/frontend/src/pages/EntityListPage.tsx
index 3bdca1069..50f15c709 100644
--- a/frontend/src/pages/EntityListPage.tsx
+++ b/frontend/src/pages/EntityListPage.tsx
@@ -4,7 +4,6 @@ import { Link, useNavigate, useLocation } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
@@ -12,6 +11,7 @@ import { EntityImportModal } from "components/entity/EntityImportModal";
import { EntityList } from "components/entity/EntityList";
import { usePage } from "hooks/usePage";
import { aironeApiClient } from "repository/AironeApiClient";
+import { topPath } from "routes/Routes";
export const EntityListPage: FC = () => {
const location = useLocation();
diff --git a/frontend/src/pages/EntryCopyPage.tsx b/frontend/src/pages/EntryCopyPage.tsx
index 0cc5c1872..84d54fa51 100644
--- a/frontend/src/pages/EntryCopyPage.tsx
+++ b/frontend/src/pages/EntryCopyPage.tsx
@@ -6,7 +6,6 @@ import { useNavigate } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
import { useTypedParams } from "../hooks/useTypedParams";
-import { entityEntriesPath, entryDetailsPath } from "Routes";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
import { SubmitButton } from "components/common/SubmitButton";
@@ -17,6 +16,7 @@ import {
import { EntryBreadcrumbs } from "components/entry/EntryBreadcrumbs";
import { usePrompt } from "hooks/usePrompt";
import { aironeApiClient } from "repository/AironeApiClient";
+import { entityEntriesPath, entryDetailsPath } from "routes/Routes";
interface Props {
CopyForm?: FC;
diff --git a/frontend/src/pages/EntryDetailsPage.test.tsx b/frontend/src/pages/EntryDetailsPage.test.tsx
index 2b6fb3595..c208ecc0f 100644
--- a/frontend/src/pages/EntryDetailsPage.test.tsx
+++ b/frontend/src/pages/EntryDetailsPage.test.tsx
@@ -6,9 +6,9 @@ import { render, screen, act, waitFor } from "@testing-library/react";
import React from "react";
import { createMemoryRouter, RouterProvider } from "react-router-dom";
-import { entryDetailsPath } from "Routes";
import { TestWrapperWithoutRoutes } from "TestWrapper";
import { EntryDetailsPage } from "pages/EntryDetailsPage";
+import { entryDetailsPath } from "routes/Routes";
afterEach(() => {
jest.clearAllMocks();
diff --git a/frontend/src/pages/EntryDetailsPage.tsx b/frontend/src/pages/EntryDetailsPage.tsx
index 8569b4249..74d42f20f 100644
--- a/frontend/src/pages/EntryDetailsPage.tsx
+++ b/frontend/src/pages/EntryDetailsPage.tsx
@@ -8,7 +8,6 @@ import { useNavigate } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
import { useTypedParams } from "../hooks/useTypedParams";
-import { entryDetailsPath, restoreEntryPath } from "Routes";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
import { EntryAttributes } from "components/entry/EntryAttributes";
@@ -16,6 +15,7 @@ import { EntryBreadcrumbs } from "components/entry/EntryBreadcrumbs";
import { EntryControlMenu } from "components/entry/EntryControlMenu";
import { EntryReferral } from "components/entry/EntryReferral";
import { aironeApiClient } from "repository/AironeApiClient";
+import { entryDetailsPath, restoreEntryPath } from "routes/Routes";
const FlexBox = styled(Box)(({}) => ({
display: "flex",
diff --git a/frontend/src/pages/EntryEditPage.test.tsx b/frontend/src/pages/EntryEditPage.test.tsx
index 171a12b2f..d687cee8c 100644
--- a/frontend/src/pages/EntryEditPage.test.tsx
+++ b/frontend/src/pages/EntryEditPage.test.tsx
@@ -10,8 +10,8 @@ import { createMemoryRouter, RouterProvider } from "react-router-dom";
import { EntryEditPage } from "./EntryEditPage";
-import { entryEditPath } from "Routes";
import { TestWrapperWithoutRoutes } from "TestWrapper";
+import { entryEditPath } from "routes/Routes";
const server = setupServer(
// getEntity
diff --git a/frontend/src/pages/EntryEditPage.tsx b/frontend/src/pages/EntryEditPage.tsx
index 8ea2ec41f..72e4f2f20 100644
--- a/frontend/src/pages/EntryEditPage.tsx
+++ b/frontend/src/pages/EntryEditPage.tsx
@@ -6,7 +6,6 @@ import { useNavigate } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { entityEntriesPath, entryDetailsPath } from "Routes";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
import { SubmitButton } from "components/common/SubmitButton";
@@ -21,6 +20,7 @@ import { useFormNotification } from "hooks/useFormNotification";
import { usePrompt } from "hooks/usePrompt";
import { useTypedParams } from "hooks/useTypedParams";
import { aironeApiClient } from "repository/AironeApiClient";
+import { entityEntriesPath, entryDetailsPath } from "routes/Routes";
import {
extractAPIException,
isResponseError,
diff --git a/frontend/src/pages/EntryHistoryListPage.test.tsx b/frontend/src/pages/EntryHistoryListPage.test.tsx
index 481b4b957..458e58f44 100644
--- a/frontend/src/pages/EntryHistoryListPage.test.tsx
+++ b/frontend/src/pages/EntryHistoryListPage.test.tsx
@@ -6,7 +6,7 @@ import { render, screen, act, waitFor } from "@testing-library/react";
import React from "react";
import { createMemoryRouter, RouterProvider } from "react-router-dom";
-import { showEntryHistoryPath } from "../Routes";
+import { showEntryHistoryPath } from "../routes/Routes";
import { EntryHistoryListPage } from "./EntryHistoryListPage";
diff --git a/frontend/src/pages/ForbiddenErrorPage.tsx b/frontend/src/pages/ForbiddenErrorPage.tsx
index ce671de1a..a91154269 100644
--- a/frontend/src/pages/ForbiddenErrorPage.tsx
+++ b/frontend/src/pages/ForbiddenErrorPage.tsx
@@ -1,7 +1,7 @@
import { Box, Button, Typography } from "@mui/material";
import React, { FC, useCallback } from "react";
-import { topPath } from "../Routes";
+import { topPath } from "../routes/Routes";
export const ForbiddenErrorPage: FC = () => {
const handleClickGoToTop = useCallback(() => {
diff --git a/frontend/src/pages/GroupEditPage.tsx b/frontend/src/pages/GroupEditPage.tsx
index cc6184ea7..20a423d80 100644
--- a/frontend/src/pages/GroupEditPage.tsx
+++ b/frontend/src/pages/GroupEditPage.tsx
@@ -6,7 +6,6 @@ import { Link, useNavigate } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { groupsPath, topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { PageHeader } from "components/common/PageHeader";
import { SubmitButton } from "components/common/SubmitButton";
@@ -16,6 +15,7 @@ import { useFormNotification } from "hooks/useFormNotification";
import { usePrompt } from "hooks/usePrompt";
import { useTypedParams } from "hooks/useTypedParams";
import { aironeApiClient } from "repository/AironeApiClient";
+import { groupsPath, topPath } from "routes/Routes";
import {
extractAPIException,
isResponseError,
diff --git a/frontend/src/pages/GroupListPage.tsx b/frontend/src/pages/GroupListPage.tsx
index ff15f5f3d..96e6afde0 100644
--- a/frontend/src/pages/GroupListPage.tsx
+++ b/frontend/src/pages/GroupListPage.tsx
@@ -18,11 +18,11 @@ import { GroupImportModal } from "../components/group/GroupImportModal";
import { GroupTreeRoot } from "../components/group/GroupTreeRoot";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { newGroupPath, topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
import { aironeApiClient } from "repository/AironeApiClient";
+import { newGroupPath, topPath } from "routes/Routes";
import { ServerContext } from "services/ServerContext";
const StyledBox = styled(Box)({
diff --git a/frontend/src/pages/JobListPage.tsx b/frontend/src/pages/JobListPage.tsx
index 248a841d8..a470f3a88 100644
--- a/frontend/src/pages/JobListPage.tsx
+++ b/frontend/src/pages/JobListPage.tsx
@@ -6,7 +6,6 @@ import { useToggle } from "react-use";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
@@ -14,6 +13,7 @@ import { PaginationFooter } from "components/common/PaginationFooter";
import { JobList } from "components/job/JobList";
import { usePage } from "hooks/usePage";
import { aironeApiClient } from "repository/AironeApiClient";
+import { topPath } from "routes/Routes";
import { JobList as ConstJobList } from "services/Constants";
export const JobListPage: FC = () => {
diff --git a/frontend/src/pages/NonTermsServiceAgreement.tsx b/frontend/src/pages/NonTermsServiceAgreement.tsx
index cf60aaa42..366803e3b 100644
--- a/frontend/src/pages/NonTermsServiceAgreement.tsx
+++ b/frontend/src/pages/NonTermsServiceAgreement.tsx
@@ -1,7 +1,7 @@
import { Box, Button, Typography } from "@mui/material";
import React, { FC } from "react";
-import { loginPath } from "../Routes";
+import { loginPath } from "../routes/Routes";
import { aironeApiClient } from "repository/AironeApiClient";
diff --git a/frontend/src/pages/NotFoundErrorPage.tsx b/frontend/src/pages/NotFoundErrorPage.tsx
index 73463ed5c..350150a6b 100644
--- a/frontend/src/pages/NotFoundErrorPage.tsx
+++ b/frontend/src/pages/NotFoundErrorPage.tsx
@@ -1,7 +1,7 @@
import { Box, Button, Typography } from "@mui/material";
import React, { FC, useCallback } from "react";
-import { topPath } from "../Routes";
+import { topPath } from "../routes/Routes";
export const NotFoundErrorPage: FC = () => {
const handleClickGoToTop = useCallback(() => {
diff --git a/frontend/src/pages/RoleEditPage.tsx b/frontend/src/pages/RoleEditPage.tsx
index d4dcd0aa1..6c8a7547f 100644
--- a/frontend/src/pages/RoleEditPage.tsx
+++ b/frontend/src/pages/RoleEditPage.tsx
@@ -7,7 +7,6 @@ import { Link, useNavigate } from "react-router-dom";
import { useAsyncWithThrow } from "../hooks/useAsyncWithThrow";
-import { rolesPath, topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { Loading } from "components/common/Loading";
import { PageHeader } from "components/common/PageHeader";
@@ -18,6 +17,7 @@ import { useFormNotification } from "hooks/useFormNotification";
import { usePrompt } from "hooks/usePrompt";
import { useTypedParams } from "hooks/useTypedParams";
import { aironeApiClient } from "repository/AironeApiClient";
+import { rolesPath, topPath } from "routes/Routes";
import {
extractAPIException,
isResponseError,
diff --git a/frontend/src/pages/RoleListPage.tsx b/frontend/src/pages/RoleListPage.tsx
index 89a8b3625..7b979a84d 100644
--- a/frontend/src/pages/RoleListPage.tsx
+++ b/frontend/src/pages/RoleListPage.tsx
@@ -7,9 +7,9 @@ import { RoleImportModal } from "../components/role/RoleImportModal";
import { RoleList } from "../components/role/RoleList";
import { aironeApiClient } from "../repository/AironeApiClient";
-import { newRolePath, topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { PageHeader } from "components/common/PageHeader";
+import { newRolePath, topPath } from "routes/Routes";
export const RoleListPage: FC = () => {
const [openImportModal, setOpenImportModal] = useState(false);
diff --git a/frontend/src/pages/TriggerEditPage.test.tsx b/frontend/src/pages/TriggerEditPage.test.tsx
index a9202d086..b8960eb64 100644
--- a/frontend/src/pages/TriggerEditPage.test.tsx
+++ b/frontend/src/pages/TriggerEditPage.test.tsx
@@ -8,7 +8,7 @@ import { setupServer } from "msw/node";
import React from "react";
import { createMemoryRouter, RouterProvider } from "react-router-dom";
-import { editTriggerPath } from "../Routes";
+import { editTriggerPath } from "../routes/Routes";
import { TriggerEditPage } from "./TriggerEditPage";
diff --git a/frontend/src/pages/TriggerEditPage.tsx b/frontend/src/pages/TriggerEditPage.tsx
index ca9ddc533..52aaab10f 100644
--- a/frontend/src/pages/TriggerEditPage.tsx
+++ b/frontend/src/pages/TriggerEditPage.tsx
@@ -22,7 +22,6 @@ import { Link, useNavigate } from "react-router-dom";
import type { TriggerActionUpdate } from "@dmm-com/airone-apiclient-typescript-fetch/src/autogenerated/models/TriggerActionUpdate";
-import { topPath, triggersPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { PageHeader } from "components/common/PageHeader";
import { SubmitButton } from "components/common/SubmitButton";
@@ -34,6 +33,7 @@ import { useFormNotification } from "hooks/useFormNotification";
import { usePrompt } from "hooks/usePrompt";
import { useTypedParams } from "hooks/useTypedParams";
import { aironeApiClient } from "repository/AironeApiClient";
+import { topPath, triggersPath } from "routes/Routes";
const StyledFlexColumnBox = styled(Box)({
display: "flex",
diff --git a/frontend/src/pages/TriggerListPage.tsx b/frontend/src/pages/TriggerListPage.tsx
index fd8cd6bd7..06270029e 100644
--- a/frontend/src/pages/TriggerListPage.tsx
+++ b/frontend/src/pages/TriggerListPage.tsx
@@ -32,18 +32,18 @@ import React, { FC, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useAsync } from "react-use";
+import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
+import { Confirmable } from "components/common/Confirmable";
+import { Loading } from "components/common/Loading";
+import { PageHeader } from "components/common/PageHeader";
+import { aironeApiClient } from "repository/AironeApiClient";
import {
editTriggerPath,
entryDetailsPath,
newTriggerPath,
topPath,
triggersPath,
-} from "Routes";
-import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
-import { Confirmable } from "components/common/Confirmable";
-import { Loading } from "components/common/Loading";
-import { PageHeader } from "components/common/PageHeader";
-import { aironeApiClient } from "repository/AironeApiClient";
+} from "routes/Routes";
const StyledList = styled(List)(() => ({
padding: "0",
diff --git a/frontend/src/pages/UnavailableErrorPage.tsx b/frontend/src/pages/UnavailableErrorPage.tsx
index cedb906b4..e997e7fd1 100644
--- a/frontend/src/pages/UnavailableErrorPage.tsx
+++ b/frontend/src/pages/UnavailableErrorPage.tsx
@@ -1,7 +1,7 @@
import { Box, Button, Typography } from "@mui/material";
import React, { FC, useCallback } from "react";
-import { topPath } from "../Routes";
+import { topPath } from "../routes/Routes";
export const UnavailableErrorPage: FC = () => {
const handleClickGoToTop = useCallback(() => {
diff --git a/frontend/src/pages/UserEditPage.tsx b/frontend/src/pages/UserEditPage.tsx
index 146206226..3d95dd048 100644
--- a/frontend/src/pages/UserEditPage.tsx
+++ b/frontend/src/pages/UserEditPage.tsx
@@ -6,7 +6,6 @@ import { useForm } from "react-hook-form";
import { Link, useNavigate } from "react-router-dom";
import { useToggle } from "react-use";
-import { topPath, usersPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { Confirmable } from "components/common/Confirmable";
import { Loading } from "components/common/Loading";
@@ -19,6 +18,7 @@ import { useFormNotification } from "hooks/useFormNotification";
import { usePrompt } from "hooks/usePrompt";
import { useTypedParams } from "hooks/useTypedParams";
import { aironeApiClient } from "repository/AironeApiClient";
+import { topPath, usersPath } from "routes/Routes";
import {
extractAPIException,
isResponseError,
diff --git a/frontend/src/pages/UserListPage.tsx b/frontend/src/pages/UserListPage.tsx
index 7db2ea99f..ff458d1a8 100644
--- a/frontend/src/pages/UserListPage.tsx
+++ b/frontend/src/pages/UserListPage.tsx
@@ -2,12 +2,12 @@ import { Box, Button, Container, Typography } from "@mui/material";
import React, { FC, useCallback, useState } from "react";
import { Link } from "react-router-dom";
-import { topPath } from "Routes";
import { AironeBreadcrumbs } from "components/common/AironeBreadcrumbs";
import { PageHeader } from "components/common/PageHeader";
import { UserImportModal } from "components/user/UserImportModal";
import { UserList } from "components/user/UserList";
import { aironeApiClient } from "repository/AironeApiClient";
+import { topPath } from "routes/Routes";
export const UserListPage: FC = () => {
const [openImportModal, setOpenImportModal] = useState(false);
diff --git a/frontend/src/pages/index.ts b/frontend/src/pages/index.ts
new file mode 100644
index 000000000..9ff84e203
--- /dev/null
+++ b/frontend/src/pages/index.ts
@@ -0,0 +1,8 @@
+// FIXME rethink export scope
+
+export * from "./EntryCopyPage";
+export * from "./EntryEditPage";
+export * from "./EntryEditPage";
+export * from "./EntryListPage";
+
+export * from "./UnavailableErrorPage";
diff --git a/frontend/src/repository/index.ts b/frontend/src/repository/index.ts
new file mode 100644
index 000000000..5b950f070
--- /dev/null
+++ b/frontend/src/repository/index.ts
@@ -0,0 +1,2 @@
+export * from "./AironeApiClient";
+export * from "./LocalStorageUtil";
diff --git a/frontend/src/AppRouter.tsx b/frontend/src/routes/AppRouter.tsx
similarity index 91%
rename from frontend/src/AppRouter.tsx
rename to frontend/src/routes/AppRouter.tsx
index 81fa2cdba..2c1ccf30b 100644
--- a/frontend/src/AppRouter.tsx
+++ b/frontend/src/routes/AppRouter.tsx
@@ -7,14 +7,33 @@ import {
RouterProvider,
} from "react-router-dom";
-import { ACLHistoryPage } from "./pages/ACLHistoryPage";
-import { EntryCopyPage } from "./pages/EntryCopyPage";
-import { EntryDetailsPage } from "./pages/EntryDetailsPage";
-import { EntryRestorePage } from "./pages/EntryRestorePage";
-import { NotFoundErrorPage } from "./pages/NotFoundErrorPage";
-import { RoleEditPage } from "./pages/RoleEditPage";
-import { RoleListPage } from "./pages/RoleListPage";
+import { ACLHistoryPage } from "../pages/ACLHistoryPage";
+import { EntryCopyPage } from "../pages/EntryCopyPage";
+import { EntryDetailsPage } from "../pages/EntryDetailsPage";
+import { EntryRestorePage } from "../pages/EntryRestorePage";
+import { NotFoundErrorPage } from "../pages/NotFoundErrorPage";
+import { RoleEditPage } from "../pages/RoleEditPage";
+import { RoleListPage } from "../pages/RoleListPage";
+import { Header } from "components/common/Header";
+import { ACLEditPage } from "pages/ACLEditPage";
+import { AdvancedSearchPage } from "pages/AdvancedSearchPage";
+import { AdvancedSearchResultsPage } from "pages/AdvancedSearchResultsPage";
+import { DashboardPage } from "pages/DashboardPage";
+import { EntityEditPage } from "pages/EntityEditPage";
+import { EntityHistoryPage } from "pages/EntityHistoryPage";
+import { EntityListPage } from "pages/EntityListPage";
+import { EntryEditPage } from "pages/EntryEditPage";
+import { EntryHistoryListPage } from "pages/EntryHistoryListPage";
+import { EntryListPage } from "pages/EntryListPage";
+import { GroupEditPage } from "pages/GroupEditPage";
+import { GroupListPage } from "pages/GroupListPage";
+import { JobListPage } from "pages/JobListPage";
+import { LoginPage } from "pages/LoginPage";
+import { TriggerEditPage } from "pages/TriggerEditPage";
+import { TriggerListPage } from "pages/TriggerListPage";
+import { UserEditPage } from "pages/UserEditPage";
+import { UserListPage } from "pages/UserListPage";
import {
aclHistoryPath,
aclPath,
@@ -46,26 +65,7 @@ import {
triggersPath,
userPath,
usersPath,
-} from "Routes";
-import { Header } from "components/Header";
-import { ACLEditPage } from "pages/ACLEditPage";
-import { AdvancedSearchPage } from "pages/AdvancedSearchPage";
-import { AdvancedSearchResultsPage } from "pages/AdvancedSearchResultsPage";
-import { DashboardPage } from "pages/DashboardPage";
-import { EntityEditPage } from "pages/EntityEditPage";
-import { EntityHistoryPage } from "pages/EntityHistoryPage";
-import { EntityListPage } from "pages/EntityListPage";
-import { EntryEditPage } from "pages/EntryEditPage";
-import { EntryHistoryListPage } from "pages/EntryHistoryListPage";
-import { EntryListPage } from "pages/EntryListPage";
-import { GroupEditPage } from "pages/GroupEditPage";
-import { GroupListPage } from "pages/GroupListPage";
-import { JobListPage } from "pages/JobListPage";
-import { LoginPage } from "pages/LoginPage";
-import { TriggerEditPage } from "pages/TriggerEditPage";
-import { TriggerListPage } from "pages/TriggerListPage";
-import { UserEditPage } from "pages/UserEditPage";
-import { UserListPage } from "pages/UserListPage";
+} from "routes/Routes";
interface Props {
customRoutes?: {
diff --git a/frontend/src/Routes.ts b/frontend/src/routes/Routes.ts
similarity index 100%
rename from frontend/src/Routes.ts
rename to frontend/src/routes/Routes.ts
diff --git a/frontend/src/routes/index.ts b/frontend/src/routes/index.ts
new file mode 100644
index 000000000..696c25e2b
--- /dev/null
+++ b/frontend/src/routes/index.ts
@@ -0,0 +1,2 @@
+export * from "./AppRouter";
+export * from "./Routes";
diff --git a/frontend/src/services/index.ts b/frontend/src/services/index.ts
new file mode 100644
index 000000000..1639a321b
--- /dev/null
+++ b/frontend/src/services/index.ts
@@ -0,0 +1,7 @@
+export * from "./AironeAPIErrorUtil";
+export * from "./Constants";
+export * from "./DateUtil";
+export * from "./JobUtil";
+export * from "./ServerContext";
+export * from "./StringUtil";
+export * from "./ZodSchemaUtil";
diff --git a/package.json b/package.json
index 9e92b859e..10cef4c97 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,18 @@
{
- "name": "airone",
+ "name": "@syucream/pagoda-core",
"version": "1.0.0",
"description": "[![CircleCI](https://circleci.com/gh/dmm-com/airone.svg?style=svg&circle-token=2e12c068b0ed1bab9d0c2d72529d5ee1da9b53b4)](https://circleci.com/gh/dmm-com/airone)",
- "main": "App.js",
+ "main": "frontend/dist/pagoda-core.js",
+ "types": "frontend/dist/index.d.ts",
"directories": {
"doc": "docs"
},
"scripts": {
"build": "npm run build:development",
- "build:development": "webpack build --mode development",
- "build:development:analyze": "webpack build --mode development --analyze",
- "build:production": "webpack build --mode production",
- "build:production:analyze": "webpack build --mode production --analyze",
+ "build:development": "webpack build --entry ./frontend/src/App.tsx --mode development",
+ "build:development:analyze": "webpack build --entry ./frontend/src/App.tsx --mode development --analyze",
+ "build:production": "webpack build --entry ./frontend/src/App.tsx --mode production",
+ "build:production:analyze": "webpack build --entry ./frontend/src/App.tsx --mode production --analyze",
"build:custom": "npm run build:custom_development",
"build:custom_development": "webpack build --entry ./frontend/src/customview/CustomApp.tsx --mode development",
"build:custom_production": "webpack build --entry ./frontend/src/customview/CustomApp.tsx --mode production",
@@ -20,17 +21,26 @@
"generate:client_docker": "./tools/generate_client.sh apiclient/typescript-fetch/src/autogenerated --docker",
"generate:custom_client_docker": "export DJANGO_CONFIGURATION=DRFSpectacularCustomView && ./tools/generate_client.sh frontend/src/apiclient/autogenerated --docker",
"link:client": "cd apiclient/typescript-fetch/ && npm link && cd ../../ && npm link '@dmm-com/airone-apiclient-typescript-fetch'",
- "watch": "webpack build --watch --mode development",
+ "watch": "webpack build --entry ./frontend/src/App.tsx --watch --mode development",
"watch:custom": "webpack build --watch --entry ./frontend/src/customview/CustomApp.tsx --mode development",
"lint": "npx eslint frontend && npx prettier --check frontend",
"fix": "npx eslint --fix frontend ; npx prettier --write frontend",
"test": "TZ=UTC npx jest --coverage frontend",
- "test:update": "TZ=UTC npx jest -u frontend"
+ "test:update": "TZ=UTC npx jest -u frontend",
+ "build:lib": "npm run build:lib:code && npm run build:lib:types",
+ "build:lib:code": "webpack --config webpack.library.config.js",
+ "build:lib:types": "tsc --project tsconfig.lib.json"
},
"repository": {
"type": "git",
- "url": "git+https://github.com/userlocalhost/airone.git"
+ "url": "git+https://github.com/syucream/airone.git"
},
+ "publishConfig": {
+ "registry": "https://npm.pkg.github.com/"
+ },
+ "files": [
+ "frontend/dist"
+ ],
"keywords": [],
"author": "Hiroyasu OHYAMA",
"license": "GPLv2",
diff --git a/tsconfig.lib.json b/tsconfig.lib.json
new file mode 100644
index 000000000..3c01b4a5c
--- /dev/null
+++ b/tsconfig.lib.json
@@ -0,0 +1,13 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "frontend/dist",
+ "declaration": true,
+ "declarationDir": "frontend/dist",
+ "emitDeclarationOnly": true,
+ "isolatedModules": true,
+ "skipLibCheck": true
+ },
+ "include": ["frontend/src/**/*"],
+ "exclude": ["node_modules", "**/*.test.ts", "**/*.test.tsx"]
+}
\ No newline at end of file
diff --git a/webpack.library.config.js b/webpack.library.config.js
new file mode 100644
index 000000000..bdbd798e0
--- /dev/null
+++ b/webpack.library.config.js
@@ -0,0 +1,42 @@
+const path = require('path');
+
+module.exports = {
+ mode: 'production',
+ entry: './frontend/src/index.ts',
+ output: {
+ path: path.resolve(__dirname, 'frontend/dist'),
+ filename: 'pagoda-core.js',
+ library: {
+ name: 'Pagoda Core Module',
+ type: 'umd',
+ },
+ globalObject: 'this',
+ },
+ module: {
+ rules: [
+ {
+ test: /\.tsx?$/,
+ exclude: /node_modules/,
+ loader: 'ts-loader',
+ options: {
+ transpileOnly: true,
+ }
+ },
+ {
+ test: /\.ts$/,
+ include: /node_modules\/@dmm-com\/airone-apiclient-typescript-fetch/,
+ loader: 'ts-loader',
+ options: {
+ transpileOnly: true,
+ }
+ },
+ ],
+ },
+ resolve: {
+ extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"],
+ modules: [
+ path.resolve(__dirname, 'frontend/src'),
+ 'node_modules',
+ ],
+ },
+}
\ No newline at end of file