From 3bd157b1319146daae8ed15fb385c4ed1fd26e8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20K=C3=BCnzel?= <simonk@fsmpi.rwth-aachen.de>
Date: Sat, 10 May 2025 03:01:37 +0200
Subject: [PATCH] Simplify imports

---
 src/pages/404.tsx                             |  2 +-
 src/pages/_app.tsx                            | 16 ++++-----
 src/pages/courses.tsx                         | 17 ++++-----
 src/pages/dynamic_course_listing.tsx          | 11 +++---
 src/pages/dynamic_player.tsx                  | 12 +++----
 src/pages/feedback.tsx                        |  8 ++---
 src/pages/index.tsx                           | 28 ++++++++-------
 src/pages/internal/changelog.tsx              | 17 ++++-----
 src/pages/internal/import.tsx                 |  9 +++--
 src/pages/internal/jobs.tsx                   | 23 ++++++------
 src/pages/internal/sorter_files.tsx           | 24 +++++++------
 src/pages/internal/timetable.tsx              |  2 +-
 src/pages/internal/user.tsx                   | 10 +++---
 src/pages/search.tsx                          | 11 +++---
 src/videoag/api/index.ts                      |  3 ++
 src/videoag/authentication/AuthStatus.tsx     |  2 +-
 .../authentication/ModeratorBarrier.tsx       |  6 ++--
 src/videoag/authentication/UserField.tsx      | 13 ++++---
 .../authentication/ViewPermissions.tsx        | 13 +++----
 src/videoag/authentication/index.ts           |  4 +++
 src/videoag/course/Chapter.tsx                | 18 +++++-----
 src/videoag/course/CourseListing.tsx          | 12 +++----
 src/videoag/course/DownloadManager.tsx        |  7 ++--
 src/videoag/course/Lecture.tsx                | 11 +++---
 src/videoag/course/LiveLabel.tsx              |  2 +-
 src/videoag/course/Medium.tsx                 | 25 ++++++-------
 src/videoag/course/Player.tsx                 | 16 ++++-----
 .../course/{Stats.tsx => StatTracker.tsx}     |  5 ++-
 src/videoag/course/index.ts                   | 12 +++++++
 src/videoag/error/ErrorDisplay.tsx            |  4 +--
 src/videoag/error/FallbackErrorBoundary.tsx   |  2 +-
 src/videoag/error/index.ts                    |  8 +++++
 src/videoag/form/TypeEditor.tsx               |  4 +--
 src/videoag/form/index.ts                     |  9 +++++
 src/videoag/job/Job.tsx                       | 12 ++++---
 src/videoag/job/index.ts                      |  1 +
 src/videoag/localization/LanguageProvider.tsx |  4 +--
 src/videoag/localization/i18n.tsx             |  2 +-
 src/videoag/localization/index.ts             |  2 ++
 src/videoag/miscellaneous/Theme.tsx           |  2 +-
 src/videoag/miscellaneous/index.ts            | 35 +++++++++++++++++++
 .../object_management/FilteredData.tsx        |  5 ++-
 .../object_management/OMConfigComponent.tsx   | 23 ++++++------
 .../object_management/OMFieldEditor.tsx       |  2 +-
 .../object_management/ObjectEditor.tsx        |  6 ++--
 .../ViewPermissionsEditor.tsx                 |  6 ++--
 src/videoag/object_management/index.ts        | 10 ++++++
 src/videoag/site/AnnouncementComponent.tsx    | 10 +++---
 src/videoag/site/DefaultLayout.tsx            | 18 ++++------
 src/videoag/site/index.ts                     |  2 ++
 50 files changed, 281 insertions(+), 225 deletions(-)
 create mode 100644 src/videoag/api/index.ts
 create mode 100644 src/videoag/authentication/index.ts
 rename src/videoag/course/{Stats.tsx => StatTracker.tsx} (99%)
 create mode 100644 src/videoag/course/index.ts
 create mode 100644 src/videoag/error/index.ts
 create mode 100644 src/videoag/form/index.ts
 create mode 100644 src/videoag/job/index.ts
 create mode 100644 src/videoag/localization/index.ts
 create mode 100644 src/videoag/miscellaneous/index.ts
 create mode 100644 src/videoag/object_management/index.ts
 create mode 100644 src/videoag/site/index.ts

diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 23f20a3..73386e6 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -1,4 +1,4 @@
-import { Four04 } from "@/videoag/error/ErrorDisplay";
+import { Four04 } from "@/videoag/error";
 
 export default function Four04Page() {
     return <Four04 />;
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 86505ff..2ecfa31 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -2,15 +2,13 @@ import type { AppProps } from "next/app";
 import { ToastContainer } from "react-toastify";
 import Head from "next/head";
 
-import { AuthStatusProvider } from "@/videoag/authentication/AuthStatus";
-import { showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { LanguageProvider } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ThemeProvider } from "@/videoag/miscellaneous/Theme";
-import Title from "@/videoag/miscellaneous/TitleComponent";
-import { RealBackendProvider } from "@/videoag/api/ApiProvider";
-import { EditModeProvider } from "@/videoag/object_management/EditModeProvider";
-import DefaultLayout from "@/videoag/site/DefaultLayout";
+import { RealBackendProvider } from "@/videoag/api";
+import { AuthStatusProvider } from "@/videoag/authentication";
+import { showErrorToast } from "@/videoag/error";
+import { LanguageProvider } from "@/videoag/localization";
+import { ReloadBoundary, ThemeProvider, Title } from "@/videoag/miscellaneous";
+import { EditModeProvider } from "@/videoag/object_management";
+import { DefaultLayout } from "@/videoag/site";
 import "@/styles/globals.scss";
 
 export default function App({ Component, pageProps }: AppProps) {
diff --git a/src/pages/courses.tsx b/src/pages/courses.tsx
index ffa6b8d..0908efe 100644
--- a/src/pages/courses.tsx
+++ b/src/pages/courses.tsx
@@ -4,20 +4,17 @@ import DropdownButton from "react-bootstrap/DropdownButton";
 import DropdownItem from "react-bootstrap/DropdownItem";
 
 import type { GetCoursesResponse, course } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { semesterToHuman } from "@/videoag/miscellaneous/Formatting";
-import { UpdateOverlay } from "@/videoag/miscellaneous/UpdateOverlay";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, semesterToHuman, UpdateOverlay } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMEdit,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 
 type GroupByTypes = "semester" | "full_name" | "organizer" | "topic";
 
diff --git a/src/pages/dynamic_course_listing.tsx b/src/pages/dynamic_course_listing.tsx
index d95a3ed..bad62fd 100644
--- a/src/pages/dynamic_course_listing.tsx
+++ b/src/pages/dynamic_course_listing.tsx
@@ -2,12 +2,11 @@ import { useEffect, useRef, useState, useTransition } from "react";
 import { usePathname } from "next/navigation";
 
 import { GetCourseResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import CourseListing from "@/videoag/course/CourseListing";
-import { Four04, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { CourseListing } from "@/videoag/course";
+import { Four04, ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { ReloadBoundary } from "@/videoag/miscellaneous";
 
 function ActualRedirector() {
     const api = useApi();
diff --git a/src/pages/dynamic_player.tsx b/src/pages/dynamic_player.tsx
index 89946e4..4c98c8a 100644
--- a/src/pages/dynamic_player.tsx
+++ b/src/pages/dynamic_player.tsx
@@ -2,13 +2,11 @@ import { useEffect, useRef, useState, useTransition } from "react";
 import { usePathname } from "next/navigation";
 
 import { course, lecture } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { Backend } from "@/videoag/api/Backend";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import Player, { EmbeddedPlayer } from "@/videoag/course/Player";
-import { Four04, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi, Backend } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { Player, EmbeddedPlayer } from "@/videoag/course";
+import { Four04, ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { ReloadBoundary } from "@/videoag/miscellaneous";
 
 export interface PlayerData {
     course: course;
diff --git a/src/pages/feedback.tsx b/src/pages/feedback.tsx
index 60cf67e..7c72ade 100644
--- a/src/pages/feedback.tsx
+++ b/src/pages/feedback.tsx
@@ -2,10 +2,10 @@ import type React from "react";
 import { useState } from "react";
 import Link from "next/link";
 
-import { useApi } from "@/videoag/api/ApiProvider";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { StringEditor, BooleanEditor } from "@/videoag/form/TypeEditor";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
+import { useApi } from "@/videoag/api";
+import { showError } from "@/videoag/error";
+import { StringEditor, BooleanEditor } from "@/videoag/form";
+import { useLanguage } from "@/videoag/localization";
 
 export default function Feedback() {
     const api = useApi();
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index f1451cd..bfc6eed 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -4,28 +4,32 @@ import { useSearchParams } from "next/navigation";
 import { useRouter } from "next/router";
 
 import type { GetHomepageResponse, featured, int } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { LectureCard } from "@/videoag/course/Lecture";
-import { LectureLiveLabel } from "@/videoag/course/LiveLabel";
-import { ErrorComponent, showError, showWarningToast } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { LectureCard, LectureLiveLabel } from "@/videoag/course";
+import {
+    ErrorComponent,
+    showError,
+    showWarningToast,
+    FallbackErrorBoundary,
+} from "@/videoag/error";
 import { useLanguage } from "@/videoag/localization/LanguageProvider";
 import {
     datetimeToStringOnlyDate,
     datetimeToStringOnlyTime,
-} from "@/videoag/miscellaneous/Formatting";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { useDebounceWithArgument } from "@/videoag/miscellaneous/PromiseHelpers";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+    ReloadBoundary,
+    useReloadBoundary,
+    useDebounceWithArgument,
+} from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
-import { Search } from "@/videoag/site/DefaultLayout";
+} from "@/videoag/object_management";
+import { Search } from "@/videoag/site";
 import { basePath } from "@/../basepath";
 
 import { SearchResults } from "./search";
diff --git a/src/pages/internal/changelog.tsx b/src/pages/internal/changelog.tsx
index 8f15157..68ae0f6 100644
--- a/src/pages/internal/changelog.tsx
+++ b/src/pages/internal/changelog.tsx
@@ -2,17 +2,12 @@ import { useEffect, useState } from "react";
 import { DateTime } from "luxon";
 
 import type { changelog_entry, GetChangelogResponse, int, object_type } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { OBJECT_TYPES } from "@/videoag/api/Backend";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { ChooserEditor, StringEditor, IntEditor } from "@/videoag/form/TypeEditor";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import {
-    useFilteredDataView,
-    PagingNavigation,
-    FilterInput,
-} from "@/videoag/object_management/FilteredData";
+import { useApi, OBJECT_TYPES } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError, ErrorComponent } from "@/videoag/error";
+import { ChooserEditor, StringEditor, IntEditor } from "@/videoag/form";
+import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous";
+import { useFilteredDataView, PagingNavigation, FilterInput } from "@/videoag/object_management";
 
 function ValToStr({ val }: { val: any }) {
     const [expand, setExpand] = useState(false);
diff --git a/src/pages/internal/import.tsx b/src/pages/internal/import.tsx
index aa9cfd0..ba7cf29 100644
--- a/src/pages/internal/import.tsx
+++ b/src/pages/internal/import.tsx
@@ -4,11 +4,10 @@ import { useSearchParams } from "next/navigation";
 import { DateTime } from "luxon";
 
 import type { GetNewConfigurationResponse, course } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError, showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ICalEvent, parseICal } from "@/videoag/miscellaneous/Calendar";
+import { useApi } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError, showErrorToast } from "@/videoag/error";
+import { ICalEvent, parseICal, ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous";
 
 function TerminBody({
     course,
diff --git a/src/pages/internal/jobs.tsx b/src/pages/internal/jobs.tsx
index 357bb04..aa31745 100644
--- a/src/pages/internal/jobs.tsx
+++ b/src/pages/internal/jobs.tsx
@@ -1,18 +1,15 @@
 import type { int, job_state, GetJobsResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { JOB_STATES } from "@/videoag/api/Backend";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { StringEditor, ChooserEditor } from "@/videoag/form/TypeEditor";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ExpandableString } from "@/videoag/miscellaneous/ExpandableString";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { Spinner } from "@/videoag/miscellaneous/Util";
+import { useApi, JOB_STATES } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { ErrorComponent } from "@/videoag/error";
+import { StringEditor, ChooserEditor } from "@/videoag/form";
 import {
-    useFilteredDataView,
-    PagingNavigation,
-    FilterInput,
-} from "@/videoag/object_management/FilteredData";
+    ReloadBoundary,
+    ExpandableString,
+    datetimeToString,
+    Spinner,
+} from "@/videoag/miscellaneous";
+import { useFilteredDataView, PagingNavigation, FilterInput } from "@/videoag/object_management";
 
 function JobList({ jobsResp }: { jobsResp: GetJobsResponse }) {
     return (
diff --git a/src/pages/internal/sorter_files.tsx b/src/pages/internal/sorter_files.tsx
index 4d9aa96..2b6044d 100644
--- a/src/pages/internal/sorter_files.tsx
+++ b/src/pages/internal/sorter_files.tsx
@@ -2,21 +2,23 @@ import { useState } from "react";
 import Link from "next/link";
 
 import type { int, GetSorterFilesResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { BooleanEditor } from "@/videoag/form/TypeEditor";
-import { JobStatusCard } from "@/videoag/job/Job";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ExpandableString } from "@/videoag/miscellaneous/ExpandableString";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { showInfoToast } from "@/videoag/miscellaneous/Util";
+import { useApi } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError, ErrorComponent } from "@/videoag/error";
+import { BooleanEditor } from "@/videoag/form";
+import { JobStatusCard } from "@/videoag/job";
+import {
+    ReloadBoundary,
+    ExpandableString,
+    datetimeToString,
+    showInfoToast,
+} from "@/videoag/miscellaneous";
 import {
     useFilteredDataView,
     PagingNavigation,
     FilterInput,
-} from "@/videoag/object_management/FilteredData";
-import { EmbeddedOMFieldComponent } from "@/videoag/object_management/OMConfigComponent";
+    EmbeddedOMFieldComponent,
+} from "@/videoag/object_management";
 
 function SorterFileList({ sorterFilesResp }: { sorterFilesResp: GetSorterFilesResponse }) {
     return (
diff --git a/src/pages/internal/timetable.tsx b/src/pages/internal/timetable.tsx
index a314687..9473039 100644
--- a/src/pages/internal/timetable.tsx
+++ b/src/pages/internal/timetable.tsx
@@ -1,7 +1,7 @@
 import Link from "next/link";
 import { DateTime } from "luxon";
 
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
+import { ModeratorBarrier } from "@/videoag/authentication";
 
 interface Days {
     index: number;
diff --git a/src/pages/internal/user.tsx b/src/pages/internal/user.tsx
index 2233cce..7ed93cf 100644
--- a/src/pages/internal/user.tsx
+++ b/src/pages/internal/user.tsx
@@ -2,11 +2,11 @@ import type React from "react";
 import { useEffect, useState } from "react";
 
 import type { GetMySettingsResponse, settings_entry, settings_section } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous";
 
 function BoolEntry({ entry, disabled }: { entry: settings_entry; disabled: boolean }) {
     const reloadFunc = useReloadBoundary();
diff --git a/src/pages/search.tsx b/src/pages/search.tsx
index 8345e46..80e17ab 100644
--- a/src/pages/search.tsx
+++ b/src/pages/search.tsx
@@ -4,12 +4,11 @@ import { useSearchParams } from "next/navigation";
 import { useRouter } from "next/router";
 
 import type { SearchResponse, course, lecture } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { LectureListItem } from "@/videoag/course/Lecture";
-import { ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { useDebounce } from "@/videoag/miscellaneous/PromiseHelpers";
+import { useApi } from "@/videoag/api";
+import { LectureListItem } from "@/videoag/course";
+import { ErrorComponent } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, useDebounce } from "@/videoag/miscellaneous";
 
 import { CourseItem } from "./courses";
 
diff --git a/src/videoag/api/index.ts b/src/videoag/api/index.ts
new file mode 100644
index 0000000..0ed895c
--- /dev/null
+++ b/src/videoag/api/index.ts
@@ -0,0 +1,3 @@
+export { ApiError } from "./ApiError";
+export { RealBackendProvider, useApi } from "./ApiProvider";
+export { JOB_STATES, OBJECT_TYPES, type Backend } from "./Backend";
diff --git a/src/videoag/authentication/AuthStatus.tsx b/src/videoag/authentication/AuthStatus.tsx
index d2ab43c..ae29757 100644
--- a/src/videoag/authentication/AuthStatus.tsx
+++ b/src/videoag/authentication/AuthStatus.tsx
@@ -2,7 +2,7 @@ import type React from "react";
 import { createContext, useContext, useState, useEffect, startTransition } from "react";
 
 import { int, user } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
+import { useApi } from "@/videoag/api";
 
 interface AuthStatusData {
     user: user | null;
diff --git a/src/videoag/authentication/ModeratorBarrier.tsx b/src/videoag/authentication/ModeratorBarrier.tsx
index 83435b8..a100085 100644
--- a/src/videoag/authentication/ModeratorBarrier.tsx
+++ b/src/videoag/authentication/ModeratorBarrier.tsx
@@ -1,8 +1,8 @@
-import { Four04 } from "@/videoag/error/ErrorDisplay";
-import { useAuthStatus } from "./AuthStatus";
-
 import type React from "react";
 
+import { Four04 } from "@/videoag/error";
+import { useAuthStatus } from "./AuthStatus";
+
 export default function ModeratorBarrier({ children }: { children: React.ReactNode }) {
     const authStatus = useAuthStatus();
 
diff --git a/src/videoag/authentication/UserField.tsx b/src/videoag/authentication/UserField.tsx
index e4206bc..518839a 100644
--- a/src/videoag/authentication/UserField.tsx
+++ b/src/videoag/authentication/UserField.tsx
@@ -3,13 +3,12 @@ import type React from "react";
 import { useState } from "react";
 import { Dropdown, Popover, OverlayTrigger } from "react-bootstrap";
 
-import { useApi } from "@/videoag/api/ApiProvider";
-import { ApiError } from "@/videoag/api/ApiError";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { TooltipButton } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi, ApiError } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { showError } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { TooltipButton } from "@/videoag/miscellaneous";
+import { useEditMode } from "@/videoag/object_management";
 
 export default function UserField({ isUnavailable }: { isUnavailable: boolean }) {
     const api = useApi();
diff --git a/src/videoag/authentication/ViewPermissions.tsx b/src/videoag/authentication/ViewPermissions.tsx
index bd1cbac..a666437 100644
--- a/src/videoag/authentication/ViewPermissions.tsx
+++ b/src/videoag/authentication/ViewPermissions.tsx
@@ -2,14 +2,11 @@ import type React from "react";
 import { useEffect, useRef, useState } from "react";
 
 import { course, lecture } from "@/videoag/api/types";
-import { ApiError } from "@/videoag/api/ApiError";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { showError, showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { useDebouncedCall } from "@/videoag/miscellaneous/PromiseHelpers";
+import { ApiError, useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { showError, showErrorToast } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { useReloadBoundary, StylizedText, useDebouncedCall } from "@/videoag/miscellaneous";
 
 export function AuthenticationMethodIcons({
     authentication_methods,
diff --git a/src/videoag/authentication/index.ts b/src/videoag/authentication/index.ts
new file mode 100644
index 0000000..f2b34ab
--- /dev/null
+++ b/src/videoag/authentication/index.ts
@@ -0,0 +1,4 @@
+export { AuthStatus, AuthStatusProvider, useAuthStatus } from "./AuthStatus";
+export { default as ModeratorBarrier } from "./ModeratorBarrier";
+export { default as UserField } from "./UserField";
+export { AuthenticationMethodIcons, ViewPermissionAuthorization } from "./ViewPermissions";
diff --git a/src/videoag/course/Chapter.tsx b/src/videoag/course/Chapter.tsx
index e397aa2..0b54c36 100644
--- a/src/videoag/course/Chapter.tsx
+++ b/src/videoag/course/Chapter.tsx
@@ -3,21 +3,19 @@ import { useEffect, useRef, useState } from "react";
 import { Popover, OverlayTrigger } from "react-bootstrap";
 
 import { int, chapter } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { StringEditor } from "@/videoag/form/TypeEditor";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { timestampToString } from "@/videoag/miscellaneous/Formatting";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { showInfoToast } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { showError } from "@/videoag/error";
+import { StringEditor } from "@/videoag/form";
+import { useLanguage } from "@/videoag/localization";
+import { timestampToString, useReloadBoundary, showInfoToast } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 
 export function Chapters({
     chapters,
diff --git a/src/videoag/course/CourseListing.tsx b/src/videoag/course/CourseListing.tsx
index 3488d34..a8247c8 100644
--- a/src/videoag/course/CourseListing.tsx
+++ b/src/videoag/course/CourseListing.tsx
@@ -1,19 +1,17 @@
 import Link from "next/link";
 
 import type { GetCourseResponse } from "@/videoag/api/types";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { AuthenticationMethodIcons } from "@/videoag/authentication/ViewPermissions";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import Title from "@/videoag/miscellaneous/TitleComponent";
-import { UpdateOverlay } from "@/videoag/miscellaneous/UpdateOverlay";
+import { useAuthStatus, AuthenticationMethodIcons } from "@/videoag/authentication";
+import { useLanguage } from "@/videoag/localization";
+import { Title, UpdateOverlay } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+} from "@/videoag/object_management";
 
 import { LectureListItem } from "./Lecture";
 import DownloadAllModal from "./DownloadManager";
diff --git a/src/videoag/course/DownloadManager.tsx b/src/videoag/course/DownloadManager.tsx
index 1da1616..4df2b5b 100644
--- a/src/videoag/course/DownloadManager.tsx
+++ b/src/videoag/course/DownloadManager.tsx
@@ -4,10 +4,9 @@ import DropdownButton from "react-bootstrap/DropdownButton";
 import DropdownItem from "react-bootstrap/DropdownItem";
 
 import type { int, course, lecture } from "@/videoag/api/types";
-import { showWarningToast, showError } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { filesizeToHuman } from "@/videoag/miscellaneous/Formatting";
+import { showWarningToast, showError } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { StylizedText, filesizeToHuman } from "@/videoag/miscellaneous";
 
 import {
     getNamedPublishMedia,
diff --git a/src/videoag/course/Lecture.tsx b/src/videoag/course/Lecture.tsx
index 368b349..13125ee 100644
--- a/src/videoag/course/Lecture.tsx
+++ b/src/videoag/course/Lecture.tsx
@@ -2,17 +2,16 @@ import Link from "next/link";
 import { OverlayTrigger, Tooltip } from "react-bootstrap";
 
 import type { lecture, course } from "@/videoag/api/types";
+import { AuthenticationMethodIcons } from "@/videoag/authentication";
+import { datetimeToString, StylizedText } from "@/videoag/miscellaneous";
+import { useLanguage } from "@/videoag/localization";
 import {
+    useEditMode,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
-import { AuthenticationMethodIcons } from "@/videoag/authentication/ViewPermissions";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
+} from "@/videoag/object_management";
 
 import {
     PublishMediumDownloadButton,
diff --git a/src/videoag/course/LiveLabel.tsx b/src/videoag/course/LiveLabel.tsx
index 816c1d3..9965e80 100644
--- a/src/videoag/course/LiveLabel.tsx
+++ b/src/videoag/course/LiveLabel.tsx
@@ -1,7 +1,7 @@
 import { DateTime } from "luxon";
 
 import { lecture } from "@/videoag/api/types";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
+import { useLanguage } from "@/videoag/localization";
 
 export function LiveLabel({ nowlive }: { nowlive: boolean }) {
     const { language } = useLanguage();
diff --git a/src/videoag/course/Medium.tsx b/src/videoag/course/Medium.tsx
index c2dadb9..2082c43 100644
--- a/src/videoag/course/Medium.tsx
+++ b/src/videoag/course/Medium.tsx
@@ -7,26 +7,23 @@ import {
     publish_medium,
     GetLectureMediaProcessOverviewResponse,
 } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { showError, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { JobStatusCard } from "@/videoag/job/Job";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import {
-    filesizeToHuman,
-    timestampToString,
-    datetimeToString,
-} from "@/videoag/miscellaneous/Formatting";
-import { useMutexCall } from "@/videoag/miscellaneous/PromiseHelpers";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import { showError, ErrorComponent } from "@/videoag/error";
+import { JobStatusCard } from "@/videoag/job";
+import { useLanguage } from "@/videoag/localization";
 import {
     Spinner,
+    ReloadBoundary,
+    useMutexCall,
     showInfoToast,
     TooltipButton,
     parseApiDatetime,
     zeropad,
-} from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
-import { OMDelete, EmbeddedOMFieldComponent } from "@/videoag/object_management/OMConfigComponent";
+    filesizeToHuman,
+    timestampToString,
+    datetimeToString,
+} from "@/videoag/miscellaneous";
+import { useEditMode, OMDelete, EmbeddedOMFieldComponent } from "@/videoag/object_management";
 
 export function getMediumQualityName(medium: publish_medium): string | undefined {
     switch (medium.medium_metadata.type) {
diff --git a/src/videoag/course/Player.tsx b/src/videoag/course/Player.tsx
index 55d5214..ac60277 100644
--- a/src/videoag/course/Player.tsx
+++ b/src/videoag/course/Player.tsx
@@ -11,19 +11,17 @@ import "@silvermine/videojs-quality-selector/dist/css/quality-selector.css";
 import VideoJsMarkers from "@/videoag/miscellaneous/videojs-markers";
 
 import type { course, chapter, publish_medium, lecture } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { ViewPermissionAuthorization } from "@/videoag/authentication/ViewPermissions";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import Title from "@/videoag/miscellaneous/TitleComponent";
-import { UpdateOverlay } from "@/videoag/miscellaneous/UpdateOverlay";
-import { showInfoToast, parseApiDatetime } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi } from "@/videoag/api";
+import { ViewPermissionAuthorization } from "@/videoag/authentication";
+import { useLanguage } from "@/videoag/localization";
+import { Title, UpdateOverlay, showInfoToast, parseApiDatetime } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 import { basePath } from "@/../basepath";
 
 import { LectureCard, urlForLecture, getLectureThumbnailUrlNoPlaceholder } from "./Lecture";
@@ -33,7 +31,7 @@ import {
     getNamedPublishMedia,
 } from "./Medium";
 import { Chapters, AddChapterButton } from "./Chapter";
-import { StatTracker } from "./Stats";
+import { StatTracker } from "./StatTracker";
 
 import { PlayerData } from "@/pages/dynamic_player";
 
diff --git a/src/videoag/course/Stats.tsx b/src/videoag/course/StatTracker.tsx
similarity index 99%
rename from src/videoag/course/Stats.tsx
rename to src/videoag/course/StatTracker.tsx
index 4710f3e..073c728 100644
--- a/src/videoag/course/Stats.tsx
+++ b/src/videoag/course/StatTracker.tsx
@@ -2,9 +2,8 @@ import VideoJsPlayerType from "video.js/dist/types/player";
 import { DateTime } from "luxon";
 
 import type { int, datetime } from "@/videoag/api/types";
-import { Backend } from "@/videoag/api/Backend";
-import { ApiError } from "@/videoag/api/ApiError";
-import { formatApiDatetime } from "@/videoag/miscellaneous/Util";
+import { Backend, ApiError } from "@/videoag/api";
+import { formatApiDatetime } from "@/videoag/miscellaneous";
 
 const MAX_SEGMENT_REALTIME_DURATION_SEC = 20;
 const MIN_SEGMENT_DURATION_SEC = 1.2;
diff --git a/src/videoag/course/index.ts b/src/videoag/course/index.ts
new file mode 100644
index 0000000..ccd0e9c
--- /dev/null
+++ b/src/videoag/course/index.ts
@@ -0,0 +1,12 @@
+export { default as CourseListing } from "./CourseListing";
+export { LectureListItem, LectureCard } from "./Lecture";
+export { LiveLabel, LectureLiveLabel } from "./LiveLabel";
+export {
+    sortPublishMediaByQuality,
+    getSortedDownloadablePublishMedia,
+    getMediumFileNameForDownload,
+    getSortedPlayerPublishMedia,
+    PublishMediumList,
+    PublishMediumDownloadButton,
+} from "./Medium";
+export { EmbeddedPlayer, default as Player } from "./Player";
diff --git a/src/videoag/error/ErrorDisplay.tsx b/src/videoag/error/ErrorDisplay.tsx
index 48107ea..5abf82a 100644
--- a/src/videoag/error/ErrorDisplay.tsx
+++ b/src/videoag/error/ErrorDisplay.tsx
@@ -3,8 +3,8 @@ import { toast, Bounce } from "react-toastify";
 import { useRouter } from "next/router";
 import Link from "next/link";
 
-import { ApiError } from "@/videoag/api/ApiError";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { ApiError } from "@/videoag/api";
+import { useReloadBoundary } from "@/videoag/miscellaneous";
 
 export function showError(error: Error, prefix_message = "Ein Fehler ist aufgetreten") {
     if (error instanceof ApiError) {
diff --git a/src/videoag/error/FallbackErrorBoundary.tsx b/src/videoag/error/FallbackErrorBoundary.tsx
index c355099..207a59b 100644
--- a/src/videoag/error/FallbackErrorBoundary.tsx
+++ b/src/videoag/error/FallbackErrorBoundary.tsx
@@ -1,7 +1,7 @@
 import React from "react";
 import { ErrorInfo } from "react";
 
-import { NestedReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { NestedReloadBoundary } from "@/videoag/miscellaneous";
 
 export class FallbackErrorBoundary extends React.Component<any, { error?: any }> {
     constructor(props: { fallback: (e: any) => React.ReactNode; children: React.ReactNode }) {
diff --git a/src/videoag/error/index.ts b/src/videoag/error/index.ts
new file mode 100644
index 0000000..25fee30
--- /dev/null
+++ b/src/videoag/error/index.ts
@@ -0,0 +1,8 @@
+export {
+    showError,
+    showErrorToast,
+    showWarningToast,
+    Four04,
+    ErrorComponent,
+} from "./ErrorDisplay";
+export { FallbackErrorBoundary } from "./FallbackErrorBoundary";
diff --git a/src/videoag/form/TypeEditor.tsx b/src/videoag/form/TypeEditor.tsx
index 0f9b9aa..a9a0498 100644
--- a/src/videoag/form/TypeEditor.tsx
+++ b/src/videoag/form/TypeEditor.tsx
@@ -2,8 +2,8 @@ import React, { useEffect, useRef, useState } from "react";
 import { DateTime } from "luxon";
 
 import type { int } from "@/videoag/api/types";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { deepEquals, parseApiDatetime, formatApiDatetime } from "@/videoag/miscellaneous/Util";
+import { useLanguage } from "@/videoag/localization";
+import { deepEquals, parseApiDatetime, formatApiDatetime } from "@/videoag/miscellaneous";
 
 export type EditorArgs = {
     value?: any;
diff --git a/src/videoag/form/index.ts b/src/videoag/form/index.ts
new file mode 100644
index 0000000..fe62678
--- /dev/null
+++ b/src/videoag/form/index.ts
@@ -0,0 +1,9 @@
+export {
+    type EditorArgs,
+    StringEditor,
+    BooleanEditor,
+    IntEditor,
+    DatetimeEditor,
+    ChooserEditor,
+    JsonEditor,
+} from "./TypeEditor";
diff --git a/src/videoag/job/Job.tsx b/src/videoag/job/Job.tsx
index 781239c..480d4c1 100644
--- a/src/videoag/job/Job.tsx
+++ b/src/videoag/job/Job.tsx
@@ -1,11 +1,13 @@
 import { useState, useEffect, useRef } from "react";
 
 import type { int, job, job_state } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { Spinner } from "@/videoag/miscellaneous/Util";
-import { useDebouncedCall } from "@/videoag/miscellaneous/PromiseHelpers";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import {
+    datetimeToString,
+    Spinner,
+    useDebouncedCall,
+    useReloadBoundary,
+} from "@/videoag/miscellaneous";
 
 export function JobStatusCard({ jobId }: { jobId: int }) {
     const api = useApi();
diff --git a/src/videoag/job/index.ts b/src/videoag/job/index.ts
new file mode 100644
index 0000000..d570abc
--- /dev/null
+++ b/src/videoag/job/index.ts
@@ -0,0 +1 @@
+export { JobStatusCard } from "./Job";
diff --git a/src/videoag/localization/LanguageProvider.tsx b/src/videoag/localization/LanguageProvider.tsx
index 7d49b93..4624c1c 100644
--- a/src/videoag/localization/LanguageProvider.tsx
+++ b/src/videoag/localization/LanguageProvider.tsx
@@ -1,8 +1,8 @@
 import type React from "react";
 import { createContext, useContext, useState, useEffect } from "react";
 
-import { useApi } from "@/videoag/api/ApiProvider";
-import { storageGetOrDefault, storageSet } from "@/videoag/miscellaneous/Storage";
+import { useApi } from "@/videoag/api";
+import { storageGetOrDefault, storageSet } from "@/videoag/miscellaneous";
 
 import { Language } from "./i18n";
 
diff --git a/src/videoag/localization/i18n.tsx b/src/videoag/localization/i18n.tsx
index af63b4a..06b764b 100644
--- a/src/videoag/localization/i18n.tsx
+++ b/src/videoag/localization/i18n.tsx
@@ -1,6 +1,6 @@
 import type React from "react";
 
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
+import { StylizedText } from "@/videoag/miscellaneous";
 
 export class Language {
     constructor(
diff --git a/src/videoag/localization/index.ts b/src/videoag/localization/index.ts
new file mode 100644
index 0000000..bc7b721
--- /dev/null
+++ b/src/videoag/localization/index.ts
@@ -0,0 +1,2 @@
+export { Language } from "./i18n";
+export { LanguageProvider, useLanguage } from "./LanguageProvider";
diff --git a/src/videoag/miscellaneous/Theme.tsx b/src/videoag/miscellaneous/Theme.tsx
index cedb7be..86b2061 100644
--- a/src/videoag/miscellaneous/Theme.tsx
+++ b/src/videoag/miscellaneous/Theme.tsx
@@ -1,7 +1,7 @@
 import type React from "react";
 import { createContext, useContext, useEffect } from "react";
 
-import { useLocalStorageState } from "@/videoag/miscellaneous/Storage";
+import { useLocalStorageState } from "./Storage";
 
 type ThemeContextType = {
     theme: string;
diff --git a/src/videoag/miscellaneous/index.ts b/src/videoag/miscellaneous/index.ts
new file mode 100644
index 0000000..d903cd6
--- /dev/null
+++ b/src/videoag/miscellaneous/index.ts
@@ -0,0 +1,35 @@
+export { type ICalEvent, parseICal } from "./Calendar";
+export { ExpandableString } from "./ExpandableString";
+export {
+    semesterToHuman,
+    timestampToString,
+    datetimeToStringOnlyDate,
+    datetimeToStringOnlyTime,
+    datetimeToString,
+    filesizeToHuman,
+} from "./Formatting";
+export {
+    useDebounce,
+    useDebounceWithArgument,
+    useMutexCall,
+    useMutexProcessing,
+    useDebouncedCall,
+    useDebouncedProcessing,
+} from "./PromiseHelpers";
+export { ReloadBoundary, NestedReloadBoundary, useReloadBoundary } from "./ReloadBoundary";
+export { default as StopNavigation } from "./StopNavigation";
+export { storageGetOrDefault, storageSet, useLocalStorageState } from "./Storage";
+export { StylizedText } from "./StylizedText";
+export { ThemeProvider, useTheme } from "./Theme";
+export { default as Title } from "./TitleComponent";
+export { UpdateOverlay } from "./UpdateOverlay";
+export {
+    zeropad,
+    formatApiDatetime,
+    parseApiDatetime,
+    deepEquals,
+    mapMap,
+    showInfoToast,
+    TooltipButton,
+    Spinner,
+} from "./Util";
diff --git a/src/videoag/object_management/FilteredData.tsx b/src/videoag/object_management/FilteredData.tsx
index 170dc46..290565b 100644
--- a/src/videoag/object_management/FilteredData.tsx
+++ b/src/videoag/object_management/FilteredData.tsx
@@ -4,9 +4,8 @@ import { useSearchParams } from "next/navigation";
 import { useRouter } from "next/router";
 
 import type { int } from "@/videoag/api/types";
-import { ChooserEditor } from "@/videoag/form/TypeEditor";
-import { useDebounceWithArgument } from "@/videoag/miscellaneous/PromiseHelpers";
-import { mapMap } from "@/videoag/miscellaneous/Util";
+import { ChooserEditor } from "@/videoag/form";
+import { useDebounceWithArgument, mapMap } from "@/videoag/miscellaneous";
 
 // TODO sometimes search params are overridden with defaults on page load (e.g. sorter files). Redo this and use a
 // boundary instead of passing filters through the parent
diff --git a/src/videoag/object_management/OMConfigComponent.tsx b/src/videoag/object_management/OMConfigComponent.tsx
index 02ff6b7..178c4ec 100644
--- a/src/videoag/object_management/OMConfigComponent.tsx
+++ b/src/videoag/object_management/OMConfigComponent.tsx
@@ -9,16 +9,19 @@ import type {
     field_value,
     int,
 } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { ApiError } from "@/videoag/api/ApiError";
-import { showError, showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { useDebouncedProcessing } from "@/videoag/miscellaneous/PromiseHelpers";
-import { datetimeToString, semesterToHuman } from "@/videoag/miscellaneous/Formatting";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { deepEquals, TooltipButton } from "@/videoag/miscellaneous/Util";
-import StopNavigation from "@/videoag/miscellaneous/StopNavigation";
+import { useApi, ApiError } from "@/videoag/api";
+import { showError, showErrorToast } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import {
+    useReloadBoundary,
+    useDebouncedProcessing,
+    datetimeToString,
+    semesterToHuman,
+    StylizedText,
+    deepEquals,
+    TooltipButton,
+    StopNavigation,
+} from "@/videoag/miscellaneous";
 import { basePath } from "@/../basepath";
 
 import { LockEditMode, useEditMode } from "./EditModeProvider";
diff --git a/src/videoag/object_management/OMFieldEditor.tsx b/src/videoag/object_management/OMFieldEditor.tsx
index 0b45fec..f165bd9 100644
--- a/src/videoag/object_management/OMFieldEditor.tsx
+++ b/src/videoag/object_management/OMFieldEditor.tsx
@@ -9,7 +9,7 @@ import {
     DatetimeEditor,
     ChooserEditor,
     JsonEditor,
-} from "@/videoag/form/TypeEditor";
+} from "@/videoag/form";
 
 import { UserIdListEditor } from "./ObjectEditor";
 import ViewPermissionsEditor from "./ViewPermissionsEditor";
diff --git a/src/videoag/object_management/ObjectEditor.tsx b/src/videoag/object_management/ObjectEditor.tsx
index 2e169ce..43ed67b 100644
--- a/src/videoag/object_management/ObjectEditor.tsx
+++ b/src/videoag/object_management/ObjectEditor.tsx
@@ -2,9 +2,9 @@ import React, { useEffect, useRef, useState } from "react";
 import Dropdown from "react-bootstrap/Dropdown";
 
 import type { GetUsersResponse, user, int } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { EditorArgs } from "@/videoag/form/TypeEditor";
-import { showError } from "@/videoag/error/ErrorDisplay";
+import { useApi } from "@/videoag/api";
+import { EditorArgs } from "@/videoag/form";
+import { showError } from "@/videoag/error";
 
 export function UserIdListEditor({
     value,
diff --git a/src/videoag/object_management/ViewPermissionsEditor.tsx b/src/videoag/object_management/ViewPermissionsEditor.tsx
index c1da6eb..0085ca7 100644
--- a/src/videoag/object_management/ViewPermissionsEditor.tsx
+++ b/src/videoag/object_management/ViewPermissionsEditor.tsx
@@ -1,9 +1,9 @@
 import React, { useEffect, useRef, useState } from "react";
 
 import type { view_permissions, int } from "@/videoag/api/types";
-import { EditorArgs } from "@/videoag/form/TypeEditor";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { deepEquals } from "@/videoag/miscellaneous/Util";
+import { EditorArgs } from "@/videoag/form";
+import { useLanguage } from "@/videoag/localization";
+import { deepEquals } from "@/videoag/miscellaneous";
 
 function createListEditor<V>(
     disabled: boolean,
diff --git a/src/videoag/object_management/index.ts b/src/videoag/object_management/index.ts
new file mode 100644
index 0000000..411b5bf
--- /dev/null
+++ b/src/videoag/object_management/index.ts
@@ -0,0 +1,10 @@
+export { EditModeProvider, useEditMode } from "./EditModeProvider";
+export { useFilteredDataView, FilterInput, PagingNavigation } from "./FilteredData";
+export {
+    ListOMFieldComponent,
+    EmbeddedOMFieldComponent,
+    OMEdit,
+    OMCreate,
+    OMDelete,
+    OMHistory,
+} from "./OMConfigComponent";
diff --git a/src/videoag/site/AnnouncementComponent.tsx b/src/videoag/site/AnnouncementComponent.tsx
index 8453f9f..0ee5f4b 100644
--- a/src/videoag/site/AnnouncementComponent.tsx
+++ b/src/videoag/site/AnnouncementComponent.tsx
@@ -1,17 +1,15 @@
 import { useRouter } from "next/router";
 
 import type { announcement } from "@/videoag/api/types";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { useLocalStorageState } from "@/videoag/miscellaneous/Storage";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { TooltipButton } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useLanguage } from "@/videoag/localization/";
+import { useLocalStorageState, StylizedText, TooltipButton } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMDelete,
     OMEdit,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 
 // TODO mark non-visible (also check time) announcements for mods
 
diff --git a/src/videoag/site/DefaultLayout.tsx b/src/videoag/site/DefaultLayout.tsx
index 94e6a79..2f01340 100644
--- a/src/videoag/site/DefaultLayout.tsx
+++ b/src/videoag/site/DefaultLayout.tsx
@@ -7,17 +7,13 @@ import Collapse from "react-bootstrap/Collapse";
 import Dropdown from "react-bootstrap/Dropdown";
 
 import type { GetStatusResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import UserField from "@/videoag/authentication/UserField";
-import { showErrorToast, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { useTheme } from "@/videoag/miscellaneous/Theme";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
-import AnnouncementComponent from "@/videoag/site/AnnouncementComponent";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus, UserField } from "@/videoag/authentication";
+import { showErrorToast, ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, StylizedText, useTheme } from "@/videoag/miscellaneous";
+import { useEditMode } from "@/videoag/object_management";
+import { AnnouncementComponent } from "@/videoag/site";
 import { basePath } from "@/../basepath";
 
 function NavBarIcon({
diff --git a/src/videoag/site/index.ts b/src/videoag/site/index.ts
new file mode 100644
index 0000000..1c3ddd5
--- /dev/null
+++ b/src/videoag/site/index.ts
@@ -0,0 +1,2 @@
+export { default as AnnouncementComponent } from "./AnnouncementComponent";
+export { default as DefaultLayout, Search } from "./DefaultLayout";
-- 
GitLab