Repository: IgnisDa/ryot (language: rust)

Issue:
Add a column for `last_activity_at` to the `user` table
When a mutation or query is done, add this user id and `DateTimeUtc` to the cache. Then in the frequent jobs, get all these keys and update the user accordingly.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 440d372acb..abcee96ac1 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -146,7 +146,6 @@ jobs:
         env:
           TARGET: ${{ matrix.platform.target }}
         run: |
-          echo "sha=${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
           echo "version=${GITHUB_REF##*/}" >> "$GITHUB_OUTPUT"
           echo "docker-arch=${{ startsWith(matrix.platform.target, 'x86_64') && 'amd64' || 'arm64' }}" >> "$GITHUB_OUTPUT"
       - name: Extract rust toolchain
@@ -178,7 +177,6 @@ jobs:
       - name: Build
         env:
           UNKEY_API_ID: ${{ secrets.UNKEY_API_ID }}
-          APP_COMMIT_SHA: ${{ steps.build.outputs.sha }}
           APP_VERSION: ${{ steps.build.outputs.version }}
           TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
           DEFAULT_MAL_CLIENT_ID: ${{ secrets.DEFAULT_MAL_CLIENT_ID }}
diff --git a/Cargo.lock b/Cargo.lock
index 4c1375959e..6bea91d42b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -337,6 +337,7 @@ dependencies = [
  "thiserror 1.0.69",
  "tracing",
  "tracing-futures",
+ "uuid",
 ]
 
 [[package]]
@@ -1132,7 +1133,6 @@ dependencies = [
  "config",
  "database-models",
  "dependent-models",
- "env-utils",
  "itertools 0.14.0",
  "sea-orm",
  "sea-query",
@@ -3823,6 +3823,7 @@ dependencies = [
  "chrono",
  "common-models",
  "common-utils",
+ "convert_case",
  "database-models",
  "database-utils",
  "dependent-models",
@@ -6811,12 +6812,11 @@ dependencies = [
  "async-trait",
  "common-models",
  "common-utils",
- "config",
  "database-models",
  "database-utils",
  "dependent-models",
  "media-models",
- "sea-orm",
+ "supporting-service",
  "tracing",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index b1c7b9cca7..e0803f56ff 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -57,6 +57,7 @@ async-graphql = { version = "=7.0.13", features = [
     "decimal",
     "log",
     "tracing",
+    "uuid",
 ] }
 async-graphql-axum = "=7.0.13"
 async-trait = "=0.1.83"
diff --git a/Cross.toml b/Cross.toml
index 49c2fea27d..e375b05075 100644
--- a/Cross.toml
+++ b/Cross.toml
@@ -1,7 +1,6 @@
 [build.env]
 passthrough = [
     "APP_VERSION",
-    "APP_COMMIT_SHA",
     "UNKEY_API_ID",
     "TRAKT_CLIENT_ID",
     "DEFAULT_MAL_CLIENT_ID",
diff --git a/README.md b/README.md
index 5754915c7a..7d92559c95 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,11 @@
 Ryot (**R**oll **Y**our **O**wn **T**racker), pronounced "riot", aims to be the only self
 hosted tracker you will ever need!
 
+## IMPORTANT NOTE FOR `v7.*` USERS
+
+If you were using `v7.*` of Ryot, please read the
+[migration guide](https://docs.ryot.io/migration.html#from-v7-to-v8) for instructions.
+
 ## Pro version
 
 Ryot is available in two versions - community and pro. The pro version has additional
diff --git a/apps/backend/src/common.rs b/apps/backend/src/common.rs
index 92392e5082..ddceada932 100644
--- a/apps/backend/src/common.rs
+++ b/apps/backend/src/common.rs
@@ -99,12 +99,11 @@ pub async fn create_app_services(
         EmptySubscription,
     )
     .extension(Tracing)
-    .data(db.clone())
-    .data(config.clone())
     .data(user_service.clone())
+    .data(fitness_service.clone())
     .data(importer_service.clone())
     .data(exporter_service.clone())
-    .data(fitness_service.clone())
+    .data(supporting_service.clone())
     .data(statistics_service.clone())
     .data(collection_service.clone())
     .data(file_storage_service.clone())
@@ -151,7 +150,7 @@ pub async fn create_app_services(
         .layer(TowerTraceLayer::new_for_http())
         .layer(TowerCatchPanicLayer::new())
         .layer(DefaultBodyLimit::max(
-            1024 * 1024 * config.server.max_file_size,
+            1024 * 1024 * config.server.max_file_size_mb,
         ))
         .layer(cors);
 
diff --git a/apps/backend/src/job.rs b/apps/backend/src/job.rs
index 89e5cd1d24..873998c294 100644
--- a/apps/backend/src/job.rs
+++ b/apps/backend/src/job.rs
@@ -36,7 +36,7 @@ pub async fn run_frequent_jobs(
         .trace_ok();
     app_services
         .miscellaneous_service
-        .send_pending_immediate_notifications()
+        .send_pending_notifications()
         .await
         .trace_ok();
     Ok(())
@@ -179,6 +179,12 @@ pub async fn perform_lp_application_job(
                 .delete_all_application_cache()
                 .await
         }
+        LpApplicationJob::UpdateUserLastActivityPerformed(user_id, timestamp) => {
+            app_services
+                .miscellaneous_service
+                .update_user_last_activity_performed(user_id, timestamp)
+                .await
+        }
     };
     status.map_err(|e| Error::Failed(Arc::new(e.message.into())))
 }
diff --git a/apps/backend/src/main.rs b/apps/backend/src/main.rs
index 88696d464d..7d7990c731 100644
--- a/apps/backend/src/main.rs
+++ b/apps/backend/src/main.rs
@@ -96,9 +96,9 @@ async fn main() -> Result<()> {
         .await
         .expect("Database connection failed");
 
-    if let Err(err) = migrate_from_v6(&db).await {
-        ryot_log!(error, "Migration from v6 failed: {}", err);
-        bail!("There was an error migrating from v6.")
+    if let Err(err) = migrate_from_v7_if_applicable(&db).await {
+        ryot_log!(error, "Migration from v7 failed: {}", err);
+        bail!("There was an error migrating from v7.")
     }
 
     if let Err(err) = Migrator::up(&db, None).await {
@@ -247,8 +247,7 @@ fn init_tracing() -> Result<()> {
     Ok(())
 }
 
-// upgrade from v6 ONLY IF APPLICABLE
-async fn migrate_from_v6(db: &DatabaseConnection) -> Result<()> {
+async fn migrate_from_v7_if_applicable(db: &DatabaseConnection) -> Result<()> {
     db.execute_unprepared(
         r#"
 DO $$
@@ -259,41 +258,42 @@ BEGIN
     ) THEN
         IF EXISTS (
             SELECT 1 FROM seaql_migrations
-            WHERE version = 'm20240606_is_v6_migration'
+            WHERE version = 'm20240825_is_v7_migration'
         ) THEN
             IF NOT EXISTS (
                 SELECT 1 FROM seaql_migrations
-                WHERE version = 'm20240817_is_last_v6_migration'
+                WHERE version = 'm20250117_is_last_v7_migration'
             ) THEN
-                RAISE EXCEPTION 'Final migration for v6 does not exist, upgrade aborted.';
+                RAISE EXCEPTION 'Final migration for v7 does not exist, upgrade aborted.';
             END IF;
 
             DELETE FROM seaql_migrations;
             INSERT INTO seaql_migrations (version, applied_at) VALUES
-                ('m20230409_create_extensions', 1684693316),
-                ('m20230410_create_metadata', 1684693316),
-                ('m20230413_create_person', 1684693316),
-                ('m20230417_create_user', 1684693316),
-                ('m20230419_create_seen', 1684693316),
-                ('m20230501_create_metadata_group', 1684693316),
-                ('m20230502_create_genre', 1684693316),
-                ('m20230504_create_collection', 1684693316),
-                ('m20230822_create_exercise', 1684693316),
-                ('m20230819_create_workout', 1684693316),
-                ('m20230818_create_workout_template', 1684693316),
-                ('m20230505_create_review', 1684693316),
-                ('m20230509_create_import_report', 1684693316),
-                ('m20230820_create_user_measurement', 1684693316),
-                ('m20230912_create_calendar_event', 1684693316),
-                ('m20231016_create_collection_to_entity', 1684693316),
-                ('m20231017_create_user_to_entity', 1684693316),
-                ('m20231219_create_metadata_relations', 1684693316),
-                ('m20240509_create_user_to_collection', 1717207621),
-                ('m20240531_create_queued_notification', 1717207621),
-                ('m20240607_create_integration', 1723854703),
-                ('m20240712_create_notification_platform', 1723854703),
-                ('m20240713_create_user_summary', 1723854703),
-                ('m20240714_create_access_link', 1723854703);
+                ('m20230403_create_extensions', 1684693316),
+                ('m20230404_create_user', 1684693317),
+                ('m20230410_create_metadata', 1684693318),
+                ('m20230411_create_metadata_group', 1684693319),
+                ('m20230413_create_person', 1684693320),
+                ('m20230419_create_seen', 1684693321),
+                ('m20230502_create_genre', 1684693322),
+                ('m20230504_create_collection', 1684693323),
+                ('m20230505_create_exercise', 1684693324),
+                ('m20230506_create_workout_template', 1684693325),
+                ('m20230507_create_workout', 1684693326),
+                ('m20230508_create_review', 1684693327),
+                ('m20230509_create_import_report', 1684693328),
+                ('m20230820_create_user_measurement', 1684693329),
+                ('m20230912_create_calendar_event', 1684693330),
+                ('m20231016_create_collection_to_entity', 1684693331),
+                ('m20231017_create_user_to_entity', 1684693332),
+                ('m20231219_create_metadata_relations', 1684693333),
+                ('m20240607_create_integration', 1684693334),
+                ('m20240712_create_notification_platform', 1684693335),
+                ('m20240714_create_access_link', 1684693336),
+                ('m20240827_create_daily_user_activity', 1684693337),
+                ('m20240904_create_monitored_entity', 1684693338),
+                ('m20241004_create_application_cache', 1684693339),
+                ('m20241214_create_user_notification', 1684693340);
         END IF;
     END IF;
 END $$;
diff --git a/apps/frontend/app/components/common.tsx b/apps/frontend/app/components/common.tsx
index d482e8e69b..1b8569dab3 100644
--- a/apps/frontend/app/components/common.tsx
+++ b/apps/frontend/app/components/common.tsx
@@ -285,13 +285,16 @@ export const DebouncedSearchInput = (props: {
 	);
 };
 
-export const ProRequiredAlert = (props: { tooltipLabel?: string }) => {
+export const ProRequiredAlert = (props: {
+	alertText?: string;
+	tooltipLabel?: string;
+}) => {
 	const coreDetails = useCoreDetails();
 
 	return !coreDetails.isServerKeyValidated ? (
 		<Alert>
 			<Tooltip label={props.tooltipLabel} disabled={!props.tooltipLabel}>
-				<Text size="xs">{PRO_REQUIRED_MESSAGE}</Text>
+				<Text size="xs">{props.alertText || PRO_REQUIRED_MESSAGE}</Text>
 			</Tooltip>
 		</Alert>
 	) : null;
@@ -472,10 +475,10 @@ export const FiltersModal = (props: {
 
 	return (
 		<Modal
-			onClose={props.closeFiltersModal}
+			centered
 			opened={props.opened}
 			withCloseButton={false}
-			centered
+			onClose={props.closeFiltersModal}
 		>
 			<Stack>
 				<Group justify="space-between">
diff --git a/apps/frontend/app/lib/generals.tsx b/apps/frontend/app/lib/generals.tsx
index 30ce6f49c4..e99d5c53ae 100644
--- a/apps/frontend/app/lib/generals.tsx
+++ b/apps/frontend/app/lib/generals.tsx
@@ -399,6 +399,12 @@ const fitnessQueryKeys = createQueryKeys("fitness", {
 	}),
 });
 
+const userQueryKeys = createQueryKeys("user", {
+	userPendingNotifications: () => ({
+		queryKey: ["userPendingNotifications"],
+	}),
+});
+
 const analyticsQueryKeys = createQueryKeys("analytics", {
 	user: (input: UserAnalyticsQueryVariables) => ({
 		queryKey: ["user", input],
@@ -406,6 +412,7 @@ const analyticsQueryKeys = createQueryKeys("analytics", {
 });
 
 export const queryFactory = mergeQueryKeys(
+	userQueryKeys,
 	mediaQueryKeys,
 	fitnessQueryKeys,
 	analyticsQueryKeys,
@@ -508,3 +515,35 @@ export const openConfirmationModal = (title: string, onConfirm: () => void) =>
 		children: <Text size="sm">{title}</Text>,
 		labels: { confirm: "Confirm", cancel: "Cancel" },
 	});
+
+export enum ApplicationTimeRange {
+	Yesterday = "Yesterday",
+	Past7Days = "Past 7 Days",
+	Past30Days = "Past 30 Days",
+	Past6Months = "Past 6 Months",
+	Past12Months = "Past 12 Months",
+	ThisWeek = "This Week",
+	ThisMonth = "This Month",
+	ThisYear = "This Year",
+	AllTime = "All Time",
+	Custom = "Custom",
+}
+
+export const getStartTimeFromRange = (range: ApplicationTimeRange) =>
+	match(range)
+		.with(ApplicationTimeRange.Yesterday, () => dayjs().subtract(1, "day"))
+		.with(ApplicationTimeRange.ThisWeek, () => dayjs().startOf("week"))
+		.with(ApplicationTimeRange.ThisMonth, () => dayjs().startOf("month"))
+		.with(ApplicationTimeRange.ThisYear, () => dayjs().startOf("year"))
+		.with(ApplicationTimeRange.Past7Days, () => dayjs().subtract(7, "day"))
+		.with(ApplicationTimeRange.Past30Days, () => dayjs().subtract(30, "day"))
+		.with(ApplicationTimeRange.Past6Months, () => dayjs().subtract(6, "month"))
+		.with(ApplicationTimeRange.Past12Months, () =>
+			dayjs().subtract(12, "month"),
+		)
+		.with(
+			ApplicationTimeRange.AllTime,
+			ApplicationTimeRange.Custom,
+			() => undefined,
+		)
+		.exhaustive();
diff --git a/apps/frontend/app/lib/hooks.ts b/apps/frontend/app/lib/hooks.ts
index c03aa60db2..97a11547c6 100644
--- a/apps/frontend/app/lib/hooks.ts
+++ b/apps/frontend/app/lib/hooks.ts
@@ -17,7 +17,7 @@ import { $path } from "remix-routes";
 import invariant from "tiny-invariant";
 import { useInterval } from "usehooks-ts";
 import {
-	FitnessAction,
+	type FitnessAction,
 	dayjsLib,
 	getMetadataDetailsQuery,
 	getUserMetadataDetailsQuery,
@@ -199,9 +199,6 @@ export const useGetWatchProviders = (mediaLot: MediaLot) => {
 
 export const useIsFitnessActionActive = () => {
 	const [currentWorkout] = useCurrentWorkout();
-	const action = currentWorkout?.currentActionOrCompleted;
-	return (
-		action !== undefined &&
-		[FitnessAction.LogWorkout, FitnessAction.CreateTemplate].includes(action)
-	);
+	const action = currentWorkout?.currentAction;
+	return action !== undefined;
 };
diff --git a/apps/frontend/app/lib/state/fitness.ts b/apps/frontend/app/lib/state/fitness.ts
index 18ea36ea02..cba7a3f6dd 100644
--- a/apps/frontend/app/lib/state/fitness.ts
+++ b/apps/frontend/app/lib/state/fitness.ts
@@ -39,11 +39,11 @@ import type { useCoreDetails } from "../hooks";
 export type ExerciseSet = {
 	lot: SetLot;
 	rpe?: number | null;
-	confirmedAt: string | null;
 	restTimerStartedAt?: string;
 	statistic: WorkoutSetStatistic;
 	note?: boolean | string | null;
 	displayRestTimeTrigger?: boolean;
+	confirmedAt: string | boolean | null;
 	restTimer?: { duration: number; hasElapsed?: boolean } | null;
 };
 
@@ -84,11 +84,11 @@ export type InProgressWorkout = {
 	caloriesBurnt?: number;
 	updateWorkoutId?: string;
 	exercises: Array<Exercise>;
+	currentAction: FitnessAction;
 	replacingExerciseIdx?: number;
 	updateWorkoutTemplateId?: string;
 	durations: Array<WorkoutDuration>;
 	timerDrawerLot: "timer" | "stopwatch";
-	currentActionOrCompleted: FitnessAction;
 };
 
 type CurrentWorkout = InProgressWorkout | null;
@@ -121,7 +121,7 @@ export const getDefaultWorkout = (
 		exercises: [],
 		timerDrawerLot: "timer",
 		startTime: date.toISOString(),
-		currentActionOrCompleted: fitnessEntity,
+		currentAction: fitnessEntity,
 		durations: [{ from: date.toISOString() }],
 		name: `${getTimeOfDay(date.hour())} Workout`,
 	};
@@ -235,9 +235,10 @@ export const currentWorkoutToCreateWorkoutInput = (
 					statistic: set.statistic,
 					restTime: set.restTimer?.duration,
 					restTimerStartedAt: set.restTimerStartedAt,
-					confirmedAt: set.confirmedAt
-						? new Date(set.confirmedAt).toISOString()
-						: null,
+					confirmedAt:
+						set.confirmedAt && isString(set.confirmedAt)
+							? new Date(set.confirmedAt).toISOString()
+							: null,
 				});
 			}
 		if (!isCreatingTemplate && sets.length === 0) continue;
@@ -428,7 +429,7 @@ export const addExerciseToWorkout = async (
 	navigate(
 		withFragment(
 			$path("/fitness/:action", {
-				action: currentWorkout.currentActionOrCompleted,
+				action: currentWorkout.currentAction,
 			}),
 			idxOfNextExercise.toString(),
 		),
diff --git a/apps/frontend/app/routes/_dashboard._index.tsx b/apps/frontend/app/routes/_dashboard._index.tsx
index f84c433b9e..fbe35af3f9 100644
--- a/apps/frontend/app/routes/_dashboard._index.tsx
+++ b/apps/frontend/app/routes/_dashboard._index.tsx
@@ -11,7 +11,7 @@ import type {
 	LoaderFunctionArgs,
 	MetaArgs,
 } from "@remix-run/node";
-import { Form, useLoaderData } from "@remix-run/react";
+import { useLoaderData, useRevalidator } from "@remix-run/react";
 import {
 	type CalendarEventPartFragment,
 	CollectionContentsDocument,
@@ -19,20 +19,17 @@ import {
 	DashboardElementLot,
 	GraphqlSortOrder,
 	MediaLot,
-	RefreshUserMetadataRecommendationsDocument,
 	UserAnalyticsDocument,
 	UserMetadataRecommendationsDocument,
-	type UserPreferences,
 	UserUpcomingCalendarEventsDocument,
 } from "@ryot/generated/graphql/backend/graphql";
-import { getActionIntent, isNumber } from "@ryot/ts-utils";
+import { isNumber } from "@ryot/ts-utils";
 import { IconInfoCircle, IconRotateClockwise } from "@tabler/icons-react";
 import CryptoJS from "crypto-js";
 import type { ReactNode } from "react";
 import { ClientOnly } from "remix-utils/client-only";
 import invariant from "tiny-invariant";
 import { match } from "ts-pattern";
-import { withQuery } from "ufo";
 import { useLocalStorage } from "usehooks-ts";
 import {
 	ApplicationGrid,
@@ -41,30 +38,29 @@ import {
 } from "~/components/common";
 import { DisplayCollectionEntity } from "~/components/common";
 import { MetadataDisplayItem } from "~/components/media";
-import { dayjsLib, openConfirmationModal } from "~/lib/generals";
 import {
-	useConfirmSubmit,
-	useCoreDetails,
-	useUserPreferences,
-} from "~/lib/hooks";
+	clientGqlService,
+	dayjsLib,
+	openConfirmationModal,
+} from "~/lib/generals";
+import { useCoreDetails, useUserPreferences } from "~/lib/hooks";
 import {
 	getUserCollectionsList,
 	getUserPreferences,
 	serverGqlService,
 } from "~/lib/utilities.server";
 
-const getTake = (preferences: UserPreferences, el: DashboardElementLot) => {
-	const t = preferences.general.dashboard.find(
-		(de) => de.section === el,
-	)?.numElements;
-	invariant(isNumber(t));
-	return t;
-};
-
 export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const preferences = await getUserPreferences(request);
-	const takeUpcoming = getTake(preferences, DashboardElementLot.Upcoming);
-	const takeInProgress = getTake(preferences, DashboardElementLot.InProgress);
+	const getTake = (el: DashboardElementLot) => {
+		const t = preferences.general.dashboard.find(
+			(de) => de.section === el,
+		)?.numElements;
+		invariant(isNumber(t));
+		return t;
+	};
+	const takeUpcoming = getTake(DashboardElementLot.Upcoming);
+	const takeInProgress = getTake(DashboardElementLot.InProgress);
 	const getRecommendations = async () => {
 		if (
 			preferences.general.dashboard.find(
@@ -76,6 +72,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 			await serverGqlService.authenticatedRequest(
 				request,
 				UserMetadataRecommendationsDocument,
+				{},
 			);
 		return userMetadataRecommendations;
 	};
@@ -122,24 +119,13 @@ export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Home | Ryot" }];
 };
 
-export const action = async ({ request }: ActionFunctionArgs) => {
-	const intent = getActionIntent(request);
-	return await match(intent)
-		.with("refreshUserMetadataRecommendations", async () => {
-			await serverGqlService.authenticatedRequest(
-				request,
-				RefreshUserMetadataRecommendationsDocument,
-			);
-			return {};
-		})
-		.run();
-};
+export const action = async (_args: ActionFunctionArgs) => {};
 
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
 	const coreDetails = useCoreDetails();
 	const userPreferences = useUserPreferences();
-	const submit = useConfirmSubmit();
+	const revalidator = useRevalidator();
 
 	const dashboardMessage = coreDetails.frontend.dashboardMessage;
 	const latestUserSummary = loaderData.userAnalytics.activities.items.at(0);
@@ -203,27 +189,23 @@ export default function Page() {
 							<Section key={v} lot={v}>
 								<Group justify="space-between">
 									<SectionTitle text="Recommendations" />
-									<Form
-										method="POST"
-										action={withQuery(".?index", {
-											intent: "refreshUserMetadataRecommendations",
-										})}
+									<ActionIcon
+										variant="subtle"
+										onClick={() => {
+											openConfirmationModal(
+												"Are you sure you want to refresh the recommendations?",
+												async () => {
+													await clientGqlService.request(
+														UserMetadataRecommendationsDocument,
+														{ shouldRefresh: true },
+													);
+													revalidator.revalidate();
+												},
+											);
+										}}
 									>
-										<ActionIcon
-											type="submit"
-											variant="subtle"
-											onClick={(e) => {
-												const form = e.currentTarget.form;
-												e.preventDefault();
-												openConfirmationModal(
-													"Are you sure you want to refresh the recommendations?",
-													() => submit(form),
-												);
-											}}
-										>
-											<IconRotateClockwise />
-										</ActionIcon>
-									</Form>
+										<IconRotateClockwise />
+									</ActionIcon>
 								</Group>
 								{coreDetails.isServerKeyValidated ? (
 									<ApplicationGrid>
diff --git a/apps/frontend/app/routes/_dashboard.analytics.tsx b/apps/frontend/app/routes/_dashboard.analytics.tsx
index 120b4c7ead..e60857fd49 100644
--- a/apps/frontend/app/routes/_dashboard.analytics.tsx
+++ b/apps/frontend/app/routes/_dashboard.analytics.tsx
@@ -66,11 +66,13 @@ import {
 	displayWeightWithUnit,
 } from "~/components/fitness";
 import {
+	ApplicationTimeRange,
 	MediaColors,
 	PRO_REQUIRED_MESSAGE,
 	clientGqlService,
 	convertUtcHourToLocalHour,
 	dayjsLib,
+	getStartTimeFromRange,
 	queryFactory,
 	selectRandomElement,
 } from "~/lib/generals";
@@ -82,23 +84,10 @@ import {
 } from "~/lib/hooks";
 import { serverGqlService } from "~/lib/utilities.server";
 
-enum AnalyticsTimeRanges {
-	Yesterday = "Yesterday",
-	Past7Days = "Past 7 Days",
-	Past30Days = "Past 30 Days",
-	Past6Months = "Past 6 Months",
-	Past12Months = "Past 12 Months",
-	ThisWeek = "This Week",
-	ThisMonth = "This Month",
-	ThisYear = "This Year",
-	AllTime = "All Time",
-	Custom = "Custom",
-}
-
 export type TimeSpanSettings = {
 	endDate?: string;
 	startDate?: string;
-	range: AnalyticsTimeRanges;
+	range: ApplicationTimeRange;
 };
 
 export const loader = async ({ request }: LoaderFunctionArgs) => {
@@ -118,35 +107,14 @@ const useTimeSpanSettings = () => {
 	const loaderData = useLoaderData<typeof loader>();
 	const [timeSpanSettings, setTimeSpanSettings] =
 		useLocalStorage<TimeSpanSettings>("TimeSpanSettings", {
-			range: AnalyticsTimeRanges.Past30Days,
+			range: ApplicationTimeRange.Past30Days,
 		});
-	const getStartTime = (range: AnalyticsTimeRanges) =>
-		match(range)
-			.with(AnalyticsTimeRanges.Yesterday, () => dayjsLib().subtract(1, "day"))
-			.with(AnalyticsTimeRanges.ThisWeek, () => dayjsLib().startOf("week"))
-			.with(AnalyticsTimeRanges.ThisMonth, () => dayjsLib().startOf("month"))
-			.with(AnalyticsTimeRanges.ThisYear, () => dayjsLib().startOf("year"))
-			.with(AnalyticsTimeRanges.Past7Days, () => dayjsLib().subtract(7, "day"))
-			.with(AnalyticsTimeRanges.Past30Days, () =>
-				dayjsLib().subtract(30, "day"),
-			)
-			.with(AnalyticsTimeRanges.Past6Months, () =>
-				dayjsLib().subtract(6, "month"),
-			)
-			.with(AnalyticsTimeRanges.Past12Months, () =>
-				dayjsLib().subtract(12, "month"),
-			)
-			.with(AnalyticsTimeRanges.AllTime, () =>
-				loaderData.userAnalyticsParameters.startDate
-					? dayjsLib(loaderData.userAnalyticsParameters.startDate)
-					: undefined,
-			)
-			.with(AnalyticsTimeRanges.Custom, () => undefined)
-			.exhaustive();
 
 	const startDate =
 		timeSpanSettings.startDate ||
-		formatDateToNaiveDate(getStartTime(timeSpanSettings.range) || new Date());
+		formatDateToNaiveDate(
+			getStartTimeFromRange(timeSpanSettings.range) || new Date(),
+		);
 
 	const endDate =
 		timeSpanSettings.endDate ||
@@ -215,7 +183,7 @@ export default function Page() {
 										</Button>
 									</Menu.Target>
 									<Menu.Dropdown>
-										{Object.values(AnalyticsTimeRanges).map((range) => (
+										{Object.values(ApplicationTimeRange).map((range) => (
 											<Menu.Item
 												ta="right"
 												key={range}
@@ -244,9 +212,9 @@ export default function Page() {
 					</SimpleGrid>
 					{!coreDetails.isServerKeyValidated &&
 					![
-						AnalyticsTimeRanges.Yesterday,
-						AnalyticsTimeRanges.Past7Days,
-						AnalyticsTimeRanges.Past30Days,
+						ApplicationTimeRange.Yesterday,
+						ApplicationTimeRange.Past7Days,
+						ApplicationTimeRange.Past30Days,
 					].includes(timeSpanSettings.range) ? (
 						<ProRequiredAlert />
 					) : (
@@ -496,7 +464,7 @@ const CustomDateSelectModal = (props: {
 							produce(timeSpanSettings, (draft) => {
 								draft.startDate = formatDateToNaiveDate(value[0] || new Date());
 								draft.endDate = formatDateToNaiveDate(value[1] || new Date());
-								draft.range = AnalyticsTimeRanges.Custom;
+								draft.range = ApplicationTimeRange.Custom;
 							}),
 						);
 						props.onClose();
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
index 7561937c8b..ffcf0a35fc 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
@@ -453,7 +453,7 @@ export default function Page() {
 								onClose={() => setSupersetModalOpened(null)}
 							/>
 							<Stack ref={parent}>
-								<NameAndCommentInputs
+								<NameAndOtherInputs
 									openAssetsModal={() => setAssetsModalOpened(null)}
 								/>
 								<Group>
@@ -713,7 +713,7 @@ export default function Page() {
 	);
 }
 
-const NameAndCommentInputs = (props: {
+const NameAndOtherInputs = (props: {
 	openAssetsModal: () => void;
 }) => {
 	const loaderData = useLoaderData<typeof loader>();
@@ -759,23 +759,32 @@ const NameAndCommentInputs = (props: {
 	return (
 		<>
 			<Modal
-				withCloseButton={false}
+				title="Additional details"
 				opened={isCaloriesBurntModalOpen}
 				onClose={() => setIsCaloriesBurntModalOpen(false)}
 			>
-				<NumberInput
-					size="sm"
-					label="Calories burnt"
-					value={currentWorkout.caloriesBurnt}
-					onChange={(e) => setCaloriesBurnt(isNumber(e) ? e : undefined)}
-				/>
+				<Stack gap="xs">
+					<NumberInput
+						size="sm"
+						label="Calories burnt"
+						value={currentWorkout.caloriesBurnt}
+						onChange={(e) => setCaloriesBurnt(isNumber(e) ? e : undefined)}
+					/>
+					<Textarea
+						size="sm"
+						minRows={2}
+						label="Comments"
+						defaultValue={comment}
+						placeholder="Your thoughts about this workout"
+						onChange={(e) => setComment(e.currentTarget.value)}
+					/>
+				</Stack>
 			</Modal>
 			<TextInput
-				required
 				size="sm"
-				label="Name"
 				defaultValue={name}
 				placeholder="A name for your workout"
+				styles={{ label: { width: "100%" } }}
 				onChange={(e) => setName(e.currentTarget.value)}
 				rightSection={
 					<ActionIcon
@@ -785,27 +794,19 @@ const NameAndCommentInputs = (props: {
 						<IconCamera size={30} />
 					</ActionIcon>
 				}
-			/>
-			<Textarea
-				size="sm"
-				minRows={2}
-				defaultValue={comment}
-				styles={{ label: { width: "100%" } }}
-				placeholder="Your thoughts about this workout"
 				label={
 					<Group justify="space-between" mr="xs">
-						<Text size="sm">Comments</Text>
+						<Text size="sm">Name</Text>
 						{!loaderData.isCreatingTemplate ? (
 							<Anchor
 								size="xs"
 								onClick={() => setIsCaloriesBurntModalOpen(true)}
 							>
-								Details
+								More Information
 							</Anchor>
 						) : null}
 					</Group>
 				}
-				onChange={(e) => setComment(e.currentTarget.value)}
 			/>
 		</>
 	);
@@ -2331,7 +2332,10 @@ const SetDisplay = (props: {
 														draft.exercises[props.exerciseIdx];
 													const currentSet = currentExercise.sets[props.setIdx];
 													currentSet.confirmedAt = newConfirmed
-														? dayjsLib().toISOString()
+														? currentWorkout.currentAction ===
+															FitnessAction.UpdateWorkout
+															? true
+															: dayjsLib().toISOString()
 														: null;
 													currentExercise.scrollMarginRemoved = true;
 													if (
diff --git a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
index eff43b6750..79fc4b2a0b 100644
--- a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
@@ -4,6 +4,7 @@ import {
 	Button,
 	Center,
 	Container,
+	Divider,
 	Flex,
 	Group,
 	Loader,
@@ -14,6 +15,7 @@ import {
 	Tabs,
 	Text,
 } from "@mantine/core";
+import { DatePickerInput } from "@mantine/dates";
 import { useDisclosure } from "@mantine/hooks";
 import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import {
@@ -58,18 +60,23 @@ import {
 	CollectionsFilter,
 	DebouncedSearchInput,
 	FiltersModal,
+	ProRequiredAlert,
 } from "~/components/common";
 import { MetadataDisplayItem } from "~/components/media";
 import {
+	ApplicationTimeRange,
 	Verb,
 	commaDelimitedString,
+	dayjsLib,
 	getLot,
+	getStartTimeFromRange,
 	getVerb,
 	pageQueryParam,
 } from "~/lib/generals";
 import {
 	useAppSearchParam,
 	useApplicationEvents,
+	useCoreDetails,
 	useUserDetails,
 	useUserPreferences,
 } from "~/lib/hooks";
@@ -92,9 +99,10 @@ export type SearchParams = {
 
 const defaultFilters = {
 	mineCollection: undefined,
-	mineGeneralFilter: MediaGeneralFilter.All,
-	mineSortOrder: GraphqlSortOrder.Desc,
 	mineSortBy: MediaSortBy.LastSeen,
+	mineSortOrder: GraphqlSortOrder.Desc,
+	mineGeneralFilter: MediaGeneralFilter.All,
+	mineDateRange: ApplicationTimeRange.AllTime,
 };
 
 enum Action {
@@ -119,15 +127,20 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const [totalResults, mediaList, mediaSearch] = await match(action)
 		.with(Action.List, async () => {
 			const urlParse = zx.parseQuery(request, {
+				collections: commaDelimitedString,
+				endDateRange: z.string().optional(),
+				startDateRange: z.string().optional(),
+				invertCollection: zx.BoolAsString.optional(),
+				sortBy: z.nativeEnum(MediaSortBy).default(defaultFilters.mineSortBy),
+				dateRange: z
+					.nativeEnum(ApplicationTimeRange)
+					.default(defaultFilters.mineDateRange),
 				sortOrder: z
 					.nativeEnum(GraphqlSortOrder)
 					.default(defaultFilters.mineSortOrder),
-				sortBy: z.nativeEnum(MediaSortBy).default(defaultFilters.mineSortBy),
 				generalFilter: z
 					.nativeEnum(MediaGeneralFilter)
 					.default(defaultFilters.mineGeneralFilter),
-				collections: commaDelimitedString,
-				invertCollection: zx.BoolAsString.optional(),
 			});
 			const { metadataList } = await serverGqlService.authenticatedRequest(
 				request,
@@ -135,13 +148,17 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 				{
 					input: {
 						lot,
-						search: { page: query[pageQueryParam], query: query.query },
+						invertCollection: urlParse.invertCollection,
 						sort: { order: urlParse.sortOrder, by: urlParse.sortBy },
+						search: { page: query[pageQueryParam], query: query.query },
 						filter: {
 							general: urlParse.generalFilter,
 							collections: urlParse.collections,
+							dateRange: {
+								endDate: urlParse.endDateRange,
+								startDate: urlParse.startDateRange,
+							},
 						},
-						invertCollection: urlParse.invertCollection,
 					},
 				},
 			);
@@ -183,8 +200,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 				metadataSearch === false ? 0 : metadataSearch.details.total,
 				undefined,
 				{
-					search: metadataSearch,
 					url: urlParse,
+					search: metadataSearch,
 					mediaSources: metadataSourcesForLot.sources,
 				},
 			] as const;
@@ -221,6 +238,7 @@ export const meta = ({ params }: MetaArgs<typeof loader>) => {
 
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
+	const coreDetails = useCoreDetails();
 	const [_, { setP }] = useAppSearchParam(loaderData.cookieName);
 	const [
 		filtersModalOpened,
@@ -228,21 +246,22 @@ export default function Page() {
 	] = useDisclosure(false);
 	const navigate = useNavigate();
 	const bulkEditingCollection = useBulkEditCollection();
-	const bulkEditingState = bulkEditingCollection.state;
 
+	const bulkEditingState = bulkEditingCollection.state;
+	const mediaSearch = loaderData.mediaSearch;
 	const isFilterChanged =
 		loaderData.mediaList?.url.generalFilter !==
 			defaultFilters.mineGeneralFilter ||
 		loaderData.mediaList?.url.sortOrder !== defaultFilters.mineSortOrder ||
 		loaderData.mediaList?.url.sortBy !== defaultFilters.mineSortBy ||
-		loaderData.mediaList?.url.collections !== defaultFilters.mineCollection;
-	const mediaSearch = loaderData.mediaSearch;
+		loaderData.mediaList?.url.collections !== defaultFilters.mineCollection ||
+		loaderData.mediaList?.url.dateRange !== defaultFilters.mineDateRange;
 
 	return (
 		<Container>
 			<Tabs
-				variant="default"
 				mt="sm"
+				variant="default"
 				value={loaderData.action}
 				onChange={(v) => {
 					if (v)
@@ -301,9 +320,9 @@ export default function Page() {
 								<IconFilter size={24} />
 							</ActionIcon>
 							<FiltersModal
-								closeFiltersModal={closeFiltersModal}
-								cookieName={loaderData.cookieName}
 								opened={filtersModalOpened}
+								cookieName={loaderData.cookieName}
+								closeFiltersModal={closeFiltersModal}
 							>
 								<FiltersModalForm />
 							</FiltersModal>
@@ -316,37 +335,43 @@ export default function Page() {
 									</Text>{" "}
 									items found
 								</Box>
-								<ApplicationGrid>
-									{loaderData.mediaList.list.items.map((item) => {
-										const becItem = {
-											entityId: item,
-											entityLot: EntityLot.Metadata,
-										};
-										const isAdded = bulkEditingCollection.isAdded(becItem);
-										return (
-											<MetadataDisplayItem
-												key={item}
-												metadataId={item}
-												rightLabelHistory
-												topRight={
-													bulkEditingState &&
-													bulkEditingState.data.action === "add" ? (
-														<ActionIcon
-															variant={isAdded ? "filled" : "transparent"}
-															color="green"
-															onClick={() => {
-																if (isAdded) bulkEditingState.remove(becItem);
-																else bulkEditingState.add(becItem);
-															}}
-														>
-															<IconCheck size={18} />
-														</ActionIcon>
-													) : undefined
-												}
-											/>
-										);
-									})}
-								</ApplicationGrid>
+								{(loaderData.mediaList?.url.startDateRange ||
+									loaderData.mediaList?.url.endDateRange) &&
+								!coreDetails.isServerKeyValidated ? (
+									<ProRequiredAlert alertText="Ryot Pro is required to filter by dates" />
+								) : (
+									<ApplicationGrid>
+										{loaderData.mediaList.list.items.map((item) => {
+											const becItem = {
+												entityId: item,
+												entityLot: EntityLot.Metadata,
+											};
+											const isAdded = bulkEditingCollection.isAdded(becItem);
+											return (
+												<MetadataDisplayItem
+													key={item}
+													metadataId={item}
+													rightLabelHistory
+													topRight={
+														bulkEditingState &&
+														bulkEditingState.data.action === "add" ? (
+															<ActionIcon
+																variant={isAdded ? "filled" : "transparent"}
+																color="green"
+																onClick={() => {
+																	if (isAdded) bulkEditingState.remove(becItem);
+																	else bulkEditingState.add(becItem);
+																}}
+															>
+																<IconCheck size={18} />
+															</ActionIcon>
+														) : undefined
+													}
+												/>
+											);
+										})}
+									</ApplicationGrid>
+								)}
 							</>
 						) : (
 							<Text>You do not have any saved yet</Text>
@@ -563,18 +588,13 @@ const FiltersModalForm = () => {
 		<>
 			<Select
 				defaultValue={loaderData.mediaList.url.generalFilter}
-				data={[
-					{
-						group: "General filters",
-						items: Object.values(MediaGeneralFilter).map((o) => ({
-							value: o.toString(),
-							label: startCase(o.toLowerCase()),
-						})),
-					},
-				]}
 				onChange={(v) => {
 					if (v) setP("generalFilter", v);
 				}}
+				data={Object.values(MediaGeneralFilter).map((o) => ({
+					value: o.toString(),
+					label: startCase(o.toLowerCase()),
+				}))}
 			/>
 			<Flex gap="xs" align="center">
 				<Select
@@ -612,6 +632,51 @@ const FiltersModalForm = () => {
 				collections={loaderData.mediaList.url.collections}
 				invertCollection={loaderData.mediaList.url.invertCollection}
 			/>
+			<Divider />
+			<Stack gap="xs">
+				<Select
+					size="xs"
+					description="Finished between time range"
+					data={Object.values(ApplicationTimeRange)}
+					defaultValue={loaderData.mediaList.url.dateRange}
+					onChange={(v) => {
+						const range = v as ApplicationTimeRange;
+						const startDateRange = getStartTimeFromRange(range);
+						setP("dateRange", v);
+						if (range === ApplicationTimeRange.Custom) return;
+						setP("startDateRange", startDateRange?.format("YYYY-MM-DD") || "");
+						setP(
+							"endDateRange",
+							range === ApplicationTimeRange.AllTime
+								? ""
+								: dayjsLib().format("YYYY-MM-DD"),
+						);
+					}}
+				/>
+				{loaderData.mediaList.url.dateRange === ApplicationTimeRange.Custom ? (
+					<DatePickerInput
+						size="xs"
+						type="range"
+						description="Select custom dates"
+						defaultValue={
+							loaderData.mediaList.url.startDateRange &&
+							loaderData.mediaList.url.endDateRange
+								? [
+										new Date(loaderData.mediaList.url.startDateRange),
+										new Date(loaderData.mediaList.url.endDateRange),
+									]
+								: undefined
+						}
+						onChange={(v) => {
+							const start = v[0];
+							const end = v[1];
+							if (!start || !end) return;
+							setP("startDateRange", dayjsLib(start).format("YYYY-MM-DD"));
+							setP("endDateRange", dayjsLib(end).format("YYYY-MM-DD"));
+						}}
+					/>
+				) : null}
+			</Stack>
 		</>
 	);
 };
diff --git a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
index db5bca87a2..9c037d2f2b 100644
--- a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
@@ -444,6 +444,10 @@ export default function Page() {
 											});
 										}}
 										label={match(name)
+											.with(
+												UserNotificationContent.OutdatedSeenEntries,
+												() => "Media has been in progress/on hold for too long",
+											)
 											.with(
 												UserNotificationContent.MetadataEpisodeNameChanged,
 												() => "Name of an episode changes",
diff --git a/apps/frontend/app/routes/_dashboard.tsx b/apps/frontend/app/routes/_dashboard.tsx
index c750bdf5b8..01de04bc30 100644
--- a/apps/frontend/app/routes/_dashboard.tsx
+++ b/apps/frontend/app/routes/_dashboard.tsx
@@ -2,11 +2,13 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
 import {
 	ActionIcon,
 	Affix,
+	Alert,
 	Anchor,
 	AppShell,
 	Box,
 	Burger,
 	Button,
+	Card,
 	Center,
 	Checkbox,
 	Code,
@@ -47,6 +49,7 @@ import {
 	useDisclosure,
 	useLocalStorage,
 } from "@mantine/hooks";
+import { notifications } from "@mantine/notifications";
 import type { LoaderFunctionArgs } from "@remix-run/node";
 import {
 	Form,
@@ -63,11 +66,13 @@ import {
 import {
 	CollectionExtraInformationLot,
 	EntityLot,
+	MarkNotificationsAsAddressedDocument,
 	MediaLot,
 	type MetadataDetailsQuery,
 	type UserCollectionsListQuery,
 	UserLot,
 	type UserMetadataDetailsQuery,
+	UserPendingNotificationsDocument,
 	UserReviewScale,
 	Visibility,
 } from "@ryot/generated/graphql/backend/graphql";
@@ -80,10 +85,13 @@ import {
 } from "@ryot/ts-utils";
 import {
 	IconArchive,
+	IconBellRinging,
 	IconBook,
 	IconBrandPagekit,
 	IconCalendar,
 	IconCancel,
+	IconCheck,
+	IconChecks,
 	IconChevronLeft,
 	IconChevronRight,
 	IconChevronsLeft,
@@ -104,13 +112,14 @@ import {
 	IconStretching,
 	IconSun,
 } from "@tabler/icons-react";
-import { useQuery } from "@tanstack/react-query";
+import { useMutation, useQuery } from "@tanstack/react-query";
 import { produce } from "immer";
 import Cookies from "js-cookie";
 import { type FC, type FormEvent, type ReactNode, useState } from "react";
 import { Fragment } from "react/jsx-runtime";
 import { $path } from "remix-routes";
 import { ClientOnly } from "remix-utils/client-only";
+import invariant from "tiny-invariant";
 import { match } from "ts-pattern";
 import { joinURL, withQuery } from "ufo";
 import {
@@ -118,9 +127,12 @@ import {
 	LOGO_IMAGE_URL,
 	ThreePointSmileyRating,
 	Verb,
+	clientGqlService,
 	convertDecimalToThreePointSmiley,
+	dayjsLib,
 	getMetadataDetailsQuery,
 	getVerb,
+	queryFactory,
 	refreshUserMetadataDetails,
 } from "~/lib/generals";
 import {
@@ -755,18 +767,18 @@ export default function Layout() {
 						<Link to={$path("/")} style={{ all: "unset" }}>
 							<Group>
 								<Image
-									src={LOGO_IMAGE_URL}
 									h={40}
 									w={40}
-									radius="md"
 									darkHidden
+									radius="md"
+									src={LOGO_IMAGE_URL}
 								/>
 								<Image
-									src="/logo-light.png"
 									h={40}
 									w={40}
 									radius="md"
 									lightHidden
+									src="/logo-light.png"
 								/>
 								<Text size="xl" className={classes.logoText}>
 									Ryot
@@ -892,34 +904,155 @@ const LinksGroup = ({
 	);
 };
 
+const useUserPendingNotifications = () => {
+	const userPendingNotificationsQuery = useQuery({
+		queryKey: queryFactory.user.userPendingNotifications().queryKey,
+		queryFn: async () => {
+			const { userPendingNotifications } = await clientGqlService.request(
+				UserPendingNotificationsDocument,
+			);
+			return userPendingNotifications;
+		},
+	});
+	return userPendingNotificationsQuery;
+};
+
+const useMarkUserNotificationsAsAddressedMutation = () => {
+	const userPendingNotificationsQuery = useUserPendingNotifications();
+	const markUserNotificationsAsAddressedMutation = useMutation({
+		mutationFn: async (notificationIds: string[]) => {
+			await clientGqlService.request(MarkNotificationsAsAddressedDocument, {
+				notificationIds,
+			});
+		},
+		onSuccess: () => {
+			userPendingNotificationsQuery.refetch();
+		},
+	});
+	return markUserNotificationsAsAddressedMutation;
+};
+
+const DisplayNotificationContent = (props: { idx: number }) => {
+	const userPendingNotificationsQuery = useUserPendingNotifications();
+	const markUserNotificationsAsAddressedMutation =
+		useMarkUserNotificationsAsAddressedMutation();
+
+	const notification = userPendingNotificationsQuery.data?.[props.idx];
+	invariant(notification);
+
+	return (
+		<Card shadow="md">
+			<Card.Section withBorder p="xs">
+				<Text size="sm">{notification.message}</Text>
+			</Card.Section>
+			<Card.Section py={4} px="sm">
+				<Group wrap="nowrap" justify="space-between">
+					<Text size="xs" c="dimmed">
+						{dayjsLib(notification.createdOn).format("L")}
+					</Text>
+					<ActionIcon
+						size="xs"
+						variant="transparent"
+						loading={markUserNotificationsAsAddressedMutation.isPending}
+						onClick={() => {
+							markUserNotificationsAsAddressedMutation.mutate([
+								notification.id,
+							]);
+						}}
+					>
+						<IconCheck />
+					</ActionIcon>
+				</Group>
+			</Card.Section>
+		</Card>
+	);
+};
+
 const Footer = () => {
 	const coreDetails = useCoreDetails();
+	const [isNotificationModalOpen, setIsNotificationModalOpen] = useState(false);
+	const [parent] = useAutoAnimate();
+
+	const userPendingNotificationsQuery = useUserPendingNotifications();
+	const markUserNotificationsAsAddressedMutation =
+		useMarkUserNotificationsAsAddressedMutation();
 
 	return (
-		<Stack>
-			<Flex gap={80} justify="center">
-				{!coreDetails.isServerKeyValidated ? (
-					<Anchor href={coreDetails.websiteUrl} target="_blank">
-						<Text c="red" fw="bold">
-							Ryot Pro
+		<>
+			<Modal
+				centered
+				opened={isNotificationModalOpen}
+				onClose={() => setIsNotificationModalOpen(false)}
+				title={`You have ${userPendingNotificationsQuery.data?.length} pending notifications`}
+			>
+				<Stack ref={parent}>
+					{userPendingNotificationsQuery.data?.map((n, idx) => (
+						<DisplayNotificationContent idx={idx} key={n.id} />
+					))}
+					{(userPendingNotificationsQuery.data?.length || 0) > 0 ? (
+						<Button
+							ta="right"
+							variant="subtle"
+							size="compact-md"
+							rightSection={<IconChecks />}
+							onClick={() => {
+								const ids = userPendingNotificationsQuery.data?.map(
+									(n) => n.id,
+								);
+								if (!ids) return;
+								notifications.show({
+									color: "green",
+									message: "All notifications will be marked as read",
+								});
+								markUserNotificationsAsAddressedMutation.mutate(ids);
+								setIsNotificationModalOpen(false);
+							}}
+						>
+							Mark all as read
+						</Button>
+					) : (
+						<Text ta="center">No notifications</Text>
+					)}
+				</Stack>
+			</Modal>
+			<Container>
+				<Stack>
+					{userPendingNotificationsQuery.data &&
+					userPendingNotificationsQuery.data.length > 0 ? (
+						<Alert
+							icon={<IconBellRinging />}
+							style={{ cursor: "pointer" }}
+							onClick={() => setIsNotificationModalOpen(true)}
+						>
+							You have {userPendingNotificationsQuery.data.length} pending
+							notifications
+						</Alert>
+					) : null}
+					<Flex gap={80} justify="center">
+						{!coreDetails.isServerKeyValidated ? (
+							<Anchor href={coreDetails.websiteUrl} target="_blank">
+								<Text c="red" fw="bold">
+									Ryot Pro
+								</Text>
+							</Anchor>
+						) : null}
+						<Anchor href={discordLink} target="_blank">
+							<Text c="indigo" fw="bold">
+								Discord
+							</Text>
+						</Anchor>
+						<Text c="grape" fw="bold" visibleFrom="md">
+							{coreDetails.version}
 						</Text>
-					</Anchor>
-				) : null}
-				<Anchor href={discordLink} target="_blank">
-					<Text c="indigo" fw="bold">
-						Discord
-					</Text>
-				</Anchor>
-				<Text c="grape" fw="bold" visibleFrom="md">
-					{coreDetails.version}
-				</Text>
-				<Anchor href={coreDetails.repositoryLink} target="_blank">
-					<Text c="orange" fw="bold">
-						Github
-					</Text>
-				</Anchor>
-			</Flex>
-		</Stack>
+						<Anchor href={coreDetails.repositoryLink} target="_blank">
+							<Text c="orange" fw="bold">
+								Github
+							</Text>
+						</Anchor>
+					</Flex>
+				</Stack>
+			</Container>
+		</>
 	);
 };
 
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 57bbc838a1..a219f2f0fb 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -12,15 +12,15 @@
 		"@formkit/auto-animate": "0.8.2",
 		"@hello-pangea/dnd": "17.0.0",
 		"@lukemorales/query-key-factory": "1.3.4",
-		"@mantine/carousel": "7.15.2",
-		"@mantine/charts": "7.15.2",
-		"@mantine/code-highlight": "7.15.2",
-		"@mantine/core": "7.15.2",
-		"@mantine/dates": "7.15.2",
-		"@mantine/form": "7.15.2",
-		"@mantine/hooks": "7.15.2",
-		"@mantine/modals": "7.15.2",
-		"@mantine/notifications": "7.15.2",
+		"@mantine/carousel": "7.16.1",
+		"@mantine/charts": "7.16.1",
+		"@mantine/code-highlight": "7.16.1",
+		"@mantine/core": "7.16.1",
+		"@mantine/dates": "7.16.1",
+		"@mantine/form": "7.16.1",
+		"@mantine/hooks": "7.16.1",
+		"@mantine/modals": "7.16.1",
+		"@mantine/notifications": "7.16.1",
 		"@remix-pwa/sw": "3.0.10",
 		"@remix-pwa/worker-runtime": "2.1.4",
 		"@remix-run/node": "2.15.2",
@@ -30,8 +30,8 @@
 		"@ryot/graphql": "workspace:*",
 		"@ryot/ts-utils": "workspace:*",
 		"@tabler/icons-react": "3.17.0",
-		"@tanstack/react-query": "5.62.11",
-		"@tanstack/react-query-devtools": "5.62.11",
+		"@tanstack/react-query": "5.64.2",
+		"@tanstack/react-query-devtools": "5.64.2",
 		"clsx": "2.1.1",
 		"cookie": "1.0.2",
 		"crypto-js": "4.2.0",
@@ -45,8 +45,8 @@
 		"html2canvas": "1.4.1",
 		"humanize-duration-ts": "2.1.1",
 		"immer": "10.1.1",
-		"isbot": "5.1.19",
-		"jotai": "2.11.0",
+		"isbot": "5.1.21",
+		"jotai": "2.11.1",
 		"js-cookie": "3.0.5",
 		"jwt-decode": "4.0.0",
 		"mantine-datatable": "7.15.1",
@@ -59,10 +59,10 @@
 		"remix-utils": "7.7.0",
 		"tailwind-merge": "2.6.0",
 		"tiny-invariant": "1.3.3",
-		"ts-pattern": "5.6.0",
+		"ts-pattern": "5.6.2",
 		"ufo": "1.5.4",
 		"usehooks-ts": "3.1.0",
-		"uuid": "11.0.3",
+		"uuid": "11.0.5",
 		"zod": "3.24.1",
 		"zodix": "0.4.4"
 	},
@@ -77,15 +77,15 @@
 		"@types/react": "18.3.18",
 		"@types/react-dom": "18.3.5",
 		"@types/uuid": "10.0.0",
-		"postcss": "8.4.49",
+		"postcss": "8.5.1",
 		"postcss-preset-mantine": "1.17.0",
 		"postcss-simple-vars": "7.0.1",
 		"remix-development-tools": "4.7.7",
 		"ts-essentials": "10.0.4",
-		"typescript": "5.7.2",
+		"typescript": "5.7.3",
 		"typescript-plugin-css-modules": "5.1.0",
 		"typescript-remix-routes-plugin": "1.0.1",
-		"vite": "5.4.11",
+		"vite": "5.4.14",
 		"vite-tsconfig-paths": "5.1.4"
 	},
 	"engines": {
diff --git a/apps/website/package.json b/apps/website/package.json
index cb5cb10374..93f8087c53 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -10,11 +10,11 @@
 		"@conform-to/zod": "1.2.2",
 		"@isaacs/ttlcache": "1.4.1",
 		"@paddle/paddle-js": "1.3.3",
-		"@paddle/paddle-node-sdk": "2.2.2",
+		"@paddle/paddle-node-sdk": "2.4.0",
 		"@radix-ui/react-label": "2.1.1",
 		"@radix-ui/react-slot": "1.1.1",
 		"@radix-ui/react-tooltip": "1.1.6",
-		"@react-email/components": "0.0.31",
+		"@react-email/components": "0.0.32",
 		"@remix-run/node": "2.15.2",
 		"@remix-run/react": "2.15.2",
 		"@remix-run/serve": "2.15.2",
@@ -23,21 +23,21 @@
 		"@ryot/transactional": "workspace:*",
 		"@ryot/ts-utils": "workspace:*",
 		"@tabler/icons-react": "3.17.0",
-		"@unkey/api": "0.29.0",
+		"@unkey/api": "0.32.0",
 		"class-variance-authority": "0.7.1",
 		"clsx": "2.1.1",
 		"crypto-js": "4.2.0",
 		"dayjs": "1.11.13",
-		"drizzle-orm": "0.38.3",
-		"embla-carousel-autoplay": "8.5.1",
-		"embla-carousel-react": "8.5.1",
+		"drizzle-orm": "0.38.4",
+		"embla-carousel-autoplay": "8.5.2",
+		"embla-carousel-react": "8.5.2",
 		"graphql": "16.10.0",
 		"graphql-request": "7.1.2",
 		"humanize-duration-ts": "2.1.1",
-		"input-otp": "1.4.1",
-		"isbot": "5.1.19",
+		"input-otp": "1.4.2",
+		"isbot": "5.1.21",
 		"lodash": "4.17.21",
-		"lucide-react": "0.469.0",
+		"lucide-react": "0.473.0",
 		"nanoid": "5.0.9",
 		"next-themes": "0.4.4",
 		"nodemailer": "6.9.16",
@@ -47,30 +47,30 @@
 		"react-dom": "18.3.1",
 		"remix-routes": "1.7.7",
 		"remix-utils": "7.7.0",
-		"sonner": "1.7.1",
+		"sonner": "1.7.2",
 		"tailwind-merge": "2.6.0",
 		"tailwindcss-animate": "1.0.7",
 		"tiny-invariant": "1.3.3",
-		"ts-pattern": "5.6.0",
+		"ts-pattern": "5.6.2",
 		"ufo": "1.5.4",
 		"zod": "3.24.1",
 		"zodix": "0.4.4"
 	},
 	"devDependencies": {
 		"@remix-run/dev": "2.15.2",
-		"@tailwindcss/typography": "0.5.15",
+		"@tailwindcss/typography": "0.5.16",
 		"@types/crypto-js": "4.2.2",
-		"@types/lodash": "4.17.13",
+		"@types/lodash": "4.17.14",
 		"@types/nodemailer": "6.4.17",
 		"@types/react": "18.3.18",
 		"@types/react-dom": "18.3.5",
 		"autoprefixer": "10.4.20",
-		"drizzle-kit": "0.30.1",
-		"postcss": "8.4.49",
+		"drizzle-kit": "0.30.2",
+		"postcss": "8.5.1",
 		"tailwindcss": "3.4.17",
-		"typescript": "5.7.2",
+		"typescript": "5.7.3",
 		"typescript-remix-routes-plugin": "1.0.1",
-		"vite": "5.4.11",
+		"vite": "5.4.14",
 		"vite-tsconfig-paths": "5.1.4"
 	},
 	"engines": {
diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs
index fbd2f4c6c9..e381b230e3 100644
--- a/crates/config/src/lib.rs
+++ b/crates/config/src/lib.rs
@@ -294,7 +294,7 @@ pub struct FrontendUmamiConfig {
 #[config(rename_all = "snake_case", env_prefix = "FRONTEND_")]
 pub struct FrontendConfig {
     /// Used as the base URL when generating item links for the frontend.
-    #[setting(default = "https://pro.ryot.io")]
+    #[setting(default = "https://app.ryot.io")]
     pub url: String,
     /// The button label for OIDC authentication.
     #[setting(default = "Continue with OpenID Connect")]
@@ -363,7 +363,7 @@ pub struct ServerConfig {
     pub progress_update_threshold: i64,
     /// The maximum file size in MB for user uploads.
     #[setting(default = 70)]
-    pub max_file_size: usize,
+    pub max_file_size_mb: usize,
     /// Whether the graphql playground will be enabled.
     #[setting(default = true)]
     pub graphql_playground_enabled: bool,
diff --git a/crates/migrations/src/lib.rs b/crates/migrations/src/lib.rs
index 7fbc2d360e..d364048024 100644
--- a/crates/migrations/src/lib.rs
+++ b/crates/migrations/src/lib.rs
@@ -1,82 +1,44 @@
 use sea_orm::entity::prelude::*;
 use sea_orm_migration::prelude::*;
 
-mod m20230409_create_extensions;
+mod m20230403_create_extensions;
+mod m20230404_create_user;
 mod m20230410_create_metadata;
+mod m20230411_create_metadata_group;
 mod m20230413_create_person;
-mod m20230417_create_user;
 mod m20230419_create_seen;
-mod m20230501_create_metadata_group;
 mod m20230502_create_genre;
 mod m20230504_create_collection;
-mod m20230505_create_review;
+mod m20230505_create_exercise;
+mod m20230506_create_workout_template;
+mod m20230507_create_workout;
+mod m20230508_create_review;
 mod m20230509_create_import_report;
-mod m20230818_create_workout_template;
-mod m20230819_create_workout;
 mod m20230820_create_user_measurement;
-mod m20230822_create_exercise;
 mod m20230912_create_calendar_event;
 mod m20231016_create_collection_to_entity;
 mod m20231017_create_user_to_entity;
 mod m20231219_create_metadata_relations;
-mod m20240509_create_user_to_collection;
-mod m20240531_create_queued_notification;
 mod m20240607_create_integration;
 mod m20240712_create_notification_platform;
-mod m20240713_create_user_summary;
 mod m20240714_create_access_link;
-mod m20240825_is_v7_migration;
 mod m20240827_create_daily_user_activity;
-mod m20240827_zz_changes_for_daily_user_activity;
-mod m20240828_add_last_login_on_column_to_user;
-mod m20240828_zz_add_columns_to_daily_user_activity;
-mod m20240829_change_structure_for_exercise_extra_information;
-mod m20240831_add_is_account_default_column_to_access_link;
-mod m20240831_add_templates_key_to_preferences;
-mod m20240903_add_changes_for_user_to_collection_removal;
 mod m20240904_create_monitored_entity;
-mod m20240918_add_default_rest_timer_to_workout_template;
-mod m20240923_remove_extra_columns_for_daily_user_activities;
-mod m20240926_add_columns_for_open_sourcing_pro_version;
-mod m20240928_add_grid_packing_to_general_preferences;
-mod m20241002_add_columns_for_associating_seen_with_reviews;
 mod m20241004_create_application_cache;
-mod m20241006_changes_for_issue_1056;
-mod m20241010_changes_for_issue_708;
-mod m20241013_changes_for_issue_1052;
-mod m20241019_changes_for_issue_929;
-mod m20241019_changes_for_issue_964;
-mod m20241025_changes_for_issue_1084;
-mod m20241110_changes_for_issue_1103;
-mod m20241121_changes_for_issue_445;
-mod m20241124_changes_for_issue_1118;
-mod m20241126_changes_for_issue_1113;
-mod m20241129_changes_for_issue_1114;
 mod m20241214_create_user_notification;
-mod m20241215_changes_for_issue_1131;
-mod m20241220_changes_for_issue_1141;
-mod m20241220_changes_for_issue_49;
-mod m20241224_changes_for_issue_1139;
-mod m20241227_changes_for_issue_1157;
-mod m20241228_changes_for_issue_1130;
-mod m20241229_changes_for_issue_1155;
-mod m20250112_changes_for_issue_1174;
-mod m20250113_changes_for_issue_1169;
-mod m20250115_changes_for_issue_1110;
-mod m20250116_changes_for_issue_1161;
-mod m20250117_is_last_v7_migration;
+mod m20250118_is_v8_migration;
 
+pub use m20230404_create_user::User as AliasedUser;
 pub use m20230410_create_metadata::Metadata as AliasedMetadata;
+pub use m20230411_create_metadata_group::MetadataGroup as AliasedMetadataGroup;
 pub use m20230413_create_person::Person as AliasedPerson;
-pub use m20230417_create_user::User as AliasedUser;
 pub use m20230419_create_seen::Seen as AliasedSeen;
-pub use m20230501_create_metadata_group::MetadataGroup as AliasedMetadataGroup;
 pub use m20230502_create_genre::{
     Genre as AliasedGenre, MetadataToGenre as AliasedMetadataToGenre,
 };
 pub use m20230504_create_collection::Collection as AliasedCollection;
-pub use m20230505_create_review::Review as AliasedReview;
-pub use m20230822_create_exercise::Exercise as AliasedExercise;
+pub use m20230505_create_exercise::Exercise as AliasedExercise;
+pub use m20230508_create_review::Review as AliasedReview;
 pub use m20230912_create_calendar_event::CalendarEvent as AliasedCalendarEvent;
 pub use m20231016_create_collection_to_entity::CollectionToEntity as AliasedCollectionToEntity;
 pub use m20231017_create_user_to_entity::UserToEntity as AliasedUserToEntity;
@@ -87,70 +49,32 @@ pub struct Migrator;
 impl MigratorTrait for Migrator {
     fn migrations() -> Vec<Box<dyn MigrationTrait>> {
         vec![
-            Box::new(m20230409_create_extensions::Migration),
-            Box::new(m20230417_create_user::Migration),
+            Box::new(m20230403_create_extensions::Migration),
+            Box::new(m20230404_create_user::Migration),
             Box::new(m20230410_create_metadata::Migration),
-            Box::new(m20230501_create_metadata_group::Migration),
+            Box::new(m20230411_create_metadata_group::Migration),
             Box::new(m20230413_create_person::Migration),
             Box::new(m20230419_create_seen::Migration),
             Box::new(m20230502_create_genre::Migration),
             Box::new(m20230504_create_collection::Migration),
-            Box::new(m20230822_create_exercise::Migration),
-            Box::new(m20230818_create_workout_template::Migration),
-            Box::new(m20230819_create_workout::Migration),
-            Box::new(m20230505_create_review::Migration),
+            Box::new(m20230505_create_exercise::Migration),
+            Box::new(m20230506_create_workout_template::Migration),
+            Box::new(m20230507_create_workout::Migration),
+            Box::new(m20230508_create_review::Migration),
             Box::new(m20230509_create_import_report::Migration),
             Box::new(m20230820_create_user_measurement::Migration),
             Box::new(m20230912_create_calendar_event::Migration),
             Box::new(m20231016_create_collection_to_entity::Migration),
             Box::new(m20231017_create_user_to_entity::Migration),
             Box::new(m20231219_create_metadata_relations::Migration),
-            Box::new(m20240509_create_user_to_collection::Migration),
-            Box::new(m20240531_create_queued_notification::Migration),
             Box::new(m20240607_create_integration::Migration),
             Box::new(m20240712_create_notification_platform::Migration),
-            Box::new(m20240713_create_user_summary::Migration),
             Box::new(m20240714_create_access_link::Migration),
-            Box::new(m20240825_is_v7_migration::Migration),
             Box::new(m20240827_create_daily_user_activity::Migration),
-            Box::new(m20240827_zz_changes_for_daily_user_activity::Migration),
-            Box::new(m20240828_add_last_login_on_column_to_user::Migration),
-            Box::new(m20240828_zz_add_columns_to_daily_user_activity::Migration),
-            Box::new(m20240829_change_structure_for_exercise_extra_information::Migration),
-            Box::new(m20240831_add_templates_key_to_preferences::Migration),
-            Box::new(m20240831_add_is_account_default_column_to_access_link::Migration),
-            Box::new(m20240903_add_changes_for_user_to_collection_removal::Migration),
             Box::new(m20240904_create_monitored_entity::Migration),
-            Box::new(m20240918_add_default_rest_timer_to_workout_template::Migration),
-            Box::new(m20240923_remove_extra_columns_for_daily_user_activities::Migration),
-            Box::new(m20240926_add_columns_for_open_sourcing_pro_version::Migration),
-            Box::new(m20240928_add_grid_packing_to_general_preferences::Migration),
-            Box::new(m20241002_add_columns_for_associating_seen_with_reviews::Migration),
             Box::new(m20241004_create_application_cache::Migration),
-            Box::new(m20241006_changes_for_issue_1056::Migration),
-            Box::new(m20241010_changes_for_issue_708::Migration),
-            Box::new(m20241013_changes_for_issue_1052::Migration),
-            Box::new(m20241019_changes_for_issue_929::Migration),
-            Box::new(m20241019_changes_for_issue_964::Migration),
-            Box::new(m20241025_changes_for_issue_1084::Migration),
-            Box::new(m20241110_changes_for_issue_1103::Migration),
-            Box::new(m20241121_changes_for_issue_445::Migration),
-            Box::new(m20241124_changes_for_issue_1118::Migration),
-            Box::new(m20241129_changes_for_issue_1114::Migration),
-            Box::new(m20241126_changes_for_issue_1113::Migration),
             Box::new(m20241214_create_user_notification::Migration),
-            Box::new(m20241215_changes_for_issue_1131::Migration),
-            Box::new(m20241220_changes_for_issue_49::Migration),
-            Box::new(m20241220_changes_for_issue_1141::Migration),
-            Box::new(m20241224_changes_for_issue_1139::Migration),
-            Box::new(m20241227_changes_for_issue_1157::Migration),
-            Box::new(m20241228_changes_for_issue_1130::Migration),
-            Box::new(m20241229_changes_for_issue_1155::Migration),
-            Box::new(m20250112_changes_for_issue_1174::Migration),
-            Box::new(m20250113_changes_for_issue_1169::Migration),
-            Box::new(m20250115_changes_for_issue_1110::Migration),
-            Box::new(m20250116_changes_for_issue_1161::Migration),
-            Box::new(m20250117_is_last_v7_migration::Migration),
+            Box::new(m20250118_is_v8_migration::Migration),
         ]
     }
 }
diff --git a/crates/migrations/src/m20230409_create_extensions.rs b/crates/migrations/src/m20230403_create_extensions.rs
similarity index 86%
rename from crates/migrations/src/m20230409_create_extensions.rs
rename to crates/migrations/src/m20230403_create_extensions.rs
index 708718d348..c9e77f17b3 100644
--- a/crates/migrations/src/m20230409_create_extensions.rs
+++ b/crates/migrations/src/m20230403_create_extensions.rs
@@ -1,4 +1,3 @@
-// FIXME: Rename this to m20230403_create_extensions in the next major release
 use sea_orm_migration::prelude::*;
 
 #[derive(DeriveMigrationName)]
diff --git a/crates/migrations/src/m20230417_create_user.rs b/crates/migrations/src/m20230404_create_user.rs
similarity index 95%
rename from crates/migrations/src/m20230417_create_user.rs
rename to crates/migrations/src/m20230404_create_user.rs
index a0f438dc58..16e7173ff8 100644
--- a/crates/migrations/src/m20230417_create_user.rs
+++ b/crates/migrations/src/m20230404_create_user.rs
@@ -1,4 +1,3 @@
-// FIXME: Rename this to m20230404_create_user in the next major release
 use sea_orm_migration::prelude::*;
 
 #[derive(DeriveMigrationName)]
@@ -16,6 +15,7 @@ pub enum User {
     LastLoginOn,
     Preferences,
     OidcIssuerId,
+    LastActivityOn,
     ExtraInformation,
 }
 
@@ -41,6 +41,7 @@ impl MigrationTrait for Migration {
                     .col(ColumnDef::new(User::ExtraInformation).json_binary())
                     .col(ColumnDef::new(User::IsDisabled).boolean())
                     .col(ColumnDef::new(User::LastLoginOn).timestamp_with_time_zone())
+                    .col(ColumnDef::new(User::LastActivityOn).timestamp_with_time_zone())
                     .to_owned(),
             )
             .await?;
diff --git a/crates/migrations/src/m20230410_create_metadata.rs b/crates/migrations/src/m20230410_create_metadata.rs
index ec83f3d6eb..904d679992 100644
--- a/crates/migrations/src/m20230410_create_metadata.rs
+++ b/crates/migrations/src/m20230410_create_metadata.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20230501_create_metadata_group.rs b/crates/migrations/src/m20230411_create_metadata_group.rs
similarity index 96%
rename from crates/migrations/src/m20230501_create_metadata_group.rs
rename to crates/migrations/src/m20230411_create_metadata_group.rs
index 6983f2de40..204292ecc8 100644
--- a/crates/migrations/src/m20230501_create_metadata_group.rs
+++ b/crates/migrations/src/m20230411_create_metadata_group.rs
@@ -1,4 +1,3 @@
-// FIXME: Rename this to m20230411_create_metadata_group in the next major release
 use sea_orm_migration::prelude::*;
 
 #[derive(DeriveMigrationName)]
diff --git a/crates/migrations/src/m20230413_create_person.rs b/crates/migrations/src/m20230413_create_person.rs
index 2ab0323896..505c1e250f 100644
--- a/crates/migrations/src/m20230413_create_person.rs
+++ b/crates/migrations/src/m20230413_create_person.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::{m20230410_create_metadata::Metadata, m20230501_create_metadata_group::MetadataGroup};
+use super::{m20230410_create_metadata::Metadata, m20230411_create_metadata_group::MetadataGroup};
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
@@ -52,65 +52,6 @@ pub enum MetadataGroupToPerson {
     MetadataGroupId,
 }
 
-// FIXME: Remove this function and move it to the main function.
-pub async fn create_metadata_group_to_person_table(
-    manager: &SchemaManager<'_>,
-) -> Result<(), DbErr> {
-    manager
-        .create_table(
-            Table::create()
-                .table(MetadataGroupToPerson::Table)
-                .col(
-                    ColumnDef::new(MetadataGroupToPerson::Role)
-                        .text()
-                        .not_null(),
-                )
-                .col(
-                    ColumnDef::new(MetadataGroupToPerson::PersonId)
-                        .text()
-                        .not_null(),
-                )
-                .col(
-                    ColumnDef::new(MetadataGroupToPerson::MetadataGroupId)
-                        .text()
-                        .not_null(),
-                )
-                .col(ColumnDef::new(MetadataGroupToPerson::Index).integer())
-                .primary_key(
-                    Index::create()
-                        .name("metadata_group_to_person-primary-key")
-                        .col(MetadataGroupToPerson::MetadataGroupId)
-                        .col(MetadataGroupToPerson::PersonId)
-                        .col(MetadataGroupToPerson::Role),
-                )
-                .foreign_key(
-                    ForeignKey::create()
-                        .name("fk-metadata_group-item_person-person_id")
-                        .from(
-                            MetadataGroupToPerson::Table,
-                            MetadataGroupToPerson::PersonId,
-                        )
-                        .to(Person::Table, Person::Id)
-                        .on_delete(ForeignKeyAction::Cascade)
-                        .on_update(ForeignKeyAction::Cascade),
-                )
-                .foreign_key(
-                    ForeignKey::create()
-                        .name("fk-metadata_group-item_person-metadata_group_id")
-                        .from(
-                            MetadataGroupToPerson::Table,
-                            MetadataGroupToPerson::MetadataGroupId,
-                        )
-                        .to(MetadataGroup::Table, MetadataGroup::Id)
-                        .on_delete(ForeignKeyAction::Cascade)
-                        .on_update(ForeignKeyAction::Cascade),
-                )
-                .to_owned(),
-        )
-        .await?;
-    Ok(())
-}
-
 #[async_trait::async_trait]
 impl MigrationTrait for Migration {
     async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
@@ -201,7 +142,58 @@ impl MigrationTrait for Migration {
                     .to_owned(),
             )
             .await?;
-        create_metadata_group_to_person_table(manager).await?;
+        manager
+            .create_table(
+                Table::create()
+                    .table(MetadataGroupToPerson::Table)
+                    .col(
+                        ColumnDef::new(MetadataGroupToPerson::Role)
+                            .text()
+                            .not_null(),
+                    )
+                    .col(
+                        ColumnDef::new(MetadataGroupToPerson::PersonId)
+                            .text()
+                            .not_null(),
+                    )
+                    .col(
+                        ColumnDef::new(MetadataGroupToPerson::MetadataGroupId)
+                            .text()
+                            .not_null(),
+                    )
+                    .col(ColumnDef::new(MetadataGroupToPerson::Index).integer())
+                    .primary_key(
+                        Index::create()
+                            .name("metadata_group_to_person-primary-key")
+                            .col(MetadataGroupToPerson::MetadataGroupId)
+                            .col(MetadataGroupToPerson::PersonId)
+                            .col(MetadataGroupToPerson::Role),
+                    )
+                    .foreign_key(
+                        ForeignKey::create()
+                            .name("fk-metadata_group-item_person-person_id")
+                            .from(
+                                MetadataGroupToPerson::Table,
+                                MetadataGroupToPerson::PersonId,
+                            )
+                            .to(Person::Table, Person::Id)
+                            .on_delete(ForeignKeyAction::Cascade)
+                            .on_update(ForeignKeyAction::Cascade),
+                    )
+                    .foreign_key(
+                        ForeignKey::create()
+                            .name("fk-metadata_group-item_person-metadata_group_id")
+                            .from(
+                                MetadataGroupToPerson::Table,
+                                MetadataGroupToPerson::MetadataGroupId,
+                            )
+                            .to(MetadataGroup::Table, MetadataGroup::Id)
+                            .on_delete(ForeignKeyAction::Cascade)
+                            .on_update(ForeignKeyAction::Cascade),
+                    )
+                    .to_owned(),
+            )
+            .await?;
         Ok(())
     }
 
diff --git a/crates/migrations/src/m20230419_create_seen.rs b/crates/migrations/src/m20230419_create_seen.rs
index 15e0383217..47c6b22da7 100644
--- a/crates/migrations/src/m20230419_create_seen.rs
+++ b/crates/migrations/src/m20230419_create_seen.rs
@@ -2,7 +2,7 @@ use sea_orm_migration::prelude::*;
 
 use enum_models::SeenState;
 
-use super::{m20230410_create_metadata::Metadata, m20230417_create_user::User};
+use super::{m20230410_create_metadata::Metadata, m20230404_create_user::User};
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20230504_create_collection.rs b/crates/migrations/src/m20230504_create_collection.rs
index 6ee1a4a079..d61813f43e 100644
--- a/crates/migrations/src/m20230504_create_collection.rs
+++ b/crates/migrations/src/m20230504_create_collection.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 pub static COLLECTION_NAME_INDEX: &str = "collection__name__index";
 
diff --git a/crates/migrations/src/m20230822_create_exercise.rs b/crates/migrations/src/m20230505_create_exercise.rs
similarity index 95%
rename from crates/migrations/src/m20230822_create_exercise.rs
rename to crates/migrations/src/m20230505_create_exercise.rs
index 75d1704657..e62fbe0dab 100644
--- a/crates/migrations/src/m20230822_create_exercise.rs
+++ b/crates/migrations/src/m20230505_create_exercise.rs
@@ -1,7 +1,6 @@
-// FIXME: Rename this to m20230505_create_exercise in the next major release
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20230818_create_workout_template.rs b/crates/migrations/src/m20230506_create_workout_template.rs
similarity index 94%
rename from crates/migrations/src/m20230818_create_workout_template.rs
rename to crates/migrations/src/m20230506_create_workout_template.rs
index d555569622..447e2b0cc0 100644
--- a/crates/migrations/src/m20230818_create_workout_template.rs
+++ b/crates/migrations/src/m20230506_create_workout_template.rs
@@ -1,7 +1,6 @@
-// FIXME: Rename this to m20230506_create_workout_template in the next major release
 use sea_orm_migration::prelude::*;
 
-use crate::m20230417_create_user::User;
+use crate::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20230819_create_workout.rs b/crates/migrations/src/m20230507_create_workout.rs
similarity index 96%
rename from crates/migrations/src/m20230819_create_workout.rs
rename to crates/migrations/src/m20230507_create_workout.rs
index 132bc46374..f68b62e5e8 100644
--- a/crates/migrations/src/m20230819_create_workout.rs
+++ b/crates/migrations/src/m20230507_create_workout.rs
@@ -1,7 +1,6 @@
-// FIXME: Rename this to m20230507_create_workout in the next major release
 use sea_orm_migration::prelude::*;
 
-use super::{m20230417_create_user::User, m20230818_create_workout_template::WorkoutTemplate};
+use super::{m20230404_create_user::User, m20230506_create_workout_template::WorkoutTemplate};
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20230505_create_review.rs b/crates/migrations/src/m20230508_create_review.rs
similarity index 95%
rename from crates/migrations/src/m20230505_create_review.rs
rename to crates/migrations/src/m20230508_create_review.rs
index 11362b6dc1..d3b55a7c29 100644
--- a/crates/migrations/src/m20230505_create_review.rs
+++ b/crates/migrations/src/m20230508_create_review.rs
@@ -1,13 +1,12 @@
-// FIXME: Rename this to m20230508_create_review in the next major release
 use enum_models::Visibility;
 use indoc::indoc;
 use sea_orm_migration::prelude::*;
 
 use super::{
-    m20230410_create_metadata::Metadata, m20230413_create_person::Person,
-    m20230417_create_user::User, m20230419_create_seen::Seen,
-    m20230501_create_metadata_group::MetadataGroup, m20230504_create_collection::Collection,
-    m20230822_create_exercise::Exercise,
+    m20230404_create_user::User, m20230410_create_metadata::Metadata,
+    m20230411_create_metadata_group::MetadataGroup, m20230413_create_person::Person,
+    m20230419_create_seen::Seen, m20230504_create_collection::Collection,
+    m20230505_create_exercise::Exercise,
 };
 
 #[derive(DeriveMigrationName)]
diff --git a/crates/migrations/src/m20230509_create_import_report.rs b/crates/migrations/src/m20230509_create_import_report.rs
index 4c2ce9f336..bed01061dd 100644
--- a/crates/migrations/src/m20230509_create_import_report.rs
+++ b/crates/migrations/src/m20230509_create_import_report.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20230820_create_user_measurement.rs b/crates/migrations/src/m20230820_create_user_measurement.rs
index 39aed099a7..a4b6d1d760 100644
--- a/crates/migrations/src/m20230820_create_user_measurement.rs
+++ b/crates/migrations/src/m20230820_create_user_measurement.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20231016_create_collection_to_entity.rs b/crates/migrations/src/m20231016_create_collection_to_entity.rs
index 383283d182..c094011ad7 100644
--- a/crates/migrations/src/m20231016_create_collection_to_entity.rs
+++ b/crates/migrations/src/m20231016_create_collection_to_entity.rs
@@ -2,10 +2,10 @@ use indoc::indoc;
 use sea_orm_migration::prelude::*;
 
 use super::{
-    m20230410_create_metadata::Metadata, m20230413_create_person::Person,
-    m20230501_create_metadata_group::MetadataGroup, m20230504_create_collection::Collection,
-    m20230818_create_workout_template::WorkoutTemplate, m20230819_create_workout::Workout,
-    m20230822_create_exercise::Exercise,
+    m20230410_create_metadata::Metadata, m20230411_create_metadata_group::MetadataGroup,
+    m20230413_create_person::Person, m20230504_create_collection::Collection,
+    m20230505_create_exercise::Exercise, m20230506_create_workout_template::WorkoutTemplate,
+    m20230507_create_workout::Workout,
 };
 
 #[derive(DeriveMigrationName)]
diff --git a/crates/migrations/src/m20231017_create_user_to_entity.rs b/crates/migrations/src/m20231017_create_user_to_entity.rs
index c6fa8a4a02..ed466e23a7 100644
--- a/crates/migrations/src/m20231017_create_user_to_entity.rs
+++ b/crates/migrations/src/m20231017_create_user_to_entity.rs
@@ -3,8 +3,8 @@ use sea_orm_migration::prelude::*;
 
 use super::{
     m20230410_create_metadata::Metadata, m20230413_create_person::Person,
-    m20230417_create_user::User, m20230501_create_metadata_group::MetadataGroup,
-    m20230504_create_collection::Collection, m20230822_create_exercise::Exercise,
+    m20230404_create_user::User, m20230411_create_metadata_group::MetadataGroup,
+    m20230504_create_collection::Collection, m20230505_create_exercise::Exercise,
 };
 
 #[derive(DeriveMigrationName)]
diff --git a/crates/migrations/src/m20231219_create_metadata_relations.rs b/crates/migrations/src/m20231219_create_metadata_relations.rs
index 329cea0fb3..5681ddcfd5 100644
--- a/crates/migrations/src/m20231219_create_metadata_relations.rs
+++ b/crates/migrations/src/m20231219_create_metadata_relations.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::{m20230410_create_metadata::Metadata, m20230501_create_metadata_group::MetadataGroup};
+use super::{m20230410_create_metadata::Metadata, m20230411_create_metadata_group::MetadataGroup};
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20240509_create_user_to_collection.rs b/crates/migrations/src/m20240509_create_user_to_collection.rs
deleted file mode 100644
index e1acf2aba0..0000000000
--- a/crates/migrations/src/m20240509_create_user_to_collection.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-// FIXME: Delete this migration
-use sea_orm_migration::prelude::*;
-
-use super::{m20230417_create_user::User, m20230504_create_collection::Collection};
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[derive(Iden)]
-pub enum UserToCollection {
-    Table,
-    CollectionId,
-    UserId,
-}
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        manager
-            .create_table(
-                Table::create()
-                    .table(UserToCollection::Table)
-                    .col(
-                        ColumnDef::new(UserToCollection::CollectionId)
-                            .text()
-                            .not_null(),
-                    )
-                    .col(ColumnDef::new(UserToCollection::UserId).text())
-                    .foreign_key(
-                        ForeignKey::create()
-                            .name("user_to_collection-fk1")
-                            .from(UserToCollection::Table, UserToCollection::CollectionId)
-                            .to(Collection::Table, Collection::Id)
-                            .on_delete(ForeignKeyAction::Cascade)
-                            .on_update(ForeignKeyAction::Cascade),
-                    )
-                    .foreign_key(
-                        ForeignKey::create()
-                            .name("user_to_collection-fk2")
-                            .from(UserToCollection::Table, UserToCollection::UserId)
-                            .to(User::Table, User::Id)
-                            .on_delete(ForeignKeyAction::Cascade)
-                            .on_update(ForeignKeyAction::Cascade),
-                    )
-                    .primary_key(
-                        Index::create()
-                            .name("pk-user_to_collection")
-                            .col(UserToCollection::UserId)
-                            .col(UserToCollection::CollectionId),
-                    )
-                    .to_owned(),
-            )
-            .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240531_create_queued_notification.rs b/crates/migrations/src/m20240531_create_queued_notification.rs
deleted file mode 100644
index 9a4a9577a7..0000000000
--- a/crates/migrations/src/m20240531_create_queued_notification.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-// FIXME: Remove this migration completely
-use sea_orm_migration::prelude::*;
-
-use super::m20230417_create_user::User;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[derive(Iden)]
-pub enum QueuedNotification {
-    Table,
-    Id,
-    UserId,
-    Message,
-}
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        manager
-            .create_table(
-                Table::create()
-                    .table(QueuedNotification::Table)
-                    .col(
-                        ColumnDef::new(QueuedNotification::Id)
-                            .text()
-                            .not_null()
-                            .primary_key(),
-                    )
-                    .col(
-                        ColumnDef::new(QueuedNotification::Message)
-                            .text()
-                            .not_null(),
-                    )
-                    .col(ColumnDef::new(QueuedNotification::UserId).text().not_null())
-                    .foreign_key(
-                        ForeignKey::create()
-                            .name("queued_notification_to_user_foreign_key")
-                            .from(QueuedNotification::Table, QueuedNotification::UserId)
-                            .to(User::Table, User::Id)
-                            .on_delete(ForeignKeyAction::Cascade)
-                            .on_update(ForeignKeyAction::Cascade),
-                    )
-                    .to_owned(),
-            )
-            .await?;
-        manager
-            .create_index(
-                Index::create()
-                    .name("queued_notification__user_id__index")
-                    .table(QueuedNotification::Table)
-                    .col(QueuedNotification::UserId)
-                    .to_owned(),
-            )
-            .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240607_create_integration.rs b/crates/migrations/src/m20240607_create_integration.rs
index da4be0d037..32b7dc24da 100644
--- a/crates/migrations/src/m20240607_create_integration.rs
+++ b/crates/migrations/src/m20240607_create_integration.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20240712_create_notification_platform.rs b/crates/migrations/src/m20240712_create_notification_platform.rs
index 6504308144..09e632447f 100644
--- a/crates/migrations/src/m20240712_create_notification_platform.rs
+++ b/crates/migrations/src/m20240712_create_notification_platform.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20240713_create_user_summary.rs b/crates/migrations/src/m20240713_create_user_summary.rs
deleted file mode 100644
index 40d58fe5cf..0000000000
--- a/crates/migrations/src/m20240713_create_user_summary.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-// FIXME: Delete this file in the next major version
-
-use sea_orm_migration::prelude::*;
-
-use super::m20230417_create_user::User;
-
-pub static USER_STATISTIC_PRIMARY_KEY: &str = "pk-user_summary";
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[derive(Iden)]
-pub enum UserSummary {
-    Table,
-    UserId,
-    CalculatedOn,
-    IsFresh,
-    Data,
-}
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        manager
-            .create_table(
-                Table::create()
-                    .table(UserSummary::Table)
-                    .col(ColumnDef::new(UserSummary::UserId).text().not_null())
-                    .col(
-                        ColumnDef::new(UserSummary::CalculatedOn)
-                            .timestamp_with_time_zone()
-                            .not_null(),
-                    )
-                    .col(ColumnDef::new(UserSummary::IsFresh).boolean().not_null())
-                    .col(ColumnDef::new(UserSummary::Data).json_binary().not_null())
-                    .primary_key(
-                        Index::create()
-                            .name(USER_STATISTIC_PRIMARY_KEY)
-                            .col(UserSummary::UserId),
-                    )
-                    .foreign_key(
-                        ForeignKey::create()
-                            .name("user_summary_to_user_foreign_key")
-                            .from(UserSummary::Table, UserSummary::UserId)
-                            .to(User::Table, User::Id)
-                            .on_delete(ForeignKeyAction::Cascade)
-                            .on_update(ForeignKeyAction::Cascade),
-                    )
-                    .to_owned(),
-            )
-            .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240714_create_access_link.rs b/crates/migrations/src/m20240714_create_access_link.rs
index 32cc79857e..c0a70ea9e0 100644
--- a/crates/migrations/src/m20240714_create_access_link.rs
+++ b/crates/migrations/src/m20240714_create_access_link.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
diff --git a/crates/migrations/src/m20240825_is_v7_migration.rs b/crates/migrations/src/m20240825_is_v7_migration.rs
deleted file mode 100644
index 02dab03ac6..0000000000
--- a/crates/migrations/src/m20240825_is_v7_migration.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240827_create_daily_user_activity.rs b/crates/migrations/src/m20240827_create_daily_user_activity.rs
index 53ac31f60a..e76c3a38f2 100644
--- a/crates/migrations/src/m20240827_create_daily_user_activity.rs
+++ b/crates/migrations/src/m20240827_create_daily_user_activity.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 fn integer_not_null<T: IntoIden>(col: T) -> ColumnDef {
     ColumnDef::new(col).integer().not_null().default(0).take()
@@ -61,128 +61,122 @@ pub enum DailyUserActivity {
     WorkoutCaloriesBurnt,
 }
 
-// FIXME: Remove this function and move it to the main function.
-pub async fn create_daily_user_activity_table(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
-    manager
-        .create_table(
-            Table::create()
-                .table(DailyUserActivity::Table)
-                .col(
-                    ColumnDef::new(DailyUserActivity::Id)
-                        .uuid()
-                        .not_null()
-                        .default(PgFunc::gen_random_uuid())
-                        .primary_key(),
-                )
-                .col(ColumnDef::new(DailyUserActivity::Date).date())
-                .col(ColumnDef::new(DailyUserActivity::UserId).text().not_null())
-                .col(
-                    ColumnDef::new(DailyUserActivity::EntityIds)
-                        .array(ColumnType::Text)
-                        .not_null()
-                        .default(Expr::cust("'{}'")),
-                )
-                .col(integer_not_null(DailyUserActivity::MetadataReviewCount))
-                .col(integer_not_null(DailyUserActivity::CollectionReviewCount))
-                .col(integer_not_null(
-                    DailyUserActivity::MetadataGroupReviewCount,
-                ))
-                .col(integer_not_null(DailyUserActivity::PersonReviewCount))
-                .col(integer_not_null(DailyUserActivity::ExerciseReviewCount))
-                .col(integer_not_null(DailyUserActivity::WorkoutCount))
-                .col(integer_not_null(DailyUserActivity::WorkoutDuration))
-                .col(integer_not_null(DailyUserActivity::MeasurementCount))
-                .col(integer_not_null(DailyUserActivity::AudioBookCount))
-                .col(integer_not_null(DailyUserActivity::AudioBookDuration))
-                .col(integer_not_null(DailyUserActivity::AnimeCount))
-                .col(integer_not_null(DailyUserActivity::BookCount))
-                .col(integer_not_null(DailyUserActivity::BookPages))
-                .col(integer_not_null(DailyUserActivity::PodcastCount))
-                .col(integer_not_null(DailyUserActivity::PodcastDuration))
-                .col(integer_not_null(DailyUserActivity::MangaCount))
-                .col(integer_not_null(DailyUserActivity::MovieCount))
-                .col(integer_not_null(DailyUserActivity::MovieDuration))
-                .col(integer_not_null(DailyUserActivity::MusicCount))
-                .col(integer_not_null(DailyUserActivity::MusicDuration))
-                .col(integer_not_null(DailyUserActivity::ShowCount))
-                .col(integer_not_null(DailyUserActivity::ShowDuration))
-                .col(integer_not_null(DailyUserActivity::VideoGameCount))
-                .col(integer_not_null(DailyUserActivity::VideoGameDuration))
-                .col(integer_not_null(DailyUserActivity::VisualNovelCount))
-                .col(integer_not_null(DailyUserActivity::VisualNovelDuration))
-                .col(integer_not_null(DailyUserActivity::WorkoutPersonalBests))
-                .col(integer_not_null(DailyUserActivity::WorkoutWeight))
-                .col(integer_not_null(DailyUserActivity::WorkoutReps))
-                .col(integer_not_null(DailyUserActivity::WorkoutDistance))
-                .col(integer_not_null(DailyUserActivity::WorkoutRestTime))
-                .col(integer_not_null(DailyUserActivity::TotalMetadataCount))
-                .col(integer_not_null(DailyUserActivity::TotalReviewCount))
-                .col(integer_not_null(DailyUserActivity::TotalCount))
-                .col(integer_not_null(DailyUserActivity::TotalDuration))
-                .col(integer_not_null(DailyUserActivity::WorkoutCaloriesBurnt))
-                .col(
-                    ColumnDef::new(DailyUserActivity::HourRecords)
-                        .json_binary()
-                        .not_null()
-                        .default(Expr::cust("'[]'")),
-                )
-                .col(
-                    ColumnDef::new(DailyUserActivity::WorkoutMuscles)
-                        .array(ColumnType::Text)
-                        .not_null()
-                        .default(Expr::cust("'{}'")),
-                )
-                .col(
-                    ColumnDef::new(DailyUserActivity::WorkoutExercises)
-                        .array(ColumnType::Text)
-                        .not_null()
-                        .default(Expr::cust("'{}'")),
-                )
-                .col(
-                    ColumnDef::new(DailyUserActivity::WorkoutEquipments)
-                        .array(ColumnType::Text)
-                        .not_null()
-                        .default(Expr::cust("'{}'")),
-                )
-                .foreign_key(
-                    ForeignKey::create()
-                        .name("daily_user_activity_to_user_foreign_key")
-                        .from(DailyUserActivity::Table, DailyUserActivity::UserId)
-                        .to(User::Table, User::Id)
-                        .on_delete(ForeignKeyAction::Cascade)
-                        .on_update(ForeignKeyAction::Cascade),
-                )
-                .to_owned(),
-        )
-        .await?;
-    manager
-        .create_index(
-            Index::create()
-                .name(DAILY_USER_ACTIVITY_COMPOSITE_UNIQUE_KEY)
-                .unique()
-                .nulls_not_distinct()
-                .table(DailyUserActivity::Table)
-                .col(DailyUserActivity::UserId)
-                .col(DailyUserActivity::Date)
-                .to_owned(),
-        )
-        .await?;
-    manager
-        .create_index(
-            Index::create()
-                .name("daily_user_activity-user_id__index")
-                .table(DailyUserActivity::Table)
-                .col(DailyUserActivity::UserId)
-                .to_owned(),
-        )
-        .await?;
-    Ok(())
-}
-
 #[async_trait::async_trait]
 impl MigrationTrait for Migration {
     async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        create_daily_user_activity_table(manager).await?;
+        manager
+            .create_table(
+                Table::create()
+                    .table(DailyUserActivity::Table)
+                    .col(
+                        ColumnDef::new(DailyUserActivity::Id)
+                            .uuid()
+                            .not_null()
+                            .default(PgFunc::gen_random_uuid())
+                            .primary_key(),
+                    )
+                    .col(ColumnDef::new(DailyUserActivity::Date).date())
+                    .col(ColumnDef::new(DailyUserActivity::UserId).text().not_null())
+                    .col(
+                        ColumnDef::new(DailyUserActivity::EntityIds)
+                            .array(ColumnType::Text)
+                            .not_null()
+                            .default(Expr::cust("'{}'")),
+                    )
+                    .col(integer_not_null(DailyUserActivity::MetadataReviewCount))
+                    .col(integer_not_null(DailyUserActivity::CollectionReviewCount))
+                    .col(integer_not_null(
+                        DailyUserActivity::MetadataGroupReviewCount,
+                    ))
+                    .col(integer_not_null(DailyUserActivity::PersonReviewCount))
+                    .col(integer_not_null(DailyUserActivity::ExerciseReviewCount))
+                    .col(integer_not_null(DailyUserActivity::WorkoutCount))
+                    .col(integer_not_null(DailyUserActivity::WorkoutDuration))
+                    .col(integer_not_null(DailyUserActivity::MeasurementCount))
+                    .col(integer_not_null(DailyUserActivity::AudioBookCount))
+                    .col(integer_not_null(DailyUserActivity::AudioBookDuration))
+                    .col(integer_not_null(DailyUserActivity::AnimeCount))
+                    .col(integer_not_null(DailyUserActivity::BookCount))
+                    .col(integer_not_null(DailyUserActivity::BookPages))
+                    .col(integer_not_null(DailyUserActivity::PodcastCount))
+                    .col(integer_not_null(DailyUserActivity::PodcastDuration))
+                    .col(integer_not_null(DailyUserActivity::MangaCount))
+                    .col(integer_not_null(DailyUserActivity::MovieCount))
+                    .col(integer_not_null(DailyUserActivity::MovieDuration))
+                    .col(integer_not_null(DailyUserActivity::MusicCount))
+                    .col(integer_not_null(DailyUserActivity::MusicDuration))
+                    .col(integer_not_null(DailyUserActivity::ShowCount))
+                    .col(integer_not_null(DailyUserActivity::ShowDuration))
+                    .col(integer_not_null(DailyUserActivity::VideoGameCount))
+                    .col(integer_not_null(DailyUserActivity::VideoGameDuration))
+                    .col(integer_not_null(DailyUserActivity::VisualNovelCount))
+                    .col(integer_not_null(DailyUserActivity::VisualNovelDuration))
+                    .col(integer_not_null(DailyUserActivity::WorkoutPersonalBests))
+                    .col(integer_not_null(DailyUserActivity::WorkoutWeight))
+                    .col(integer_not_null(DailyUserActivity::WorkoutReps))
+                    .col(integer_not_null(DailyUserActivity::WorkoutDistance))
+                    .col(integer_not_null(DailyUserActivity::WorkoutRestTime))
+                    .col(integer_not_null(DailyUserActivity::TotalMetadataCount))
+                    .col(integer_not_null(DailyUserActivity::TotalReviewCount))
+                    .col(integer_not_null(DailyUserActivity::TotalCount))
+                    .col(integer_not_null(DailyUserActivity::TotalDuration))
+                    .col(integer_not_null(DailyUserActivity::WorkoutCaloriesBurnt))
+                    .col(
+                        ColumnDef::new(DailyUserActivity::HourRecords)
+                            .json_binary()
+                            .not_null()
+                            .default(Expr::cust("'[]'")),
+                    )
+                    .col(
+                        ColumnDef::new(DailyUserActivity::WorkoutMuscles)
+                            .array(ColumnType::Text)
+                            .not_null()
+                            .default(Expr::cust("'{}'")),
+                    )
+                    .col(
+                        ColumnDef::new(DailyUserActivity::WorkoutExercises)
+                            .array(ColumnType::Text)
+                            .not_null()
+                            .default(Expr::cust("'{}'")),
+                    )
+                    .col(
+                        ColumnDef::new(DailyUserActivity::WorkoutEquipments)
+                            .array(ColumnType::Text)
+                            .not_null()
+                            .default(Expr::cust("'{}'")),
+                    )
+                    .foreign_key(
+                        ForeignKey::create()
+                            .name("daily_user_activity_to_user_foreign_key")
+                            .from(DailyUserActivity::Table, DailyUserActivity::UserId)
+                            .to(User::Table, User::Id)
+                            .on_delete(ForeignKeyAction::Cascade)
+                            .on_update(ForeignKeyAction::Cascade),
+                    )
+                    .to_owned(),
+            )
+            .await?;
+        manager
+            .create_index(
+                Index::create()
+                    .name(DAILY_USER_ACTIVITY_COMPOSITE_UNIQUE_KEY)
+                    .unique()
+                    .nulls_not_distinct()
+                    .table(DailyUserActivity::Table)
+                    .col(DailyUserActivity::UserId)
+                    .col(DailyUserActivity::Date)
+                    .to_owned(),
+            )
+            .await?;
+        manager
+            .create_index(
+                Index::create()
+                    .name("daily_user_activity-user_id__index")
+                    .table(DailyUserActivity::Table)
+                    .col(DailyUserActivity::UserId)
+                    .to_owned(),
+            )
+            .await?;
         Ok(())
     }
 
diff --git a/crates/migrations/src/m20240827_zz_changes_for_daily_user_activity.rs b/crates/migrations/src/m20240827_zz_changes_for_daily_user_activity.rs
deleted file mode 100644
index 771d176716..0000000000
--- a/crates/migrations/src/m20240827_zz_changes_for_daily_user_activity.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "user"
-SET preferences = jsonb_set(
-  preferences,
-  '{general, dashboard}',
-  (
-    CASE
-      WHEN NOT EXISTS (
-        SELECT 1
-        FROM jsonb_array_elements(COALESCE(preferences->'general'->'dashboard', '[]'::jsonb)) AS elem
-        WHERE elem->>'section' = 'ACTIVITY'
-      )
-      THEN jsonb_build_array(jsonb_build_object('hidden', false, 'section', 'ACTIVITY')) || COALESCE(preferences->'general'->'dashboard', '[]'::jsonb)
-      ELSE COALESCE(preferences->'general'->'dashboard', '[]'::jsonb)
-    END
-  )::jsonb,
-  true
-)
-WHERE preferences->'general'->'dashboard' IS NOT NULL;
-"#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "workout" ADD COLUMN IF NOT EXISTS "duration" integer NOT NULL GENERATED
-ALWAYS AS (EXTRACT(EPOCH FROM (end_time - start_time))) STORED;
-"#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "review"
-ADD COLUMN IF NOT EXISTS entity_lot text GENERATED ALWAYS AS (
-    CASE
-        WHEN "metadata_id" IS NOT NULL THEN 'metadata'
-        WHEN "person_id" IS NOT NULL THEN 'person'
-        WHEN "metadata_group_id" IS NOT NULL THEN 'metadata_group'
-        WHEN "collection_id" IS NOT NULL THEN 'collection'
-    END
-) STORED;
-        "#,
-        )
-        .await?;
-        db.execute_unprepared(r#"DROP TABLE IF EXISTS "user_summary""#)
-            .await?;
-        db.execute_unprepared(
-            r#"ALTER TABLE "user_to_entity" DROP COLUMN IF EXISTS "metadata_units_consumed""#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240828_add_last_login_on_column_to_user.rs b/crates/migrations/src/m20240828_add_last_login_on_column_to_user.rs
deleted file mode 100644
index c46da1cfff..0000000000
--- a/crates/migrations/src/m20240828_add_last_login_on_column_to_user.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "last_login_on" timestamp with time zone;
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240828_zz_add_columns_to_daily_user_activity.rs b/crates/migrations/src/m20240828_zz_add_columns_to_daily_user_activity.rs
deleted file mode 100644
index 12f6cdc68a..0000000000
--- a/crates/migrations/src/m20240828_zz_add_columns_to_daily_user_activity.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "anime_episode_count" INTEGER NOT NULL DEFAULT 0;
-ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "manga_chapter_count" INTEGER NOT NULL DEFAULT 0;
-ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "manga_volume_count" INTEGER NOT NULL DEFAULT 0;
-ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "show_season_count" INTEGER NOT NULL DEFAULT 0;
-ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "show_episode_count" INTEGER NOT NULL DEFAULT 0;
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240829_change_structure_for_exercise_extra_information.rs b/crates/migrations/src/m20240829_change_structure_for_exercise_extra_information.rs
deleted file mode 100644
index fd99539297..0000000000
--- a/crates/migrations/src/m20240829_change_structure_for_exercise_extra_information.rs
+++ /dev/null
@@ -1,105 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-DO $$
-DECLARE
-    rec RECORD;
-    new_history JSONB;
-    best_set JSONB;
-    history_element JSONB;
-BEGIN
-    -- Loop through each row in the user_to_entity table where exercise_extra_information is not null
-    FOR rec IN
-        SELECT id, exercise_extra_information
-        FROM "user_to_entity"
-        WHERE exercise_extra_information IS NOT NULL
-    LOOP
-        -- Initialize an empty array to store the new history with best_set
-        new_history := '[]'::jsonb;
-
-        -- Loop through each history element in exercise_extra_information
-        FOR history_element IN
-            SELECT * FROM jsonb_array_elements(rec.exercise_extra_information->'history')
-        LOOP
-            -- Build the new history element with the value
-            new_history := new_history || jsonb_set(
-                history_element,
-                '{workout_end_on}',
-                '"2024-08-26 00:43:30 +00:00"'
-            );
-        END LOOP;
-
-        -- Update the exercise_extra_information field with the new history format
-        UPDATE "user_to_entity"
-        SET exercise_extra_information = jsonb_set(
-            exercise_extra_information,
-            '{history}',
-            new_history
-        )
-        WHERE id = rec.id;
-    END LOOP;
-END $$;
-"#,
-        )
-        .await?;
-        if !manager.has_column("review", "exercise_id").await? {
-            db.execute_unprepared(
-            r#"
-ALTER TABLE "review" ADD COLUMN "exercise_id" TEXT;
-ALTER TABLE "review" ADD CONSTRAINT "review_to_exercise_foreign_key" FOREIGN KEY ("exercise_id") REFERENCES "exercise"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-            "#,
-        )
-        .await?;
-        }
-        if !manager.has_column("review", "entity_id").await? {
-            db.execute_unprepared(
-                r#"
-ALTER TABLE "review" ADD COLUMN "entity_id" TEXT NOT NULL GENERATED ALWAYS AS (
-    COALESCE(
-        "metadata_id",
-        "person_id",
-        "metadata_group_id",
-        "collection_id",
-        "exercise_id"
-    )
-) STORED;
-"#,
-            )
-            .await?;
-        }
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "review" DROP COLUMN "entity_lot";
-ALTER TABLE "review" ADD COLUMN "entity_lot" TEXT GENERATED ALWAYS AS (
-    CASE
-        WHEN "metadata_id" IS NOT NULL THEN 'metadata'
-        WHEN "person_id" IS NOT NULL THEN 'person'
-        WHEN "metadata_group_id" IS NOT NULL THEN 'metadata_group'
-        WHEN "collection_id" IS NOT NULL THEN 'collection'
-        WHEN "exercise_id" IS NOT NULL THEN 'exercise'
-    END
-) STORED;
-"#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "exercise_review_count" INTEGER NOT NULL DEFAULT 0;
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240831_add_is_account_default_column_to_access_link.rs b/crates/migrations/src/m20240831_add_is_account_default_column_to_access_link.rs
deleted file mode 100644
index 6b9cdd4e3d..0000000000
--- a/crates/migrations/src/m20240831_add_is_account_default_column_to_access_link.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "access_link" ADD COLUMN IF NOT EXISTS "is_account_default" boolean
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240831_add_templates_key_to_preferences.rs b/crates/migrations/src/m20240831_add_templates_key_to_preferences.rs
deleted file mode 100644
index d1ebf2ea10..0000000000
--- a/crates/migrations/src/m20240831_add_templates_key_to_preferences.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "user" SET "preferences" = jsonb_set("preferences", '{features_enabled,fitness,templates}', 'true');
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240903_add_changes_for_user_to_collection_removal.rs b/crates/migrations/src/m20240903_add_changes_for_user_to_collection_removal.rs
deleted file mode 100644
index 8b539013f9..0000000000
--- a/crates/migrations/src/m20240903_add_changes_for_user_to_collection_removal.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use super::m20231017_create_user_to_entity::CONSTRAINT_SQL;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager
-            .has_column("user_to_entity", "collection_id")
-            .await?
-        {
-            db.execute_unprepared(
-                r#"
-ALTER TABLE "user_to_entity" ADD COLUMN "collection_id" TEXT;
-ALTER TABLE "user_to_entity" ADD CONSTRAINT "user_to_entity-fk6" FOREIGN KEY ("collection_id") REFERENCES "collection" ("id") ON DELETE CASCADE ON UPDATE CASCADE;
-CREATE UNIQUE INDEX "user_to_entity-uqi5" ON "user_to_entity" ("user_id", "collection_id");
-"#,
-            )
-            .await?;
-            db.execute_unprepared(CONSTRAINT_SQL).await?;
-        }
-        if manager.has_table("user_to_collection").await? {
-            db.execute_unprepared(
-                r#"
-INSERT INTO "user_to_entity" ("user_id", "collection_id") SELECT "user_id", "collection_id" FROM "user_to_collection";
-DROP TABLE "user_to_collection";
-"#,
-            )
-            .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240918_add_default_rest_timer_to_workout_template.rs b/crates/migrations/src/m20240918_add_default_rest_timer_to_workout_template.rs
deleted file mode 100644
index da5c716148..0000000000
--- a/crates/migrations/src/m20240918_add_default_rest_timer_to_workout_template.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager
-            .has_column("workout_template", "default_rest_timer")
-            .await?
-        {
-            db.execute_unprepared(
-                r#"
-ALTER TABLE "workout_template" ADD COLUMN "default_rest_timer" INTEGER;
-"#,
-            )
-            .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240923_remove_extra_columns_for_daily_user_activities.rs b/crates/migrations/src/m20240923_remove_extra_columns_for_daily_user_activities.rs
deleted file mode 100644
index 81756fe045..0000000000
--- a/crates/migrations/src/m20240923_remove_extra_columns_for_daily_user_activities.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "daily_user_activity" DROP COLUMN IF EXISTS "anime_episode_count";
-ALTER TABLE "daily_user_activity" DROP COLUMN IF EXISTS "manga_chapter_count";
-ALTER TABLE "daily_user_activity" DROP COLUMN IF EXISTS "manga_volume_count";
-ALTER TABLE "daily_user_activity" DROP COLUMN IF EXISTS "show_season_count";
-ALTER TABLE "daily_user_activity" DROP COLUMN IF EXISTS "show_episode_count";
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240926_add_columns_for_open_sourcing_pro_version.rs b/crates/migrations/src/m20240926_add_columns_for_open_sourcing_pro_version.rs
deleted file mode 100644
index 12bb9f29db..0000000000
--- a/crates/migrations/src/m20240926_add_columns_for_open_sourcing_pro_version.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use super::{
-    m20231016_create_collection_to_entity::{CONSTRAINT_SQL, ENTITY_ID_SQL, ENTITY_LOT_SQL},
-    m20240904_create_monitored_entity::MONITORED_ENTITY_VIEW_CREATION_SQL,
-};
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-ALTER TABLE "metadata" ADD COLUMN IF NOT EXISTS "is_recommendation" BOOLEAN;
-ALTER TABLE "seen" ADD COLUMN IF NOT EXISTS "manual_time_spent" NUMERIC;
-ALTER TABLE "integration" ADD COLUMN IF NOT EXISTS "sync_to_owned_collection" BOOLEAN;
-
-UPDATE "user" SET "preferences" = jsonb_set("preferences", '{features_enabled,fitness,templates}', 'true');
-"#,
-        )
-        .await?;
-        if !manager.has_column("workout", "template_id").await? {
-            db.execute_unprepared(
-            r#"
-ALTER TABLE "workout" ADD COLUMN "template_id" TEXT;
-ALTER TABLE "workout" ADD CONSTRAINT "template_to_workout_foreign_key" FOREIGN KEY ("template_id") REFERENCES "workout_template"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-"#,
-        )
-        .await?;
-        }
-        if !manager
-            .has_column("collection_to_entity", "workout_template_id")
-            .await?
-        {
-            db.execute_unprepared(
-            r#"
-ALTER TABLE "collection_to_entity" ADD COLUMN "workout_template_id" TEXT;
-ALTER TABLE "collection_to_entity" ADD CONSTRAINT "collection_to_entity-fk7" FOREIGN KEY ("workout_template_id") REFERENCES "workout_template"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-CREATE UNIQUE INDEX "collection_to_entity_uqi6" ON "collection_to_entity" ("collection_id", "workout_template_id");
-"#,
-        )
-        .await?;
-        }
-        db.execute_unprepared(r#"DROP VIEW "monitored_entity""#)
-            .await?;
-        db.execute_unprepared(CONSTRAINT_SQL).await?;
-        db.execute_unprepared(&format!(
-            r#"
-        ALTER TABLE "collection_to_entity" DROP COLUMN "entity_lot";
-        ALTER TABLE "collection_to_entity" ADD COLUMN "entity_lot" TEXT {};
-        "#,
-            ENTITY_LOT_SQL
-        ))
-        .await?;
-        db.execute_unprepared(&format!(
-            r#"
-ALTER TABLE "collection_to_entity" DROP COLUMN "entity_id";
-ALTER TABLE "collection_to_entity" ADD COLUMN "entity_id" TEXT {};
-"#,
-            ENTITY_ID_SQL
-        ))
-        .await?;
-        db.execute_unprepared(MONITORED_ENTITY_VIEW_CREATION_SQL)
-            .await?;
-        if !manager
-            .has_column("daily_user_activity", "entity_ids")
-            .await?
-        {
-            db.execute_unprepared(
-                r#"
-DELETE FROM "daily_user_activity";
-ALTER TABLE "daily_user_activity" ADD COLUMN "entity_ids" TEXT[] NOT NULL DEFAULT '{}';
-        "#,
-            )
-            .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20240928_add_grid_packing_to_general_preferences.rs b/crates/migrations/src/m20240928_add_grid_packing_to_general_preferences.rs
deleted file mode 100644
index aeaa832bbe..0000000000
--- a/crates/migrations/src/m20240928_add_grid_packing_to_general_preferences.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "user" SET "preferences" = jsonb_set("preferences", '{general,grid_packing}', '"Normal"');
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241002_add_columns_for_associating_seen_with_reviews.rs b/crates/migrations/src/m20241002_add_columns_for_associating_seen_with_reviews.rs
deleted file mode 100644
index 45bdd8b6ae..0000000000
--- a/crates/migrations/src/m20241002_add_columns_for_associating_seen_with_reviews.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "user" SET "preferences" = jsonb_set("preferences", '{general,grid_packing}', '"Dense"');
-"#,
-        )
-        .await?;
-        if !manager.has_column("seen", "review_id").await? {
-            db.execute_unprepared(
-            r#"
-ALTER TABLE "seen" ADD COLUMN "review_id" TEXT;
-ALTER TABLE "seen" ADD CONSTRAINT "review_to_seen_foreign_key" FOREIGN KEY ("review_id") REFERENCES "review" ("id") ON DELETE SET NULL ON UPDATE CASCADE;
-"#,
-        )
-        .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241004_create_application_cache.rs b/crates/migrations/src/m20241004_create_application_cache.rs
index f6cf24f6f3..b704445c88 100644
--- a/crates/migrations/src/m20241004_create_application_cache.rs
+++ b/crates/migrations/src/m20241004_create_application_cache.rs
@@ -40,13 +40,17 @@ impl MigrationTrait for Migration {
                             .not_null()
                             .unique_key(),
                     )
-                    .col(ColumnDef::new(ApplicationCache::ExpiresAt).timestamp_with_time_zone())
+                    .col(
+                        ColumnDef::new(ApplicationCache::ExpiresAt)
+                            .timestamp_with_time_zone()
+                            .not_null(),
+                    )
                     .col(
                         ColumnDef::new(ApplicationCache::Value)
                             .json_binary()
                             .not_null(),
                     )
-                    .col(ColumnDef::new(ApplicationCache::Version).text().not_null())
+                    .col(ColumnDef::new(ApplicationCache::Version).text())
                     .to_owned(),
             )
             .await?;
diff --git a/crates/migrations/src/m20241006_changes_for_issue_1056.rs b/crates/migrations/src/m20241006_changes_for_issue_1056.rs
deleted file mode 100644
index c438d63b1c..0000000000
--- a/crates/migrations/src/m20241006_changes_for_issue_1056.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use enum_models::MediaLot;
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(&format!(
-            r#"
-UPDATE "user"
-SET preferences = jsonb_set(
-    preferences,
-    '{{general,watch_providers}}',
-    jsonb_build_array(
-        jsonb_build_object(
-            'lot', '{}',
-            'values', preferences -> 'general' -> 'watch_providers'
-        )
-    )
-);
-"#,
-            MediaLot::Movie
-        ))
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241010_changes_for_issue_708.rs b/crates/migrations/src/m20241010_changes_for_issue_708.rs
deleted file mode 100644
index 16305d058c..0000000000
--- a/crates/migrations/src/m20241010_changes_for_issue_708.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "person"
-SET "is_partial" = true
-WHERE EXISTS (
-    SELECT 1
-    FROM "metadata_to_person" mp
-    WHERE mp."person_id" = "person"."id"
-);
-DELETE FROM "metadata_to_person";
-"#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241013_changes_for_issue_1052.rs b/crates/migrations/src/m20241013_changes_for_issue_1052.rs
deleted file mode 100644
index acc15ce77b..0000000000
--- a/crates/migrations/src/m20241013_changes_for_issue_1052.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "user"
-SET "preferences" = jsonb_set(
-    "preferences",
-    '{fitness,exercises,set_rest_timers}',
-    jsonb_build_object('normal', 60)
-);
-DELETE FROM "daily_user_activity";
-ALTER TABLE "workout_template" DROP COLUMN IF EXISTS "default_rest_timer";
-        "#,
-        )
-        .await?;
-        for x in ["workout", "workout_template"] {
-            db.execute_unprepared(&format!(
-                r#"
-UPDATE "{x}"
-SET "information" = jsonb_set(
-    "information",
-    '{{exercises}}',
-    (
-    SELECT jsonb_agg(
-        jsonb_set(
-            exercise,
-            '{{identifier}}',
-            to_jsonb(gen_random_uuid())
-            )
-        )
-        FROM jsonb_array_elements("information"->'exercises') AS exercise
-    )
-)
-WHERE jsonb_array_length("information"->'exercises') > 0;
-
-UPDATE "{x}"
-SET "summary" = jsonb_set(
-    "summary",
-    '{{exercises}}',
-    (
-        SELECT jsonb_agg(
-            jsonb_set(
-                exercise - 'id',
-                '{{name}}',
-                exercise->'id'
-            )
-        )
-        FROM jsonb_array_elements("summary"->'exercises') AS exercise
-    )
-)
-WHERE jsonb_array_length("summary"->'exercises') > 0;
-                    "#,
-            ))
-            .await?;
-            db.execute_unprepared(&format!(
-            r#"
-DO $$
-DECLARE
-    workout_record RECORD;
-    workout_json jsonb;
-    updated_json jsonb;
-    superset_with jsonb;
-    exercise jsonb;
-    exercise_index int;
-    superset_array jsonb := '[]'::jsonb;
-    superset jsonb;
-    color_options text[] := ARRAY['red', 'pink', 'yellow', 'gray', 'teal', 'green'];
-BEGIN
-    FOR workout_record IN SELECT id, information FROM "{x}" LOOP
-        workout_json := workout_record.information;
-
-        FOR exercise_index IN 0..jsonb_array_length(workout_json->'exercises')-1 LOOP
-            exercise := workout_json->'exercises'->exercise_index;
-            superset_with := exercise->'superset_with';
-
-            IF jsonb_array_length(superset_with) > 0 THEN
-                superset := jsonb_build_array(exercise_index) || superset_with;
-                superset := (SELECT jsonb_agg(elem ORDER BY elem) FROM jsonb_array_elements(superset) AS elem);
-
-                superset_array := jsonb_insert(superset_array, '{{-1}}', superset);
-            END IF;
-        END LOOP;
-
-        IF jsonb_array_length(superset_array) > 0 THEN
-            superset_array := (SELECT jsonb_agg(DISTINCT elem) FROM jsonb_array_elements(superset_array) AS elem);
-	        superset_array := (
-	            SELECT jsonb_agg(
-	                jsonb_build_object('color', color_options[(row_num - 1) % array_length(color_options, 1) + 1], 'exercises', elem)
-	            )
-	            FROM (
-	                SELECT elem, row_number() OVER () as row_num
-	                FROM jsonb_array_elements(superset_array) AS elem
-	            ) subquery
-	        );
-
-		END IF;
-
-        updated_json := jsonb_set(workout_json, '{{supersets}}', superset_array);
-        UPDATE "{x}" SET information = updated_json WHERE id = workout_record.id;
-
-        superset_array := '[]'::jsonb;
-    END LOOP;
-END $$;
-    "#
-        ))
-        .await?;
-        }
-        db.execute_unprepared(
-            r#"
-UPDATE "user_to_entity"
-SET "exercise_extra_information" = jsonb_set(
-    "exercise_extra_information",
-    '{settings}',
-    jsonb_build_object('set_rest_timers', jsonb_build_object('normal', 60))
-)
-WHERE "user_to_entity"."exercise_extra_information" IS NOT NULL;
-        "#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-DO $$
-DECLARE
-    workout_row RECORD;
-BEGIN
-    FOR workout_row IN SELECT id, information FROM workout
-    LOOP
-        UPDATE workout
-        SET information = jsonb_set(
-            workout_row.information,
-            '{exercises}',
-            (
-                SELECT jsonb_agg(
-                    CASE
-                        WHEN (exercise->>'rest_time')::int > 0 THEN
-                            jsonb_set(exercise, '{sets}', (
-                                SELECT jsonb_agg(jsonb_set(set_row, '{rest_time}', exercise->'rest_time'))
-                                FROM jsonb_array_elements(exercise->'sets') AS set_row
-                            ))
-                        ELSE
-                            exercise
-                    END
-                )
-                FROM jsonb_array_elements(workout_row.information->'exercises') AS exercise
-            )
-        )
-        WHERE id = workout_row.id;
-    END LOOP;
-END $$;
-        "#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241019_changes_for_issue_929.rs b/crates/migrations/src/m20241019_changes_for_issue_929.rs
deleted file mode 100644
index e702e1c863..0000000000
--- a/crates/migrations/src/m20241019_changes_for_issue_929.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use super::m20240904_create_monitored_entity::MONITORED_ENTITY_VIEW_CREATION_SQL;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(r#"DROP VIEW "monitored_entity""#)
-            .await?;
-        db.execute_unprepared(MONITORED_ENTITY_VIEW_CREATION_SQL)
-            .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241019_changes_for_issue_964.rs b/crates/migrations/src/m20241019_changes_for_issue_964.rs
deleted file mode 100644
index a1da3a8448..0000000000
--- a/crates/migrations/src/m20241019_changes_for_issue_964.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "integration" SET "provider" = 'jellyfin_sink' WHERE "provider" = 'jellyfin';
-ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "video_game_duration" INTEGER NOT NULL DEFAULT 0;
-        "#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241025_changes_for_issue_1084.rs b/crates/migrations/src/m20241025_changes_for_issue_1084.rs
deleted file mode 100644
index 2dfe21a1fc..0000000000
--- a/crates/migrations/src/m20241025_changes_for_issue_1084.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "user"
-SET "preferences" = jsonb_set(
-    "preferences",
-    '{fitness,logging}',
-    '{"show_details_while_editing": true}'::jsonb
-);
-        "#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241110_changes_for_issue_1103.rs b/crates/migrations/src/m20241110_changes_for_issue_1103.rs
deleted file mode 100644
index 131f47794b..0000000000
--- a/crates/migrations/src/m20241110_changes_for_issue_1103.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        for x in ["workout", "workout_template"] {
-            db.execute_unprepared(&format!(
-                r#"
-UPDATE "{x}"
-SET "summary" = jsonb_set("summary", '{{focused}}', '{{"muscles":[], "forces":[], "lots":[], "levels":[], "equipments":[]}}'::jsonb);
-                    "#,
-            ))
-            .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241121_changes_for_issue_445.rs b/crates/migrations/src/m20241121_changes_for_issue_445.rs
deleted file mode 100644
index 0ad0be4f0c..0000000000
--- a/crates/migrations/src/m20241121_changes_for_issue_445.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager
-            .has_column("daily_user_activity", "hour_records")
-            .await?
-        {
-            db.execute_unprepared(
-                r#"
-DELETE FROM "daily_user_activity";
-ALTER TABLE "daily_user_activity" ADD COLUMN "hour_records" JSONB NOT NULL DEFAULT '[]';
-        "#,
-            )
-            .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241124_changes_for_issue_1118.rs b/crates/migrations/src/m20241124_changes_for_issue_1118.rs
deleted file mode 100644
index 6186893e0b..0000000000
--- a/crates/migrations/src/m20241124_changes_for_issue_1118.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-                r#"
-UPDATE "user" SET "preferences" = jsonb_set("preferences", '{fitness,logging,mute_sounds}', 'false');
-                    "#,
-            )
-            .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241126_changes_for_issue_1113.rs b/crates/migrations/src/m20241126_changes_for_issue_1113.rs
deleted file mode 100644
index 7c51c51814..0000000000
--- a/crates/migrations/src/m20241126_changes_for_issue_1113.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use super::{
-    m20230822_create_exercise::EXERCISE_NAME_INDEX,
-    m20240827_create_daily_user_activity::create_daily_user_activity_table,
-};
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager.has_column("daily_user_activity", "id").await? {
-            db.execute_unprepared("DROP TABLE daily_user_activity")
-                .await?;
-            create_daily_user_activity_table(manager).await?;
-        }
-        if !manager.has_column("exercise", "name").await? {
-            db.execute_unprepared(&format!(
-                r#"
-UPDATE exercise SET identifier = id;
-ALTER TABLE exercise RENAME COLUMN identifier TO name;
-DROP INDEX "exercise__identifier__index";
-CREATE INDEX "{}" ON "exercise" ("name");
-              "#,
-                EXERCISE_NAME_INDEX
-            ))
-            .await?;
-            for table in ["workout", "workout_template"] {
-                db.execute_unprepared(&format!(
-                    r#"
-UPDATE "{table}"
-SET information =
-    JSONB_SET(
-        information,
-        '{{exercises}}',
-        (
-            SELECT JSONB_AGG(
-                JSONB_SET(
-                    exercise,
-                    '{{id}}',
-                    exercise->'name',
-                    true
-                ) - 'name'
-            )
-            FROM JSONB_ARRAY_ELEMENTS(information->'exercises') AS exercise
-        )
-    ),
-    summary =
-    JSONB_SET(
-        summary,
-        '{{exercises}}',
-        (
-            SELECT JSONB_AGG(
-                JSONB_SET(
-                    exercise,
-                    '{{id}}',
-                    exercise->'name',
-                    true
-                ) - 'name'
-            )
-            FROM JSONB_ARRAY_ELEMENTS(summary->'exercises') AS exercise
-        )
-    );
-            "#,
-                ))
-                .await?;
-            }
-        }
-        db.execute_unprepared(
-            r#"
-DO $$
-BEGIN
-    IF EXISTS (
-        SELECT 1
-        FROM information_schema.columns
-        WHERE table_name = 'exercise'
-          AND column_name = 'muscles'
-          AND data_type = 'jsonb'
-    ) THEN
-        ALTER TABLE exercise ADD COLUMN muscles_text_array text[];
-        UPDATE exercise
-        SET muscles_text_array = ARRAY(SELECT jsonb_array_elements_text(muscles));
-        ALTER TABLE exercise DROP COLUMN muscles;
-        ALTER TABLE exercise RENAME COLUMN muscles_text_array TO muscles;
-    END IF;
-END $$;
-        "#,
-        )
-        .await?;
-        if !manager.has_column("application_cache", "value").await? {
-            db.execute_unprepared(
-                r#"
-ALTER TABLE "application_cache" ADD COLUMN "value" JSONB;
-UPDATE "application_cache" SET "value" = '"Empty"'::jsonb;
-ALTER TABLE "application_cache" ALTER COLUMN "value" SET NOT NULL;
-"#,
-            )
-            .await?;
-        }
-        db.execute_unprepared(
-            r#"
-UPDATE "user" SET "extra_information" = JSONB_BUILD_OBJECT('scheduled_for_workout_revision', true);
-        "#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-UPDATE "user" SET "preferences" = jsonb_set(
-    "preferences", '{features_enabled,analytics}',
-    JSONB_BUILD_OBJECT('enabled', true)
-);
-        "#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-UPDATE "integration" SET "provider" = 'plex_sink' WHERE "provider" = 'plex';
-UPDATE "integration" SET "provider_specifics" = jsonb_set(
-    "provider_specifics",
-    '{plex_sink_username}',
-    "provider_specifics"->'plex_username'
-) - 'plex_username' WHERE "provider_specifics" -> 'plex_username' IS NOT NULL;
-            "#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-UPDATE "user_to_entity" SET "exercise_extra_information" = jsonb_set("exercise_extra_information", '{settings,exclude_from_analytics}', 'false')
-WHERE "exercise_extra_information" IS NOT NULL;
-            "#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-UPDATE "user"
-SET preferences =
-    jsonb_set(
-        preferences,
-        '{general, dashboard}',
-        (
-            SELECT jsonb_agg(element)
-            FROM jsonb_array_elements(preferences->'general'->'dashboard') AS element
-            WHERE element->>'section' != 'ACTIVITY'
-        )
-    );
-            "#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241129_changes_for_issue_1114.rs b/crates/migrations/src/m20241129_changes_for_issue_1114.rs
deleted file mode 100644
index d0addaf7a9..0000000000
--- a/crates/migrations/src/m20241129_changes_for_issue_1114.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager.has_column("import_report", "progress").await? {
-            db.execute_unprepared(r#"ALTER TABLE "import_report" ADD COLUMN "progress" DECIMAL"#)
-                .await?;
-        }
-        if !manager.has_column("import_report", "source_result").await? {
-            db.execute_unprepared(
-                r#"ALTER TABLE "import_report" ADD COLUMN "source_result" JSONB"#,
-            )
-            .await?;
-        }
-        db.execute_unprepared(
-            r#"
-UPDATE "import_report" SET "source" = 'myanimelist' WHERE "source" = 'mal';
-UPDATE "import_report" SET "source" = 'mediatracker' WHERE "source" = 'media_tracker';
-UPDATE "import_report" SET "source" = 'storygraph' WHERE "source" = 'story_graph';
-            "#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241214_create_user_notification.rs b/crates/migrations/src/m20241214_create_user_notification.rs
index d63f84d9e7..6629f3ec8c 100644
--- a/crates/migrations/src/m20241214_create_user_notification.rs
+++ b/crates/migrations/src/m20241214_create_user_notification.rs
@@ -1,6 +1,6 @@
 use sea_orm_migration::prelude::*;
 
-use super::m20230417_create_user::User;
+use super::m20230404_create_user::User;
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
@@ -12,6 +12,7 @@ pub enum UserNotification {
     Table,
     UserId,
     Message,
+    CreatedOn,
     IsAddressed,
 }
 
@@ -24,15 +25,20 @@ impl MigrationTrait for Migration {
                     .table(UserNotification::Table)
                     .col(
                         ColumnDef::new(UserNotification::Id)
-                            .uuid()
+                            .text()
                             .not_null()
-                            .default(PgFunc::gen_random_uuid())
                             .primary_key(),
                     )
                     .col(ColumnDef::new(UserNotification::Lot).text().not_null())
                     .col(ColumnDef::new(UserNotification::Message).text().not_null())
                     .col(ColumnDef::new(UserNotification::UserId).text().not_null())
                     .col(ColumnDef::new(UserNotification::IsAddressed).boolean())
+                    .col(
+                        ColumnDef::new(UserNotification::CreatedOn)
+                            .timestamp_with_time_zone()
+                            .not_null()
+                            .default(Expr::current_timestamp()),
+                    )
                     .foreign_key(
                         ForeignKey::create()
                             .name("notification_to_user_foreign_key")
diff --git a/crates/migrations/src/m20241215_changes_for_issue_1131.rs b/crates/migrations/src/m20241215_changes_for_issue_1131.rs
deleted file mode 100644
index cb06552ba4..0000000000
--- a/crates/migrations/src/m20241215_changes_for_issue_1131.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if manager.has_table("queued_notification").await? {
-            db.execute_unprepared(r#"DROP TABLE "queued_notification""#)
-                .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241220_changes_for_issue_1141.rs b/crates/migrations/src/m20241220_changes_for_issue_1141.rs
deleted file mode 100644
index 2da9abf3fe..0000000000
--- a/crates/migrations/src/m20241220_changes_for_issue_1141.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use crate::m20240827_create_daily_user_activity::DAILY_USER_ACTIVITY_COMPOSITE_UNIQUE_KEY;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(&format!(
-            r#"
-UPDATE "user"
-SET "preferences" = jsonb_set(
-    "preferences",
-    '{{fitness,logging,prompt_for_rest_timer}}',
-    'false'
-);
-
---
-
-ALTER TABLE "workout" DROP COLUMN "duration";
-ALTER TABLE "workout" ADD COLUMN "duration" INTEGER;
-UPDATE "workout" SET "duration" = extract(epoch FROM "end_time" - "start_time");
-ALTER TABLE "workout" ALTER COLUMN "duration" SET NOT NULL;
-
---
-
-DELETE FROM "daily_user_activity";
-DROP INDEX "{dua_idx}";
-CREATE UNIQUE INDEX "{dua_idx}" ON "daily_user_activity"("user_id", "date") NULLS NOT DISTINCT;
---
-
-DELETE FROM "application_cache";
-ALTER TABLE "application_cache" ADD COLUMN IF NOT EXISTS "version" TEXT NOT NULL;
-        "#,
-            dua_idx = DAILY_USER_ACTIVITY_COMPOSITE_UNIQUE_KEY
-        ))
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241220_changes_for_issue_49.rs b/crates/migrations/src/m20241220_changes_for_issue_49.rs
deleted file mode 100644
index cdc74bcd39..0000000000
--- a/crates/migrations/src/m20241220_changes_for_issue_49.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use crate::m20240827_create_daily_user_activity::create_daily_user_activity_table;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager.has_column("metadata", "music_specifics").await? {
-            db.execute_unprepared(r#"ALTER TABLE "metadata" ADD COLUMN "music_specifics" JSONB"#)
-                .await?;
-        }
-        if manager.has_column("metadata", "state_changes").await? {
-            db.execute_unprepared(r#"ALTER TABLE "metadata" DROP COLUMN "state_changes""#)
-                .await?;
-        }
-        let tables = vec!["metadata", "metadata_group", "person"];
-        for table in tables {
-            if !manager.has_column(table, "source_url").await? {
-                db.execute_unprepared(&format!(
-                    r#"ALTER TABLE "{}" ADD COLUMN "source_url" TEXT"#,
-                    table
-                ))
-                .await?;
-            }
-        }
-        if !manager
-            .has_column("daily_user_activity", "music_count")
-            .await?
-        {
-            db.execute_unprepared(r#"DROP TABLE "daily_user_activity""#)
-                .await?;
-            create_daily_user_activity_table(manager).await?;
-        }
-        db.execute_unprepared(r#"
-UPDATE "user" SET preferences = jsonb_set(preferences, '{features_enabled,media,music}', 'true', true);
-ALTER TABLE "metadata_group" ALTER COLUMN "images" DROP NOT NULL;
-"#)
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241224_changes_for_issue_1139.rs b/crates/migrations/src/m20241224_changes_for_issue_1139.rs
deleted file mode 100644
index b2c6d185a7..0000000000
--- a/crates/migrations/src/m20241224_changes_for_issue_1139.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use crate::m20230410_create_metadata::METADATA_TO_USER_FOREIGN_KEY;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager.has_column("metadata", "created_by_user_id").await? {
-            db.execute_unprepared(&format!(
-            r#"
-ALTER TABLE "metadata" ADD COLUMN "created_by_user_id" TEXT;
-ALTER TABLE "metadata" ADD CONSTRAINT "{fk}" FOREIGN KEY ("created_by_user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-        "#,
-        fk = METADATA_TO_USER_FOREIGN_KEY
-        ))
-        .await?;
-            db.execute_unprepared(r#"
-UPDATE "metadata" SET "created_by_user_id" = (SELECT "id" FROM "user" WHERE lot = 'admin') WHERE source = 'custom'"#).await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241227_changes_for_issue_1157.rs b/crates/migrations/src/m20241227_changes_for_issue_1157.rs
deleted file mode 100644
index 6ade759355..0000000000
--- a/crates/migrations/src/m20241227_changes_for_issue_1157.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use crate::m20230413_create_person::create_metadata_group_to_person_table;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager.has_table("metadata_group_to_person").await? {
-            create_metadata_group_to_person_table(manager).await?;
-        }
-        db.execute_unprepared(
-            r#"
-UPDATE
-  "user"
-SET
-  preferences = JSONB_SET(
-    preferences,
-    '{notifications,to_send}',
-    (
-      (preferences -> 'notifications' -> 'to_send')
-        || '"PersonMetadataAssociated"' || '"PersonMetadataGroupAssociated"'
-    ) - 'PersonMediaAssociated'
-  )
-WHERE
-  preferences -> 'notifications' -> 'to_send' ? 'PersonMediaAssociated'
-            "#,
-        )
-        .await?;
-        db.execute_unprepared(
-            r#"
-UPDATE
-  "person"
-SET
-  "state_changes" = JSONB_SET(
-    JSONB_SET("state_changes", '{metadata_associated}', "state_changes"->'media_associated') - 'media_associated',
-    '{metadata_groups_associated}',
-    '[]'
-  )
-WHERE
-  "state_changes"->'media_associated' is not null;
-"#,
-        )
-        .await?;
-        db.execute_unprepared(r#"ALTER TABLE "metadata_group" ALTER COLUMN "parts" SET DEFAULT 0"#)
-            .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241228_changes_for_issue_1130.rs b/crates/migrations/src/m20241228_changes_for_issue_1130.rs
deleted file mode 100644
index 67bb7be6e8..0000000000
--- a/crates/migrations/src/m20241228_changes_for_issue_1130.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager
-            .has_column("user_notification", "is_addressed")
-            .await?
-        {
-            db.execute_unprepared(
-                r#"ALTER TABLE "user_notification" ADD COLUMN "is_addressed" boolean"#,
-            )
-            .await?;
-        }
-        db.execute_unprepared(
-            r#"
-UPDATE
-  "user"
-SET
-  preferences = JSONB_SET(
-    preferences,
-    '{notifications,to_send}',
-    (preferences -> 'notifications' -> 'to_send') || '"NewWorkoutCreated"'
-  )
-where
-  NOT (
-    preferences -> 'notifications' -> 'to_send' ? 'NewWorkoutCreated'
-  );
-            "#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20241229_changes_for_issue_1155.rs b/crates/migrations/src/m20241229_changes_for_issue_1155.rs
deleted file mode 100644
index 551320ea49..0000000000
--- a/crates/migrations/src/m20241229_changes_for_issue_1155.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if manager.has_column("workout_template", "visibility").await? {
-            db.execute_unprepared(r#"ALTER TABLE "workout_template" DROP COLUMN "visibility""#)
-                .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20250112_changes_for_issue_1174.rs b/crates/migrations/src/m20250112_changes_for_issue_1174.rs
deleted file mode 100644
index 7aa8081189..0000000000
--- a/crates/migrations/src/m20250112_changes_for_issue_1174.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager
-            .has_column("import_report", "estimated_finish_time")
-            .await?
-        {
-            db.execute_unprepared(
-                r#"
-ALTER TABLE "import_report" ADD COLUMN "estimated_finish_time" timestamp with time zone;
-UPDATE "import_report" SET "estimated_finish_time" = ("started_on" + interval '1 hour');
-ALTER TABLE "import_report" ALTER COLUMN "estimated_finish_time" SET NOT NULL;
-                "#,
-            )
-            .await?;
-        }
-        for enum_name in ["ReviewConversion", "SeenHistoryConversion"] {
-            db.execute_unprepared(&format!(
-                r#"
-UPDATE import_report
-SET details = jsonb_set(
-    details,
-    '{{failed_items}}',
-    (
-        SELECT jsonb_agg(
-            CASE
-                WHEN (failed_item ->> 'step') = '{enum_name}' THEN
-                    jsonb_set(failed_item, '{{step}}', '"DatabaseCommit"')
-                ELSE
-                    failed_item
-            END
-        )
-        FROM jsonb_array_elements(details -> 'failed_items') AS failed_item
-    )
-)
-WHERE details -> 'failed_items' @> '[{{"step": "{enum_name}"}}]';
-        "#,
-            ))
-            .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20250113_changes_for_issue_1169.rs b/crates/migrations/src/m20250113_changes_for_issue_1169.rs
deleted file mode 100644
index 2daa30e3e9..0000000000
--- a/crates/migrations/src/m20250113_changes_for_issue_1169.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        db.execute_unprepared(
-            r#"
-UPDATE "user"
-SET "preferences" = jsonb_set(
-    "preferences",
-    '{features_enabled,media,specific}',
-    '[
-        "book",
-        "show",
-        "movie",
-        "anime",
-        "manga",
-        "music",
-        "podcast",
-        "audio_book",
-        "video_game",
-        "visual_novel"
-    ]'::jsonb
-);
-            "#,
-        )
-        .await?;
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20250115_changes_for_issue_1110.rs b/crates/migrations/src/m20250115_changes_for_issue_1110.rs
deleted file mode 100644
index 25d8770e76..0000000000
--- a/crates/migrations/src/m20250115_changes_for_issue_1110.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager.has_column("person", "alternate_names").await? {
-            db.execute_unprepared(r#"ALTER TABLE "person" ADD COLUMN "alternate_names" text[]"#)
-                .await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20250116_changes_for_issue_1161.rs b/crates/migrations/src/m20250116_changes_for_issue_1161.rs
deleted file mode 100644
index 1b67a743c4..0000000000
--- a/crates/migrations/src/m20250116_changes_for_issue_1161.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-use crate::m20240827_create_daily_user_activity::create_daily_user_activity_table;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        let db = manager.get_connection();
-        if !manager.has_column("workout", "calories_burnt").await? {
-            db.execute_unprepared(r#"ALTER TABLE "workout" ADD COLUMN "calories_burnt" decimal"#)
-                .await?;
-        }
-        if !manager
-            .has_column("daily_user_activity", "workout_calories_burnt")
-            .await?
-        {
-            db.execute_unprepared("DROP TABLE daily_user_activity")
-                .await?;
-            create_daily_user_activity_table(manager).await?;
-        }
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20250117_is_last_v7_migration.rs b/crates/migrations/src/m20250117_is_last_v7_migration.rs
deleted file mode 100644
index 02dab03ac6..0000000000
--- a/crates/migrations/src/m20250117_is_last_v7_migration.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use sea_orm_migration::prelude::*;
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
-    async fn up(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-
-    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
-        Ok(())
-    }
-}
diff --git a/crates/migrations/src/m20250118_is_v8_migration.rs b/crates/migrations/src/m20250118_is_v8_migration.rs
new file mode 100644
index 0000000000..1828b7faac
--- /dev/null
+++ b/crates/migrations/src/m20250118_is_v8_migration.rs
@@ -0,0 +1,44 @@
+use sea_orm_migration::prelude::*;
+
+#[derive(DeriveMigrationName)]
+pub struct Migration;
+
+#[async_trait::async_trait]
+impl MigrationTrait for Migration {
+    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
+        let db = manager.get_connection();
+        db.execute_unprepared(
+            r#"
+ALTER TABLE "application_cache" ALTER COLUMN "version" DROP NOT NULL;
+ALTER TABLE "application_cache" ALTER COLUMN "expires_at" SET NOT NULL;
+
+ALTER TABLE "user_notification" ALTER COLUMN "id" DROP DEFAULT;
+ALTER TABLE "user_notification" ALTER COLUMN "id" TYPE text;
+ALTER TABLE "user_notification" ADD COLUMN IF NOT EXISTS "created_on" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP;
+
+ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "last_activity_on" TIMESTAMP WITH TIME ZONE;
+
+UPDATE "user_notification" SET "lot" = 'queued' WHERE "lot" = 'immediate';
+
+UPDATE
+  "user"
+SET
+  preferences = JSONB_SET(
+    preferences,
+    '{notifications,to_send}',
+    (preferences -> 'notifications' -> 'to_send') || '"OutdatedSeenEntries"'
+  )
+where
+  NOT (
+    preferences -> 'notifications' -> 'to_send' ? 'OutdatedSeenEntries'
+  );
+        "#,
+        )
+        .await?;
+        Ok(())
+    }
+
+    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
+        Ok(())
+    }
+}
diff --git a/crates/models/background/src/lib.rs b/crates/models/background/src/lib.rs
index efa0329849..0f821b05dc 100644
--- a/crates/models/background/src/lib.rs
+++ b/crates/models/background/src/lib.rs
@@ -1,4 +1,4 @@
-use chrono::DateTime;
+use chrono::{DateTime, Utc};
 use chrono_tz::Tz;
 use database_models::seen;
 use media_models::{DeployImportJobInput, ProgressUpdateInput, ReviewPostedEvent};
@@ -35,6 +35,7 @@ pub enum LpApplicationJob {
     HandleOnSeenComplete(String),
     HandleEntityAddedToCollectionEvent(Uuid),
     HandleAfterMediaSeenTasks(Box<seen::Model>),
+    UpdateUserLastActivityPerformed(String, DateTime<Utc>),
 }
 
 #[derive(Debug, Deserialize, Serialize, Display, Clone)]
diff --git a/crates/models/common/src/lib.rs b/crates/models/common/src/lib.rs
index 59d679cf58..2f1b18434b 100644
--- a/crates/models/common/src/lib.rs
+++ b/crates/models/common/src/lib.rs
@@ -191,6 +191,7 @@ pub enum UserNotificationContent {
     ReviewPosted,
     MetadataPublished,
     NewWorkoutCreated,
+    OutdatedSeenEntries,
     MetadataStatusChanged,
     MetadataEpisodeReleased,
     PersonMetadataAssociated,
diff --git a/crates/models/database/src/application_cache.rs b/crates/models/database/src/application_cache.rs
index f186210d4d..9df657e762 100644
--- a/crates/models/database/src/application_cache.rs
+++ b/crates/models/database/src/application_cache.rs
@@ -8,13 +8,13 @@ use sea_orm::entity::prelude::*;
 pub struct Model {
     #[sea_orm(primary_key)]
     pub id: Uuid,
-    pub version: String,
+    pub version: Option<String>,
     pub created_at: DateTimeUtc,
+    pub expires_at: DateTimeUtc,
     #[sea_orm(column_type = "Json")]
     pub key: ApplicationCacheKey,
     #[sea_orm(column_type = "Json")]
     pub value: serde_json::Value,
-    pub expires_at: Option<DateTimeUtc>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
diff --git a/crates/models/database/src/user.rs b/crates/models/database/src/user.rs
index b527019233..f30e2bbfbf 100644
--- a/crates/models/database/src/user.rs
+++ b/crates/models/database/src/user.rs
@@ -28,6 +28,8 @@ pub struct Model {
     pub is_disabled: Option<bool>,
     pub preferences: UserPreferences,
     #[graphql(skip)]
+    pub last_activity_on: Option<DateTimeUtc>,
+    #[graphql(skip)]
     pub extra_information: Option<UserExtraInformation>,
 }
 
diff --git a/crates/models/database/src/user_notification.rs b/crates/models/database/src/user_notification.rs
index d61698f3ec..2da3d04374 100644
--- a/crates/models/database/src/user_notification.rs
+++ b/crates/models/database/src/user_notification.rs
@@ -1,17 +1,24 @@
 //! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.1
 
+use async_graphql::SimpleObject;
+use async_trait::async_trait;
 use enum_models::UserNotificationLot;
-use sea_orm::entity::prelude::*;
-use uuid::Uuid;
+use nanoid::nanoid;
+use sea_orm::{entity::prelude::*, ActiveValue};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, SimpleObject)]
 #[sea_orm(table_name = "user_notification")]
+#[graphql(name = "UserNotification")]
 pub struct Model {
     #[sea_orm(primary_key)]
-    pub id: Uuid,
+    pub id: String,
     pub message: String,
+    #[graphql(skip)]
     pub user_id: String,
+    pub created_on: DateTimeUtc,
+    #[graphql(skip)]
     pub lot: UserNotificationLot,
+    #[graphql(skip)]
     pub is_addressed: Option<bool>,
 }
 
@@ -33,4 +40,15 @@ impl Related<super::user::Entity> for Entity {
     }
 }
 
-impl ActiveModelBehavior for ActiveModel {}
+#[async_trait]
+impl ActiveModelBehavior for ActiveModel {
+    async fn before_save<C>(mut self, _db: &C, insert: bool) -> Result<Self, DbErr>
+    where
+        C: ConnectionTrait,
+    {
+        if insert {
+            self.id = ActiveValue::Set(format!("untf_{}", nanoid!(12)));
+        }
+        Ok(self)
+    }
+}
diff --git a/crates/models/dependent/src/lib.rs b/crates/models/dependent/src/lib.rs
index 13abbac99a..b095383886 100644
--- a/crates/models/dependent/src/lib.rs
+++ b/crates/models/dependent/src/lib.rs
@@ -440,7 +440,6 @@ pub struct TmdbSettings {
     pub languages: Vec<TmdbLanguage>,
 }
 
-#[skip_serializing_none]
 #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize, Clone)]
 pub struct EmptyCacheValue {
     pub _empty: (),
diff --git a/crates/models/enum/src/lib.rs b/crates/models/enum/src/lib.rs
index dbe042ac1b..aaa33afc7d 100644
--- a/crates/models/enum/src/lib.rs
+++ b/crates/models/enum/src/lib.rs
@@ -104,7 +104,7 @@ pub enum UserLot {
 )]
 pub enum UserNotificationLot {
     Queued,
-    Immediate,
+    Display,
 }
 
 #[derive(
diff --git a/crates/models/media/src/lib.rs b/crates/models/media/src/lib.rs
index 06c0ab2ebf..4c4589fbc5 100644
--- a/crates/models/media/src/lib.rs
+++ b/crates/models/media/src/lib.rs
@@ -4,8 +4,8 @@ use async_graphql::{Enum, InputObject, InputType, OneofObject, SimpleObject, Uni
 use boilermates::boilermates;
 use chrono::{NaiveDate, NaiveDateTime};
 use common_models::{
-    CollectionExtraInformation, IdAndNamedObject, PersonSourceSpecifics, SearchInput, StoredUrl,
-    StringIdObject,
+    ApplicationDateRange, CollectionExtraInformation, IdAndNamedObject, PersonSourceSpecifics,
+    SearchInput, StoredUrl, StringIdObject,
 };
 use common_utils::deserialize_date;
 use enum_models::{
@@ -1302,6 +1302,7 @@ pub enum MediaGeneralFilter {
 pub struct MediaFilter {
     pub collections: Option<Vec<String>>,
     pub general: Option<MediaGeneralFilter>,
+    pub date_range: Option<ApplicationDateRange>,
 }
 
 #[derive(Debug, Serialize, Deserialize, InputObject, Clone, Default)]
diff --git a/crates/resolvers/user/src/lib.rs b/crates/resolvers/user/src/lib.rs
index 752a7b4018..445d95736f 100644
--- a/crates/resolvers/user/src/lib.rs
+++ b/crates/resolvers/user/src/lib.rs
@@ -2,7 +2,7 @@ use std::sync::Arc;
 
 use async_graphql::{Context, Object, Result};
 use common_models::StringIdObject;
-use database_models::{access_link, integration, notification_platform, user};
+use database_models::{access_link, integration, notification_platform, user, user_notification};
 use dependent_models::{UserDetailsResult, UserMetadataRecommendationsResponse};
 use media_models::{
     AuthUserInput, CreateAccessLinkInput, CreateUserIntegrationInput,
@@ -25,10 +25,13 @@ impl UserQuery {
     async fn user_metadata_recommendations(
         &self,
         gql_ctx: &Context<'_>,
+        should_refresh: Option<bool>,
     ) -> Result<UserMetadataRecommendationsResponse> {
         let service = gql_ctx.data_unchecked::<Arc<UserService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
-        service.user_metadata_recommendations(&user_id).await
+        service
+            .user_metadata_recommendations(&user_id, should_refresh)
+            .await
     }
 
     /// Get all access links generated by the currently logged in user.
@@ -62,6 +65,16 @@ impl UserQuery {
         service.user_integrations(&user_id).await
     }
 
+    /// Get all pending display notifications for the currently logged in user.
+    async fn user_pending_notifications(
+        &self,
+        gql_ctx: &Context<'_>,
+    ) -> Result<Vec<user_notification::Model>> {
+        let service = gql_ctx.data_unchecked::<Arc<UserService>>();
+        let user_id = self.user_id_from_ctx(gql_ctx).await?;
+        service.user_pending_notifications(&user_id).await
+    }
+
     /// Get all the notification platforms for the currently logged in user.
     async fn user_notification_platforms(
         &self,
@@ -272,12 +285,16 @@ impl UserMutation {
         service.generate_auth_token(user_id).await
     }
 
-    /// Refresh the user metadata recommendations.
-    async fn refresh_user_metadata_recommendations(&self, gql_ctx: &Context<'_>) -> Result<bool> {
+    /// Mark user notifications as addressed.
+    async fn mark_notifications_as_addressed(
+        &self,
+        gql_ctx: &Context<'_>,
+        notification_ids: Vec<String>,
+    ) -> Result<bool> {
         let service = gql_ctx.data_unchecked::<Arc<UserService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service
-            .refresh_user_metadata_recommendations(&user_id)
+            .mark_notifications_as_addressed(user_id, notification_ids)
             .await
     }
 }
diff --git a/crates/services/cache/Cargo.toml b/crates/services/cache/Cargo.toml
index 9293690cfb..29ef320797 100644
--- a/crates/services/cache/Cargo.toml
+++ b/crates/services/cache/Cargo.toml
@@ -11,7 +11,6 @@ common-utils = { path = "../../utils/common" }
 config = { path = "../../config" }
 database-models = { path = "../../models/database" }
 dependent-models = { path = "../../models/dependent" }
-env-utils = { path = "../../utils/env" }
 itertools = { workspace = true }
 sea-orm = { workspace = true }
 sea-query = { workspace = true }
diff --git a/crates/services/cache/src/lib.rs b/crates/services/cache/src/lib.rs
index 87c25049a6..be444420ea 100644
--- a/crates/services/cache/src/lib.rs
+++ b/crates/services/cache/src/lib.rs
@@ -6,13 +6,13 @@ use common_models::ApplicationCacheKey;
 use common_utils::ryot_log;
 use database_models::{application_cache, prelude::ApplicationCache};
 use dependent_models::ApplicationCacheValue;
-use env_utils::APP_COMMIT_SHA;
 use itertools::Itertools;
 use sea_orm::{ActiveValue, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
 use sea_query::OnConflict;
 use serde::de::DeserializeOwned;
 
 pub struct CacheService {
+    version: String,
     db: DatabaseConnection,
     config: Arc<config::AppConfig>,
 }
@@ -22,6 +22,7 @@ impl CacheService {
         Self {
             config,
             db: db.clone(),
+            version: Utc::now().to_rfc2822(),
         }
     }
 }
@@ -64,15 +65,20 @@ impl CacheService {
         let now = Utc::now();
         let to_insert = items
             .into_iter()
-            .map(|(key, value)| application_cache::ActiveModel {
-                created_at: ActiveValue::Set(now),
-                key: ActiveValue::Set(key.clone()),
-                version: ActiveValue::Set(APP_COMMIT_SHA.to_owned()),
-                value: ActiveValue::Set(serde_json::to_value(value).unwrap()),
-                expires_at: ActiveValue::Set(Some(
-                    now + Duration::hours(self.get_expiry_for_key(&key)),
-                )),
-                ..Default::default()
+            .map(|(key, value)| {
+                let version = self
+                    .should_respect_version(&key)
+                    .then(|| self.version.to_owned());
+                application_cache::ActiveModel {
+                    created_at: ActiveValue::Set(now),
+                    key: ActiveValue::Set(key.clone()),
+                    version: ActiveValue::Set(version),
+                    value: ActiveValue::Set(serde_json::to_value(value).unwrap()),
+                    expires_at: ActiveValue::Set(
+                        now + Duration::hours(self.get_expiry_for_key(&key)),
+                    ),
+                    ..Default::default()
+                }
             })
             .collect_vec();
         let inserted = ApplicationCache::insert_many(to_insert)
@@ -111,13 +117,14 @@ impl CacheService {
             .await?;
         let mut values = HashMap::new();
         for cache in caches {
-            let valid_by_expiry = cache.expires_at.map_or(true, |ea| ea > Utc::now());
+            let valid_by_expiry = cache.expires_at > Utc::now();
             if !valid_by_expiry {
                 continue;
             }
-            let should_respect_version = self.should_respect_version(&cache.key);
-            if should_respect_version && cache.version != APP_COMMIT_SHA {
-                continue;
+            if let Some(version) = cache.version {
+                if version != self.version {
+                    continue;
+                }
             }
             values.insert(cache.key, serde_json::from_value(cache.value)?);
         }
@@ -136,7 +143,7 @@ impl CacheService {
         let deleted = ApplicationCache::update_many()
             .filter(application_cache::Column::Key.eq(key))
             .set(application_cache::ActiveModel {
-                expires_at: ActiveValue::Set(Some(Utc::now())),
+                expires_at: ActiveValue::Set(Utc::now()),
                 ..Default::default()
             })
             .exec(&self.db)
diff --git a/crates/services/importer/src/audiobookshelf.rs b/crates/services/importer/src/audiobookshelf.rs
index c27e5b74ff..9503447af2 100644
--- a/crates/services/importer/src/audiobookshelf.rs
+++ b/crates/services/importer/src/audiobookshelf.rs
@@ -1,19 +1,18 @@
-use std::{future::Future, sync::Arc};
+use std::sync::Arc;
 
 use anyhow::anyhow;
 use application_utils::{get_base_http_client, get_podcast_episode_number_by_name};
 use async_graphql::Result;
-use common_models::StringIdObject;
 use common_utils::ryot_log;
 use data_encoding::BASE64;
 use dependent_models::{ImportCompletedItem, ImportResult};
-use dependent_utils::get_identifier_from_book_isbn;
+use dependent_utils::{commit_metadata, get_identifier_from_book_isbn};
 use enum_models::{ImportSource, MediaLot, MediaSource};
 use external_models::audiobookshelf as audiobookshelf_models;
-use external_utils::audiobookshelf::get_updated_metadata;
+use external_utils::audiobookshelf::get_updated_podcast_metadata;
 use media_models::{
-    DeployUrlAndKeyImportInput, ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen,
-    UniqueMediaIdentifier,
+    CommitMediaInput, DeployUrlAndKeyImportInput, ImportOrExportMetadataItem,
+    ImportOrExportMetadataItemSeen, UniqueMediaIdentifier,
 };
 use providers::{
     google_books::GoogleBooksService, hardcover::HardcoverService, openlibrary::OpenlibraryService,
@@ -27,17 +26,13 @@ use supporting_service::SupportingService;
 
 use super::{ImportFailStep, ImportFailedItem};
 
-pub async fn import<F>(
+pub async fn import(
     input: DeployUrlAndKeyImportInput,
     ss: &Arc<SupportingService>,
     hardcover_service: &HardcoverService,
     google_books_service: &GoogleBooksService,
     open_library_service: &OpenlibraryService,
-    commit_metadata: impl Fn(UniqueMediaIdentifier) -> F,
-) -> Result<ImportResult>
-where
-    F: Future<Output = Result<StringIdObject>>,
-{
+) -> Result<ImportResult> {
     let mut completed = vec![];
     let mut failed = vec![];
     let url = format!("{}/api", input.api_url);
@@ -127,13 +122,19 @@ where
                             if let Some(true) =
                                 episode_details.user_media_progress.map(|u| u.is_finished)
                             {
-                                commit_metadata(UniqueMediaIdentifier {
-                                    lot,
-                                    source,
-                                    identifier: itunes_id.clone(),
-                                })
+                                commit_metadata(
+                                    CommitMediaInput {
+                                        name: "Loading...".to_owned(),
+                                        unique: UniqueMediaIdentifier {
+                                            lot,
+                                            source,
+                                            identifier: itunes_id.clone(),
+                                        },
+                                    },
+                                    ss,
+                                )
                                 .await?;
-                                let podcast = get_updated_metadata(&itunes_id, ss).await?;
+                                let podcast = get_updated_podcast_metadata(&itunes_id, ss).await?;
                                 if let Some(pe) = podcast.podcast_specifics.and_then(|p| {
                                     get_podcast_episode_number_by_name(&p, &episode.title)
                                 }) {
diff --git a/crates/services/importer/src/lib.rs b/crates/services/importer/src/lib.rs
index 4b6bea0dd1..f518b060a5 100644
--- a/crates/services/importer/src/lib.rs
+++ b/crates/services/importer/src/lib.rs
@@ -10,13 +10,13 @@ use database_models::{
     prelude::{Exercise, ImportReport},
 };
 use dependent_utils::{
-    commit_metadata, deploy_background_job, generate_exercise_id, get_google_books_service,
-    get_hardcover_service, get_openlibrary_service, get_tmdb_non_media_service, process_import,
+    deploy_background_job, generate_exercise_id, get_google_books_service, get_hardcover_service,
+    get_openlibrary_service, get_tmdb_non_media_service, process_import,
 };
 use enum_models::ImportSource;
 use enum_models::{ExerciseLot, ExerciseSource};
 use importer_models::{ImportFailStep, ImportFailedItem};
-use media_models::{CommitMediaInput, DeployImportJobInput, ImportOrExportMetadataItem};
+use media_models::{DeployImportJobInput, ImportOrExportMetadataItem};
 use rust_decimal_macros::dec;
 use sea_orm::{
     prelude::Expr, ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, QueryFilter, QueryOrder,
@@ -118,15 +118,6 @@ impl ImporterService {
                     &get_hardcover_service(&self.0.config).await.unwrap(),
                     &get_google_books_service(&self.0.config).await.unwrap(),
                     &get_openlibrary_service(&self.0.config).await.unwrap(),
-                    |input| {
-                        commit_metadata(
-                            CommitMediaInput {
-                                unique: input,
-                                name: "Loading...".to_owned(),
-                            },
-                            &self.0,
-                        )
-                    },
                 )
                 .await
             }
diff --git a/crates/services/integration/src/lib.rs b/crates/services/integration/src/lib.rs
index a58ab62ddb..d80db25cf6 100644
--- a/crates/services/integration/src/lib.rs
+++ b/crates/services/integration/src/lib.rs
@@ -11,11 +11,10 @@ use database_models::{
 use database_utils::{server_key_validation_guard, user_by_id};
 use dependent_models::{ImportCompletedItem, ImportResult};
 use dependent_utils::{
-    commit_metadata, get_google_books_service, get_hardcover_service, get_openlibrary_service,
-    process_import,
+    get_google_books_service, get_hardcover_service, get_openlibrary_service, process_import,
 };
 use enum_models::{EntityLot, IntegrationLot, IntegrationProvider, MediaLot};
-use media_models::{CommitMediaInput, SeenShowExtraInformation};
+use media_models::SeenShowExtraInformation;
 use rust_decimal_macros::dec;
 use sea_orm::{ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, QueryFilter, QuerySelect};
 use supporting_service::SupportingService;
@@ -263,14 +262,15 @@ impl IntegrationService {
         let integrations = Integration::find()
             .filter(integration::Column::UserId.eq(user_id))
             .filter(integration::Column::Lot.eq(IntegrationLot::Yank))
+            .filter(
+                integration::Column::IsDisabled
+                    .is_null()
+                    .or(integration::Column::IsDisabled.eq(false)),
+            )
             .all(&self.0.db)
             .await?;
         let mut progress_updates = vec![];
         for integration in integrations.into_iter() {
-            if integration.is_disabled.unwrap_or_default() {
-                ryot_log!(debug, "Integration {} is disabled", integration.id);
-                continue;
-            }
             let specifics = integration.clone().provider_specifics.unwrap();
             let response = match integration.provider {
                 IntegrationProvider::Audiobookshelf => {
@@ -281,15 +281,6 @@ impl IntegrationService {
                         &get_hardcover_service(&self.0.config).await.unwrap(),
                         &get_google_books_service(&self.0.config).await.unwrap(),
                         &get_openlibrary_service(&self.0.config).await.unwrap(),
-                        |input| {
-                            commit_metadata(
-                                CommitMediaInput {
-                                    unique: input,
-                                    name: "Loading...".to_owned(),
-                                },
-                                &self.0,
-                            )
-                        },
                     )
                     .await
                 }
@@ -354,14 +345,15 @@ impl IntegrationService {
         let integrations = Integration::find()
             .filter(integration::Column::UserId.eq(user_id))
             .filter(integration::Column::SyncToOwnedCollection.eq(true))
+            .filter(
+                integration::Column::IsDisabled
+                    .is_null()
+                    .or(integration::Column::IsDisabled.eq(false)),
+            )
             .all(&self.0.db)
             .await?;
         let mut progress_updates = vec![];
         for integration in integrations.into_iter() {
-            if integration.is_disabled.unwrap_or_default() {
-                ryot_log!(debug, "Integration {} is disabled", integration.id);
-                continue;
-            }
             let specifics = integration.clone().provider_specifics.unwrap();
             let response = match integration.provider {
                 IntegrationProvider::Audiobookshelf => {
diff --git a/crates/services/integration/src/yank/audiobookshelf.rs b/crates/services/integration/src/yank/audiobookshelf.rs
index 0a2d6cface..2a56744202 100644
--- a/crates/services/integration/src/yank/audiobookshelf.rs
+++ b/crates/services/integration/src/yank/audiobookshelf.rs
@@ -1,16 +1,17 @@
-use std::{future::Future, sync::Arc};
+use std::sync::Arc;
 
 use anyhow::{anyhow, Result};
 use application_utils::{get_base_http_client, get_podcast_episode_number_by_name};
-use common_models::{DefaultCollection, StringIdObject};
+use common_models::DefaultCollection;
 use common_utils::ryot_log;
 use dependent_models::{ImportCompletedItem, ImportResult};
-use dependent_utils::get_identifier_from_book_isbn;
+use dependent_utils::{commit_metadata, get_identifier_from_book_isbn};
 use enum_models::{MediaLot, MediaSource};
 use external_models::audiobookshelf::{self, LibrariesListResponse, ListResponse};
-use external_utils::audiobookshelf::get_updated_metadata;
+use external_utils::audiobookshelf::get_updated_podcast_metadata;
 use media_models::{
-    ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen, UniqueMediaIdentifier,
+    CommitMediaInput, ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen,
+    UniqueMediaIdentifier,
 };
 use providers::{
     google_books::GoogleBooksService, hardcover::HardcoverService, openlibrary::OpenlibraryService,
@@ -29,18 +30,14 @@ fn get_http_client(access_token: &String) -> Client {
     )]))
 }
 
-pub async fn yank_progress<F>(
+pub async fn yank_progress(
     base_url: String,
     access_token: String,
     ss: &Arc<SupportingService>,
     hardcover_service: &HardcoverService,
     google_books_service: &GoogleBooksService,
     open_library_service: &OpenlibraryService,
-    commit_metadata: impl Fn(UniqueMediaIdentifier) -> F,
-) -> Result<ImportResult>
-where
-    F: Future<Output = async_graphql::Result<StringIdObject>>,
-{
+) -> Result<ImportResult> {
     let url = format!("{}/api", base_url);
     let client = get_http_client(&access_token);
 
@@ -59,80 +56,73 @@ where
 
     for item in resp.library_items.iter() {
         let metadata = item.media.clone().unwrap().metadata;
-        let (progress_id, identifier, lot, source, podcast_episode_number) =
-            if Some("epub".to_string()) == item.media.as_ref().unwrap().ebook_format {
-                match &metadata.isbn {
-                    Some(isbn) => match get_identifier_from_book_isbn(
-                        isbn,
-                        hardcover_service,
-                        google_books_service,
-                        open_library_service,
-                    )
-                    .await
-                    {
-                        Some(id) => (item.id.clone(), id.0, MediaLot::Book, id.1, None),
-                        _ => {
-                            ryot_log!(debug, "No Google Books ID found for ISBN {:#?}", isbn);
-                            continue;
-                        }
-                    },
-                    _ => {
-                        ryot_log!(debug, "No ISBN found for item {:#?}", item);
-                        continue;
-                    }
-                }
-            } else if let Some(asin) = metadata.asin.clone() {
-                (
+
+        let update_information = 'ui: {
+            if let Some(asin) = metadata.asin.clone() {
+                break 'ui Some((
                     item.id.clone(),
                     asin,
                     MediaLot::AudioBook,
                     MediaSource::Audible,
                     None,
-                )
-            } else if let Some(itunes_id) = metadata.itunes_id.clone() {
-                match &item.recent_episode {
-                    Some(pe) => {
-                        let lot = MediaLot::Podcast;
-                        let source = MediaSource::Itunes;
-                        commit_metadata(UniqueMediaIdentifier {
+                ));
+            }
+            if let (Some(itunes_id), Some(pe)) = (metadata.itunes_id.clone(), &item.recent_episode)
+            {
+                let lot = MediaLot::Podcast;
+                let source = MediaSource::Itunes;
+                commit_metadata(
+                    CommitMediaInput {
+                        name: "Loading...".to_owned(),
+                        unique: UniqueMediaIdentifier {
                             lot,
                             source,
                             identifier: itunes_id.clone(),
-                        })
-                        .await
-                        .unwrap();
-                        let podcast = get_updated_metadata(&itunes_id, ss).await?;
-                        match podcast
-                            .podcast_specifics
-                            .and_then(|p| get_podcast_episode_number_by_name(&p, &pe.title))
-                        {
-                            Some(episode) => (
-                                format!("{}/{}", item.id, pe.id),
-                                itunes_id,
-                                lot,
-                                source,
-                                Some(episode),
-                            ),
-                            _ => {
-                                ryot_log!(debug, "No podcast found for iTunes ID {:#?}", itunes_id);
-                                continue;
-                            }
-                        }
-                    }
-                    _ => {
-                        ryot_log!(debug, "No recent episode found for item {:?}", item);
-                        continue;
-                    }
+                        },
+                    },
+                    ss,
+                )
+                .await
+                .unwrap();
+                let podcast = get_updated_podcast_metadata(&itunes_id, ss).await?;
+                if let Some(episode) = podcast
+                    .podcast_specifics
+                    .and_then(|p| get_podcast_episode_number_by_name(&p, &pe.title))
+                {
+                    break 'ui Some((
+                        format!("{}/{}", item.id, pe.id),
+                        itunes_id,
+                        lot,
+                        source,
+                        Some(episode),
+                    ));
                 }
-            } else {
-                ryot_log!(
-                    debug,
-                    "No ASIN, ISBN or iTunes ID found for item {:?}",
-                    item
-                );
-                continue;
             };
+            if let Some(isbn) = metadata.isbn.clone() {
+                if let Some(id) = get_identifier_from_book_isbn(
+                    &isbn,
+                    hardcover_service,
+                    google_books_service,
+                    open_library_service,
+                )
+                .await
+                {
+                    break 'ui Some((item.id.clone(), id.0, MediaLot::Book, id.1, None));
+                };
+            };
+            None
+        };
 
+        let Some((progress_id, identifier, lot, source, podcast_episode_number)) =
+            update_information
+        else {
+            ryot_log!(
+                debug,
+                "No ASIN, ISBN or iTunes ID found for item {:?}",
+                item
+            );
+            continue;
+        };
         match client
             .get(format!("{}/me/progress/{}", url, progress_id))
             .send()
@@ -154,6 +144,10 @@ where
                         progress = ebook_progress;
                     }
                 }
+                if progress == dec!(1) && resp.is_finished {
+                    ryot_log!(debug, "Item {:?} is finished", item);
+                    continue;
+                }
                 result
                     .completed
                     .push(ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
diff --git a/crates/services/integration/src/yank/youtube_music.rs b/crates/services/integration/src/yank/youtube_music.rs
index 08bf10f4af..f462d39743 100644
--- a/crates/services/integration/src/yank/youtube_music.rs
+++ b/crates/services/integration/src/yank/youtube_music.rs
@@ -1,7 +1,8 @@
 use std::{collections::HashMap, sync::Arc};
 
 use anyhow::Result;
-use chrono::{NaiveDate, Utc};
+use application_utils::{get_current_date, get_current_time};
+use chrono::{Duration, NaiveDate, NaiveDateTime};
 use common_models::{ApplicationCacheKey, UserLevelCacheKey, YoutubeMusicSongListened};
 use common_utils::TEMP_DIR;
 use dependent_models::{ApplicationCacheValue, ImportCompletedItem, ImportResult};
@@ -11,6 +12,12 @@ use rust_decimal_macros::dec;
 use rustypipe::client::RustyPipe;
 use supporting_service::SupportingService;
 
+static THRESHOLD_MINUTES: i64 = 10;
+
+fn get_end_of_day(date: NaiveDate) -> NaiveDateTime {
+    date.and_hms_opt(23, 59, 59).unwrap()
+}
+
 // DEV: Youtube music only returns one record regardless of how many time you have listened
 // to it that day. It also does not include what time the song was listened to. So, for
 // each song listened to today, we cache the song id with `is_complete=false` and return
@@ -18,12 +25,20 @@ use supporting_service::SupportingService;
 // `is_complete=true` and return `progress=100%`. Thus the music gets marked as complete
 // and gets the correct start and end date marked. When the song appears again that day, we
 // silently ignore it since it is already in the cache as completely synced.
+//
+// Also, 10 minutes before the end of the day, we do not deploy the 35% progress since it
+// messes up with the caching mechanism which works on the current date.
 pub async fn yank_progress(
     auth_cookie: String,
     user_id: &String,
     ss: &Arc<SupportingService>,
 ) -> Result<ImportResult> {
-    let date = Utc::now().date_naive();
+    let date = get_current_date(&ss.timezone);
+    let current_time = get_current_time(&ss.timezone);
+    let end_of_day = get_end_of_day(date);
+    let is_within_threshold =
+        end_of_day.signed_duration_since(current_time) <= Duration::minutes(THRESHOLD_MINUTES);
+
     let client = RustyPipe::builder().storage_dir(TEMP_DIR).build().unwrap();
     client.user_auth_set_cookie(auth_cookie).await?;
     let music_history = client
@@ -69,7 +84,7 @@ pub async fn yank_progress(
             continue;
         };
         let (cache_value, progress) = match items_in_cache.get(cache_key) {
-            None => (
+            None if !is_within_threshold => (
                 ApplicationCacheValue::YoutubeMusicSongListened(false),
                 dec!(35),
             ),
diff --git a/crates/services/miscellaneous/Cargo.toml b/crates/services/miscellaneous/Cargo.toml
index ce280e100d..4cdd711807 100644
--- a/crates/services/miscellaneous/Cargo.toml
+++ b/crates/services/miscellaneous/Cargo.toml
@@ -10,6 +10,7 @@ background-models = { path = "../../models/background" }
 chrono = { workspace = true }
 common-models = { path = "../../models/common" }
 common-utils = { path = "../../utils/common" }
+convert_case = { workspace = true }
 database-models = { path = "../../models/database" }
 database-utils = { path = "../../utils/database" }
 dependent-models = { path = "../../models/dependent" }
diff --git a/crates/services/miscellaneous/src/lib.rs b/crates/services/miscellaneous/src/lib.rs
index 8c4927fb99..bc161b5886 100644
--- a/crates/services/miscellaneous/src/lib.rs
+++ b/crates/services/miscellaneous/src/lib.rs
@@ -9,7 +9,7 @@ use application_utils::{
 };
 use async_graphql::{Error, Result};
 use background_models::{ApplicationJob, HpApplicationJob, MpApplicationJob};
-use chrono::{Days, NaiveDate, Utc};
+use chrono::{Days, Duration, NaiveDate, Utc};
 use common_models::{
     ApplicationCacheKey, BackgroundJob, ChangeCollectionToEntityInput, DefaultCollection,
     IdAndNamedObject, MetadataGroupSearchInput, MetadataSearchInput, PeopleSearchInput,
@@ -19,6 +19,7 @@ use common_models::{
 use common_utils::{
     get_first_and_last_day_of_month, ryot_log, PAGE_SIZE, SHOW_SPECIAL_SEASON_NAMES,
 };
+use convert_case::{Case, Casing};
 use database_models::{
     access_link, application_cache, calendar_event, collection, collection_to_entity,
     functions::{associate_user_with_entity, get_user_to_entity_association},
@@ -50,8 +51,8 @@ use dependent_utils::{
     deploy_after_handle_media_seen_tasks, deploy_background_job, deploy_update_metadata_group_job,
     deploy_update_metadata_job, deploy_update_person_job, first_metadata_image_as_url,
     get_entity_recently_consumed, get_google_books_service, get_hardcover_service,
-    get_metadata_provider, get_openlibrary_service, get_tmdb_non_media_service,
-    get_users_and_cte_monitoring_entity, get_users_monitoring_entity,
+    get_metadata_provider, get_openlibrary_service, get_pending_notifications_for_user,
+    get_tmdb_non_media_service, get_users_and_cte_monitoring_entity, get_users_monitoring_entity,
     handle_after_media_seen_tasks, is_metadata_finished_by_user, metadata_groups_list,
     metadata_images_as_urls, metadata_list, people_list, post_review, progress_update,
     refresh_collection_to_entity_association, remove_entity_from_collection,
@@ -2708,35 +2709,20 @@ ORDER BY RANDOM() LIMIT 10;
         Ok(())
     }
 
-    async fn get_pending_notifications_for_user(
-        &self,
-        user_id: &String,
-        lot: UserNotificationLot,
-    ) -> Result<Vec<user_notification::Model>> {
-        let notifications = UserNotification::find()
-            .filter(user_notification::Column::UserId.eq(user_id))
-            .filter(user_notification::Column::Lot.eq(lot))
-            .filter(
-                user_notification::Column::IsAddressed
-                    .eq(false)
-                    .or(user_notification::Column::IsAddressed.is_null()),
-            )
-            .all(&self.0.db)
-            .await?;
-        Ok(notifications)
-    }
-
-    async fn send_pending_queued_notifications(&self) -> Result<()> {
+    pub async fn send_pending_notifications(&self) -> Result<()> {
         let users = User::find().all(&self.0.db).await?;
         for user_details in users {
             ryot_log!(debug, "Sending notification to user: {:?}", user_details.id);
-            let notifications = self
-                .get_pending_notifications_for_user(&user_details.id, UserNotificationLot::Queued)
-                .await?;
+            let notifications = get_pending_notifications_for_user(
+                &user_details.id,
+                UserNotificationLot::Queued,
+                &self.0,
+            )
+            .await?;
             if notifications.is_empty() {
                 continue;
             }
-            let notification_ids = notifications.iter().map(|n| n.id).collect_vec();
+            let notification_ids = notifications.iter().map(|n| n.id.clone()).collect_vec();
             let msg = notifications
                 .into_iter()
                 .map(|n| n.message)
@@ -2771,55 +2757,69 @@ ORDER BY RANDOM() LIMIT 10;
         Ok(())
     }
 
-    pub async fn send_pending_immediate_notifications(&self) -> Result<()> {
-        let users = User::find().all(&self.0.db).await?;
-        for user_details in users {
-            ryot_log!(debug, "Sending notification to user: {:?}", user_details.id);
-            let notifications = self
-                .get_pending_notifications_for_user(
-                    &user_details.id,
-                    UserNotificationLot::Immediate,
-                )
-                .await?;
-            let notification_ids = notifications.iter().map(|n| n.id).collect_vec();
-            let platforms = NotificationPlatform::find()
-                .filter(notification_platform::Column::UserId.eq(&user_details.id))
+    pub async fn sync_integrations_data_to_owned_collection(&self) -> Result<()> {
+        self.0
+            .perform_application_job(ApplicationJob::Mp(MpApplicationJob::SyncIntegrationsData))
+            .await?;
+        Ok(())
+    }
+
+    async fn queue_notifications_for_outdated_seen_entries(&self) -> Result<()> {
+        if !self.0.is_server_key_validated().await? {
+            return Ok(());
+        }
+        for state in [SeenState::InProgress, SeenState::OnAHold] {
+            let days = match state {
+                SeenState::InProgress => 7,
+                SeenState::OnAHold => 14,
+                _ => unreachable!(),
+            };
+            let threshold = Utc::now() - Duration::days(days);
+            let seen_items = Seen::find()
+                .filter(seen::Column::State.eq(state))
+                .filter(seen::Column::LastUpdatedOn.lte(threshold))
                 .all(&self.0.db)
                 .await?;
-            for notification in notifications {
-                for platform in platforms.iter() {
-                    if platform.is_disabled.unwrap_or_default() {
-                        ryot_log!(
-                        debug,
-                        "Skipping sending notification to user: {} for platform: {} since it is disabled",
-                        user_details.id,
-                        platform.lot
-                    );
-                        continue;
-                    }
-                    if let Err(err) = send_notification(
-                        platform.platform_specifics.to_owned(),
-                        &self.0.config,
-                        &notification.message,
-                    )
-                    .await
-                    {
-                        ryot_log!(trace, "Error sending notification: {:?}", err);
-                    }
-                }
-            }
-            UserNotification::update_many()
-                .filter(user_notification::Column::Id.is_in(notification_ids))
-                .col_expr(user_notification::Column::IsAddressed, Expr::value(true))
-                .exec(&self.0.db)
+            for seen_item in seen_items {
+                let Some(metadata) = seen_item.find_related(Metadata).one(&self.0.db).await? else {
+                    continue;
+                };
+                let state = seen_item
+                    .state
+                    .to_string()
+                    .to_case(Case::Title)
+                    .to_case(Case::Lower);
+                create_notification_for_user(
+                    &seen_item.user_id,
+                    &(
+                        format!(
+                            "{} ({}) has been kept {} for more than {} days. Last updated on: {}.",
+                            metadata.title,
+                            metadata.lot,
+                            state,
+                            days,
+                            seen_item.last_updated_on.date_naive()
+                        ),
+                        UserNotificationContent::OutdatedSeenEntries,
+                    ),
+                    UserNotificationLot::Display,
+                    &self.0,
+                )
                 .await?;
+            }
         }
         Ok(())
     }
 
-    pub async fn sync_integrations_data_to_owned_collection(&self) -> Result<()> {
-        self.0
-            .perform_application_job(ApplicationJob::Mp(MpApplicationJob::SyncIntegrationsData))
+    pub async fn update_user_last_activity_performed(
+        &self,
+        user_id: String,
+        timestamp: DateTimeUtc,
+    ) -> Result<()> {
+        User::update_many()
+            .filter(user::Column::Id.eq(user_id))
+            .col_expr(user::Column::LastActivityOn, Expr::value(timestamp))
+            .exec(&self.0.db)
             .await?;
         Ok(())
     }
@@ -2849,8 +2849,6 @@ ORDER BY RANDOM() LIMIT 10;
         self.queue_notifications_for_released_media()
             .await
             .trace_ok();
-        ryot_log!(trace, "Sending all pending notifications");
-        self.send_pending_queued_notifications().await.trace_ok();
         ryot_log!(trace, "Cleaning up user and metadata association");
         self.cleanup_user_and_metadata_association()
             .await
@@ -2861,6 +2859,10 @@ ORDER BY RANDOM() LIMIT 10;
         self.sync_integrations_data_to_owned_collection()
             .await
             .trace_ok();
+        ryot_log!(trace, "Queueing notifications for outdated seen entries");
+        self.queue_notifications_for_outdated_seen_entries()
+            .await
+            .trace_ok();
         ryot_log!(trace, "Removing useless data");
         self.remove_useless_data().await.trace_ok();
         ryot_log!(trace, "Putting entities in partial state");
diff --git a/crates/services/user/src/lib.rs b/crates/services/user/src/lib.rs
index 5a672baeca..37f23d4afa 100644
--- a/crates/services/user/src/lib.rs
+++ b/crates/services/user/src/lib.rs
@@ -8,8 +8,8 @@ use common_models::{ApplicationCacheKey, DefaultCollection, StringIdObject, User
 use common_utils::ryot_log;
 use database_models::{
     access_link, integration, metadata, notification_platform,
-    prelude::{AccessLink, Integration, Metadata, NotificationPlatform, User},
-    user,
+    prelude::{AccessLink, Integration, Metadata, NotificationPlatform, User, UserNotification},
+    user, user_notification,
 };
 use database_utils::{
     admin_account_guard, deploy_job_to_calculate_user_activities_and_summary, ilike_sql,
@@ -18,9 +18,11 @@ use database_utils::{
 use dependent_models::{
     ApplicationCacheValue, UserDetailsResult, UserMetadataRecommendationsResponse,
 };
-use dependent_utils::create_or_update_collection;
+use dependent_utils::{create_or_update_collection, get_pending_notifications_for_user};
 use enum_meta::Meta;
-use enum_models::{IntegrationLot, IntegrationProvider, NotificationPlatformLot, UserLot};
+use enum_models::{
+    IntegrationLot, IntegrationProvider, NotificationPlatformLot, UserLot, UserNotificationLot,
+};
 use itertools::Itertools;
 use jwt_service::{sign, AccessLinkClaims};
 use media_models::{
@@ -60,6 +62,7 @@ impl UserService {
     pub async fn user_metadata_recommendations(
         &self,
         user_id: &String,
+        should_refresh: Option<bool>,
     ) -> Result<UserMetadataRecommendationsResponse> {
         let cc = &self.0.cache_service;
         let metadata_recommendations_key =
@@ -67,11 +70,18 @@ impl UserService {
                 input: (),
                 user_id: user_id.to_owned(),
             });
-        if let Some(recommendations) = cc
-            .get_value::<UserMetadataRecommendationsResponse>(metadata_recommendations_key.clone())
-            .await
-        {
-            return Ok(recommendations);
+        match should_refresh {
+            Some(true) => {}
+            _ => {
+                if let Some(recommendations) = cc
+                    .get_value::<UserMetadataRecommendationsResponse>(
+                        metadata_recommendations_key.clone(),
+                    )
+                    .await
+                {
+                    return Ok(recommendations);
+                };
+            }
         };
         let preferences = user_by_id(user_id, &self.0).await?.preferences;
         let limit = preferences
@@ -120,19 +130,6 @@ impl UserService {
         Ok(recommendations)
     }
 
-    pub async fn refresh_user_metadata_recommendations(&self, user_id: &String) -> Result<bool> {
-        self.0
-            .cache_service
-            .expire_key(ApplicationCacheKey::UserMetadataRecommendations(
-                UserLevelCacheKey {
-                    input: (),
-                    user_id: user_id.to_owned(),
-                },
-            ))
-            .await?;
-        Ok(true)
-    }
-
     pub async fn user_access_links(&self, user_id: &String) -> Result<Vec<access_link::Model>> {
         let links = AccessLink::find()
             .filter(access_link::Column::UserId.eq(user_id))
@@ -696,6 +693,16 @@ impl UserService {
         Ok(integrations)
     }
 
+    pub async fn user_pending_notifications(
+        &self,
+        user_id: &String,
+    ) -> Result<Vec<user_notification::Model>> {
+        let notifications =
+            get_pending_notifications_for_user(user_id, UserNotificationLot::Display, &self.0)
+                .await?;
+        Ok(notifications)
+    }
+
     pub async fn user_notification_platforms(
         &self,
         user_id: &String,
@@ -748,4 +755,18 @@ impl UserService {
             .map(|u| u.id);
         Ok(user)
     }
+
+    pub async fn mark_notifications_as_addressed(
+        &self,
+        user_id: String,
+        notification_ids: Vec<String>,
+    ) -> Result<bool> {
+        UserNotification::update_many()
+            .filter(user_notification::Column::UserId.eq(user_id))
+            .filter(user_notification::Column::Id.is_in(notification_ids))
+            .col_expr(user_notification::Column::IsAddressed, Expr::value(true))
+            .exec(&self.0.db)
+            .await?;
+        Ok(true)
+    }
 }
diff --git a/crates/traits/Cargo.toml b/crates/traits/Cargo.toml
index e79548a9bf..07d1897892 100644
--- a/crates/traits/Cargo.toml
+++ b/crates/traits/Cargo.toml
@@ -11,11 +11,10 @@ async-graphql = { workspace = true }
 async-trait = { workspace = true }
 common-models = { path = "../models/common" }
 common-utils = { path = "../utils/common" }
-config = { path = "../config" }
 database-models = { path = "../models/database" }
 database-utils = { path = "../utils/database" }
 media-models = { path = "../models/media" }
-sea-orm = { workspace = true }
+supporting-service = { path = "../services/supporting" }
 tracing = { workspace = true }
 
 [package.metadata.cargo-machete]
diff --git a/crates/traits/src/lib.rs b/crates/traits/src/lib.rs
index 1a7b061d1d..f719633aa1 100644
--- a/crates/traits/src/lib.rs
+++ b/crates/traits/src/lib.rs
@@ -7,12 +7,12 @@ use async_trait::async_trait;
 use common_models::{BackendError, PersonSourceSpecifics};
 use common_utils::ryot_log;
 use database_models::metadata_group::MetadataGroupWithoutId;
-use database_utils::check_token;
+use database_utils::{check_token, deploy_job_to_mark_user_last_activity};
 use dependent_models::{
-    MetadataGroupSearchResponse, PersonDetails, PeopleSearchResponse, SearchResults,
+    MetadataGroupSearchResponse, PeopleSearchResponse, PersonDetails, SearchResults,
 };
 use media_models::{MetadataDetails, MetadataSearchItem, PartialMetadataWithoutId};
-use sea_orm::DatabaseConnection;
+use supporting_service::SupportingService;
 
 #[async_trait]
 pub trait MediaProvider {
@@ -103,10 +103,12 @@ pub trait AuthProvider {
 
     async fn user_id_from_ctx(&self, ctx: &Context<'_>) -> GraphqlResult<String> {
         let auth_ctx = ctx.data_unchecked::<AuthContext>();
+        let ss = ctx.data_unchecked::<Arc<SupportingService>>();
         if let Some(auth_token) = &auth_ctx.auth_token {
-            let config = ctx.data_unchecked::<Arc<config::AppConfig>>();
-            let db = ctx.data_unchecked::<DatabaseConnection>();
-            check_token(auth_token, self.is_mutation(), &config.users.jwt_secret, db).await?;
+            check_token(auth_token, self.is_mutation(), ss).await?;
+        }
+        if let Some(user_id) = &auth_ctx.user_id {
+            deploy_job_to_mark_user_last_activity(user_id, ss).await?;
         }
         auth_ctx
             .user_id
diff --git a/crates/utils/application/src/lib.rs b/crates/utils/application/src/lib.rs
index 00a3893b35..8b12224e2e 100644
--- a/crates/utils/application/src/lib.rs
+++ b/crates/utils/application/src/lib.rs
@@ -7,7 +7,7 @@ use axum::{
     http::{header::AUTHORIZATION, request::Parts, StatusCode},
     Extension, RequestPartsExt,
 };
-use chrono::{NaiveDate, Utc};
+use chrono::{NaiveDate, NaiveDateTime, Utc};
 use common_utils::USER_AGENT_STR;
 use file_storage_service::FileStorageService;
 use media_models::{
@@ -83,8 +83,12 @@ pub fn get_base_http_client(headers: Option<Vec<(HeaderName, HeaderValue)>>) ->
         .unwrap()
 }
 
+pub fn get_current_time(timezone: &chrono_tz::Tz) -> NaiveDateTime {
+    Utc::now().with_timezone(timezone).naive_local()
+}
+
 pub fn get_current_date(timezone: &chrono_tz::Tz) -> NaiveDate {
-    Utc::now().with_timezone(timezone).date_naive()
+    get_current_time(timezone).date()
 }
 
 pub fn graphql_to_db_order(value: GraphqlSortOrder) -> Order {
diff --git a/crates/utils/database/src/lib.rs b/crates/utils/database/src/lib.rs
index 7eada9948b..669f9cc4a5 100644
--- a/crates/utils/database/src/lib.rs
+++ b/crates/utils/database/src/lib.rs
@@ -4,8 +4,8 @@ use application_utils::{
     get_podcast_episode_by_number, get_show_episode_by_numbers, GraphqlRepresentation,
 };
 use async_graphql::{Error, Result};
-use background_models::{ApplicationJob, HpApplicationJob};
-use chrono::Timelike;
+use background_models::{ApplicationJob, HpApplicationJob, LpApplicationJob};
+use chrono::{Timelike, Utc};
 use common_models::{
     BackendError, DailyUserActivityHourRecord, DailyUserActivityHourRecordEntity, IdAndNamedObject,
 };
@@ -263,15 +263,14 @@ pub fn user_claims_from_token(token: &str, jwt_secret: &str) -> Result<Claims> {
 pub async fn check_token(
     token: &str,
     is_mutation: bool,
-    jwt_secret: &str,
-    db: &DatabaseConnection,
+    ss: &Arc<SupportingService>,
 ) -> Result<bool> {
-    let claims = user_claims_from_token(token, jwt_secret)?;
+    let claims = user_claims_from_token(token, &ss.config.users.jwt_secret)?;
     let Some(access_link) = claims.access_link else {
         return Ok(true);
     };
     let access_link = AccessLink::find_by_id(access_link.id)
-        .one(db)
+        .one(&ss.db)
         .await?
         .ok_or_else(|| Error::new(BackendError::SessionExpired.to_string()))?;
     if access_link.is_revoked.unwrap_or_default() {
@@ -286,6 +285,18 @@ pub async fn check_token(
     Ok(true)
 }
 
+#[inline]
+pub async fn deploy_job_to_mark_user_last_activity(
+    user_id: &String,
+    ss: &Arc<SupportingService>,
+) -> Result<()> {
+    ss.perform_application_job(ApplicationJob::Lp(
+        LpApplicationJob::UpdateUserLastActivityPerformed(user_id.to_owned(), Utc::now()),
+    ))
+    .await?;
+    Ok(())
+}
+
 pub async fn item_reviews(
     user_id: &String,
     entity_id: &String,
diff --git a/crates/utils/dependent/src/lib.rs b/crates/utils/dependent/src/lib.rs
index 792cb44deb..46da5f68a6 100644
--- a/crates/utils/dependent/src/lib.rs
+++ b/crates/utils/dependent/src/lib.rs
@@ -20,8 +20,8 @@ use database_models::{
     metadata_to_metadata_group, metadata_to_person, monitored_entity, person,
     prelude::{
         Collection, CollectionToEntity, Exercise, Genre, Metadata, MetadataGroup, MetadataToGenre,
-        MetadataToMetadata, MetadataToPerson, MonitoredEntity, Person, Seen, UserToEntity, Workout,
-        WorkoutTemplate,
+        MetadataToMetadata, MetadataToPerson, MonitoredEntity, Person, Seen, UserNotification,
+        UserToEntity, Workout, WorkoutTemplate,
     },
     review, seen, user_measurement, user_notification, user_to_entity, workout, workout_template,
 };
@@ -2061,7 +2061,7 @@ pub async fn create_or_update_user_workout(
                         format!("New workout created - {}", data.name),
                         UserNotificationContent::NewWorkoutCreated,
                     ),
-                    UserNotificationLot::Immediate,
+                    UserNotificationLot::Queued,
                     ss,
                 )
                 .await?
@@ -2831,6 +2831,18 @@ pub async fn metadata_list(
                     .add(Expr::col(metadata::Column::Description).ilike(ilike_sql(&v))),
             )
         })
+        .apply_if(
+            input.filter.clone().and_then(|f| f.date_range),
+            |outer_query, outer_value| {
+                outer_query
+                    .apply_if(outer_value.start_date, |inner_query, inner_value| {
+                        inner_query.filter(seen::Column::FinishedOn.gte(inner_value))
+                    })
+                    .apply_if(outer_value.end_date, |inner_query, inner_value| {
+                        inner_query.filter(seen::Column::FinishedOn.lte(inner_value))
+                    })
+            },
+        )
         .apply_if(
             input.filter.clone().and_then(|f| f.collections),
             |query, v| {
@@ -3239,3 +3251,21 @@ pub async fn exercises_list(
         items,
     })
 }
+
+pub async fn get_pending_notifications_for_user(
+    user_id: &String,
+    lot: UserNotificationLot,
+    ss: &Arc<SupportingService>,
+) -> Result<Vec<user_notification::Model>> {
+    let notifications = UserNotification::find()
+        .filter(user_notification::Column::UserId.eq(user_id))
+        .filter(user_notification::Column::Lot.eq(lot))
+        .filter(
+            user_notification::Column::IsAddressed
+                .eq(false)
+                .or(user_notification::Column::IsAddressed.is_null()),
+        )
+        .all(&ss.db)
+        .await?;
+    Ok(notifications)
+}
diff --git a/crates/utils/env/src/lib.rs b/crates/utils/env/src/lib.rs
index 7ab09b2425..398fb6a52c 100644
--- a/crates/utils/env/src/lib.rs
+++ b/crates/utils/env/src/lib.rs
@@ -1,4 +1,3 @@
-pub const APP_COMMIT_SHA: &str = env!("APP_COMMIT_SHA");
 pub const APP_VERSION: &str = env!("APP_VERSION");
 pub const UNKEY_API_ID: &str = env!("UNKEY_API_ID");
 pub const TRAKT_CLIENT_ID: &str = env!("TRAKT_CLIENT_ID");
diff --git a/crates/utils/external/src/lib.rs b/crates/utils/external/src/lib.rs
index 171488d5ab..83355b1650 100644
--- a/crates/utils/external/src/lib.rs
+++ b/crates/utils/external/src/lib.rs
@@ -120,7 +120,7 @@ pub mod jellyfin {
 pub mod audiobookshelf {
     use super::*;
 
-    pub async fn get_updated_metadata(
+    pub async fn get_updated_podcast_metadata(
         identifier: &String,
         ss: &Arc<SupportingService>,
     ) -> Result<metadata::Model> {
diff --git a/docs/content/configuration.md b/docs/content/configuration.md
index 911a2389c2..782f682674 100644
--- a/docs/content/configuration.md
+++ b/docs/content/configuration.md
@@ -17,11 +17,6 @@ Ryot serves the final configuration loaded at the `/backend/config` endpoint as
 | `VIDEO_GAMES_TWITCH_CLIENT_ID`     | The client ID issued by Twitch. **Required** to enable video games tracking. [More information](guides/video-games.md) |
 | `VIDEO_GAMES_TWITCH_CLIENT_SECRET` | The client secret issued by Twitch. **Required** to enable video games tracking.                                       |
 
-## Delete all cache
-
-Configuration is cached in database. If you need to clear it, login as the admin user and
-go to the "Miscellaneous" settings and deploy the job to "Delete all cache".
-
 ## Health endpoint
 
 The `/health` endpoint can be used for checking service healthiness. More information
diff --git a/docs/content/contributing.md b/docs/content/contributing.md
index af887975bb..13a13d6494 100644
--- a/docs/content/contributing.md
+++ b/docs/content/contributing.md
@@ -9,7 +9,6 @@
 
   ```bash title=".env"
   DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres
-  APP_COMMIT_SHA=45937eded96319941d381eef9074922702b05f73
   DEFAULT_TMDB_ACCESS_TOKEN=your-tmdb-access-token
   DEFAULT_MAL_CLIENT_ID=your-mal-client-id
   TRAKT_CLIENT_ID=your-trakt-client-id
diff --git a/docs/content/guides/authentication.md b/docs/content/guides/authentication.md
index 8a93f1ef0a..cf649daf4c 100644
--- a/docs/content/guides/authentication.md
+++ b/docs/content/guides/authentication.md
@@ -38,7 +38,6 @@ users to authenticate using OIDC.
 - Setup OpenID on your instance using the the above guide.
 - Make a backup of your database using this
   [guide](./exporting.md#exporting-the-entire-database).
-- Delete all cache using this [guide](../configuration.md#delete-all-cache).
 - Logout of your original account and then click on "Continue with OpenID Connect".
   Continue with user you want to select, after which a new account will be created.
 - Let's say that I want `IgnisDa` below to be able to login using OIDC (of
diff --git a/docs/content/guides/books.md b/docs/content/guides/books.md
index c8c55827ff..471509db2d 100644
--- a/docs/content/guides/books.md
+++ b/docs/content/guides/books.md
@@ -4,11 +4,6 @@ The default provider that Ryot uses for book tracking is
 [Openlibrary](https://openlibrary.org). You might find it lacking in some cases, so Ryot
 also supports other providers.
 
-!!! warning
-
-   Once you have followed any of the following steps, restart the server and delete the
-   application cache using [this guide](../configuration.md#delete-all-cache).
-
 ## Hardcover
 
 Ryot supports tracking books via [Hardcover](https://hardcover.app). I prefer this method
@@ -23,7 +18,7 @@ You can use the following steps to obtain your own API keys:
 3. Set the environment variable as described in the [configuration](../configuration.md)
    docs.
    ```bash
-   BOOKS_HARDCOVER_API_KEY="Bearer eyJhbGciOiJIUzI1NiIsCJ9.5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36Pw5c"
+   BOOKS_HARDCOVER_API_KEY=Bearer eyJhbGciOiJIUzI1NiIsCJ9.5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36Pw5c
    ```
 
 ## Google Books
diff --git a/docs/content/guides/video-games.md b/docs/content/guides/video-games.md
index fba4d07714..7f92446f20 100644
--- a/docs/content/guides/video-games.md
+++ b/docs/content/guides/video-games.md
@@ -22,5 +22,3 @@ You can follow the below steps to obtain your own API keys and enable video game
 6. Generate a client secret. Copy the **Client ID** and **Client Secret**.
 7. Set the `VIDEO_GAMES_*` environment variables as described in the
    [configuration](../configuration.md#important-parameters) docs.
-8. Restart the server and delete the application cache using [this
-   guide](../configuration.md#delete-all-cache).
diff --git a/docs/content/index.md b/docs/content/index.md
index a0eb5f58cd..23569d5321 100644
--- a/docs/content/index.md
+++ b/docs/content/index.md
@@ -7,25 +7,25 @@ services:
   ryot-db:
     image: postgres:16-alpine # at-least version 15 is required
     restart: unless-stopped
+    container_name: ryot-db
     volumes:
       - postgres_storage:/var/lib/postgresql/data
     environment:
-      - POSTGRES_PASSWORD=postgres
-      - POSTGRES_USER=postgres
-      - POSTGRES_DB=postgres
       - TZ=Europe/Amsterdam
-    container_name: ryot-db
+      - POSTGRES_DB=postgres
+      - POSTGRES_USER=postgres
+      - POSTGRES_PASSWORD=postgres
 
   ryot:
-    image: ignisda/ryot:v7 # or ghcr.io/ignisda/ryot:v7
+    image: ignisda/ryot:v8 # or ghcr.io/ignisda/ryot:v8
+    pull_policy: always
+    container_name: ryot
+    ports:
+      - "8000:8000"
     environment:
-      - DATABASE_URL=postgres://postgres:postgres@ryot-db:5432/postgres
       - TZ=Europe/Amsterdam
       - SERVER_ADMIN_ACCESS_TOKEN=28ebb3ae554fa9867ba0 # CHANGE THIS
-    ports:
-      - "8000:8000"
-    pull_policy: always
-    container_name: ryot
+      - DATABASE_URL=postgres://postgres:postgres@ryot-db:5432/postgres
 
 volumes:
   postgres_storage:
@@ -39,13 +39,13 @@ the features of the pro version, check the [features page]({{extra.main_website_
 To upgrade to the pro version, you need to provide a `SERVER_PRO_KEY` environment variable.
 You can get a key by purchasing it from the [website]({{extra.main_website_url}}).
 
-- Once you have the key, you can set it in the `docker-compose.yml` file:
-  ```diff
-    ryot:
-      environment:
-  +      - SERVER_PRO_KEY=<pro_key_issued_to_you>
-  ```
-- Remove cached configuration using this [guide](./configuration.md#delete-all-cache).
+Once you have the key, you can set it in the `docker-compose.yml` file:
+
+```diff
+  ryot:
+    environment:
++      - SERVER_PRO_KEY=<pro_key_issued_to_you>
+```
 
 If the key is invalid or your subscription has expired, the server will automatically start
 with the community version. Since the two versions are compatible, you can switch between
@@ -56,11 +56,11 @@ them by simply fixing the key and restarting the server.
 Each version of Ryot is released as docker images. For example, if the latest tag is
 `v5.2.1`, then the docker image will be tagged as `v5.2.1`, `v5.2`, `v5` and `latest`. The
 images will be made available on [Docker Hub](https://hub.docker.com/r/ignisda/ryot) and
-[GitHub Container Registry](https://ghcr.io/ignisda/ryot).
+[GitHub Container Registry](https://ghcr.io/ignisda/ryot). Ryot is released on a (loosely)
+weekly basis.
 
-Ryot is released on a (loosely) weekly basis. If you prefer to live on the edge, you can
-use the `develop` docker tag which is released when changes are merged into the `main`
-branch.
+If you prefer to live on the edge, you can use the `develop` docker tag which is released
+when changes are merged into the `main` branch.
 
 ## Telemetry
 
diff --git a/docs/content/integrations.md b/docs/content/integrations.md
index c4107f2a7a..afb7aaf105 100644
--- a/docs/content/integrations.md
+++ b/docs/content/integrations.md
@@ -166,11 +166,6 @@ want to sync media progress, then take a look at the [Plex Sink](#plex-sink) int
 
 ### Youtube Music
 
-!!! warn
-
-      This currently has a [bug](https://github.com/IgnisDa/ryot/issues/1186). Please do
-      not use this integration until the bug is fixed.
-
 L|Pro Version|https://ryot.io|
 
 The [Youtube Music](https://music.youtube.com) integration syncs all music that you have
diff --git a/docs/content/migration.md b/docs/content/migration.md
index 9f517d7d4f..0fc290fdbd 100644
--- a/docs/content/migration.md
+++ b/docs/content/migration.md
@@ -1,7 +1,23 @@
 # Migration
 
 All steps below are required unless otherwise stated. Please follow them in the correct
-order. Please make sure to replace `ryot` with `ryot-pro` if you are using the pro version.
+order.
+
+## From `v7.*` to `v8.*`
+
+1. Upgrade the server to `v7.16.0` to make sure all `v7` migrations are applied. For
+   example, you can make this change: `image: "ignisda/ryot:v7.16.0"` in your docker-compose
+   file.
+
+2. Create a backup of your database. [Here](./guides/exporting.md#exporting-the-entire-database)
+   is a guide on how to do this.
+
+3. Now you can upgrade to the latest version (`v8.*`). For example you can make this
+   change: `image: "ignisda/ryot:v8"` in your docker-compose file. This will
+   automatically apply all migrations required for the new version.
+
+4. **OPTIONAL**: Login as the admin user and go to the "Miscellaneous" settings page and
+   click on the button to "Perform background tasks".
 
 ## From `v6.*` to `v7.*`
 
diff --git a/docs/includes/backend-config-schema.yaml b/docs/includes/backend-config-schema.yaml
index a6f5da21a5..42c75c8ee4 100644
--- a/docs/includes/backend-config-schema.yaml
+++ b/docs/includes/backend-config-schema.yaml
@@ -109,7 +109,7 @@ frontend:
 
   # Used as the base URL when generating item links for the frontend.
   # @envvar FRONTEND_URL
-  url: "https://pro.ryot.io"
+  url: "https://app.ryot.io"
 
 # Settings related to external integrations.
 integration:
@@ -171,8 +171,8 @@ server:
   graphql_playground_enabled: true
 
   # The maximum file size in MB for user uploads.
-  # @envvar SERVER_MAX_FILE_SIZE
-  max_file_size: 70
+  # @envvar SERVER_MAX_FILE_SIZE_MB
+  max_file_size_mb: 70
 
   # The OIDC related settings.
   oidc:
diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts
index 0de5f7ca98..ffd9923528 100644
--- a/libs/generated/src/graphql/backend/gql.ts
+++ b/libs/generated/src/graphql/backend/gql.ts
@@ -14,7 +14,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
  * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
  */
 const documents = {
-    "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserMetadataRecommendations {\n  refreshUserMetadataRecommendations\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}": types.RegisterUserDocument,
+    "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}\n\nmutation MarkNotificationsAsAddressed($notificationIds: [String!]!) {\n  markNotificationsAsAddressed(notificationIds: $notificationIds)\n}": types.RegisterUserDocument,
     "query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    movieSpecifics {\n      runtime\n    }\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    bookSpecifics {\n      pages\n      isCompilation\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}": types.MetadataDetailsDocument,
     "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n      alternateNames\n    }\n  }\n}": types.PersonDetailsDocument,
     "query UserAnalytics($input: UserAnalyticsInput!) {\n  userAnalytics(input: $input) {\n    hours {\n      hour\n      entities {\n        entityId\n        entityLot\n        metadataLot\n      }\n    }\n    activities {\n      groupedBy\n      totalCount\n      totalDuration\n      items {\n        day\n        bookCount\n        showCount\n        totalCount\n        movieCount\n        musicCount\n        animeCount\n        mangaCount\n        workoutCount\n        podcastCount\n        totalDuration\n        totalBookPages\n        audioBookCount\n        videoGameCount\n        totalReviewCount\n        visualNovelCount\n        totalWorkoutReps\n        totalShowDuration\n        totalMovieDuration\n        totalMusicDuration\n        totalMetadataCount\n        totalWorkoutWeight\n        totalPodcastDuration\n        totalWorkoutDistance\n        totalWorkoutRestTime\n        totalWorkoutDuration\n        userMeasurementCount\n        totalVideoGameDuration\n        totalAudioBookDuration\n        totalPersonReviewCount\n        totalVisualNovelDuration\n        totalMetadataReviewCount\n        totalWorkoutPersonalBests\n        totalCollectionReviewCount\n        totalMetadataGroupReviewCount\n      }\n    }\n    fitness {\n      workoutReps\n      workoutCount\n      workoutWeight\n      workoutDistance\n      workoutDuration\n      workoutRestTime\n      measurementCount\n      workoutPersonalBests\n      workoutCaloriesBurnt\n      workoutExercises {\n        count\n        exercise\n      }\n      workoutMuscles {\n        count\n        muscle\n      }\n      workoutEquipments {\n        count\n        equipment\n      }\n    }\n  }\n}": types.UserAnalyticsDocument,
@@ -22,7 +22,7 @@ const documents = {
     "query UserExerciseDetails($exerciseId: String!) {\n  userExerciseDetails(exerciseId: $exerciseId) {\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    history {\n      idx\n      workoutId\n      workoutEndOn\n      bestSet {\n        ...WorkoutSetRecordPart\n      }\n    }\n    details {\n      exerciseId\n      createdOn\n      lastUpdatedOn\n      exerciseNumTimesInteracted\n      exerciseExtraInformation {\n        settings {\n          excludeFromAnalytics\n          setRestTimers {\n            ...SetRestTimersPart\n          }\n        }\n        lifetimeStats {\n          weight\n          reps\n          distance\n          duration\n          personalBestsAchieved\n        }\n        personalBests {\n          lot\n          sets {\n            setIdx\n            workoutId\n            exerciseIdx\n          }\n        }\n      }\n    }\n  }\n}": types.UserExerciseDetailsDocument,
     "query UserMeasurementsList($input: UserMeasurementsListInput!) {\n  userMeasurementsList(input: $input) {\n    timestamp\n    name\n    comment\n    stats {\n      weight\n      bodyMassIndex\n      totalBodyWater\n      muscle\n      leanBodyMass\n      bodyFat\n      boneMass\n      visceralFat\n      waistCircumference\n      waistToHeightRatio\n      hipCircumference\n      waistToHipRatio\n      chestCircumference\n      thighCircumference\n      bicepsCircumference\n      neckCircumference\n      bodyFatCaliper\n      chestSkinfold\n      abdominalSkinfold\n      thighSkinfold\n      basalMetabolicRate\n      totalDailyEnergyExpenditure\n      calories\n      custom\n    }\n  }\n}": types.UserMeasurementsListDocument,
     "query UserMetadataDetails($metadataId: String!) {\n  userMetadataDetails(metadataId: $metadataId) {\n    mediaReason\n    hasInteracted\n    averageRating\n    seenByAllCount\n    seenByUserCount\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    history {\n      ...SeenPart\n    }\n    nextEntry {\n      season\n      volume\n      episode\n      chapter\n    }\n    inProgress {\n      ...SeenPart\n    }\n    collections {\n      ...CollectionPart\n    }\n    showProgress {\n      timesSeen\n      seasonNumber\n      episodes {\n        episodeNumber\n        timesSeen\n      }\n    }\n    podcastProgress {\n      episodeNumber\n      timesSeen\n    }\n  }\n}": types.UserMetadataDetailsDocument,
-    "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      caloriesBurnt\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}": types.GetOidcRedirectUrlDocument,
+    "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations($shouldRefresh: Boolean) {\n  userMetadataRecommendations(shouldRefresh: $shouldRefresh)\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      caloriesBurnt\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}\n\nquery UserPendingNotifications {\n  userPendingNotifications {\n    id\n    message\n    createdOn\n  }\n}": types.GetOidcRedirectUrlDocument,
     "fragment SeenPodcastExtraInformationPart on SeenPodcastExtraInformation {\n  episode\n}\n\nfragment SeenShowExtraInformationPart on SeenShowExtraInformation {\n  episode\n  season\n}\n\nfragment SeenAnimeExtraInformationPart on SeenAnimeExtraInformation {\n  episode\n}\n\nfragment SeenMangaExtraInformationPart on SeenMangaExtraInformation {\n  volume\n  chapter\n}\n\nfragment CalendarEventPart on GraphqlCalendarEvent {\n  date\n  metadataId\n  metadataLot\n  episodeName\n  metadataTitle\n  metadataImage\n  calendarEventId\n  showExtraInformation {\n    ...SeenShowExtraInformationPart\n  }\n  podcastExtraInformation {\n    ...SeenPodcastExtraInformationPart\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n}\n\nfragment SeenPart on Seen {\n  id\n  state\n  progress\n  reviewId\n  startedOn\n  finishedOn\n  lastUpdatedOn\n  manualTimeSpent\n  numTimesUpdated\n  providerWatchedOn\n  showExtraInformation {\n    ...SeenShowExtraInformationPart\n  }\n  podcastExtraInformation {\n    ...SeenPodcastExtraInformationPart\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n  mangaExtraInformation {\n    ...SeenMangaExtraInformationPart\n  }\n}\n\nfragment MetadataSearchItemPart on MetadataSearchItem {\n  title\n  image\n  identifier\n  publishYear\n}\n\nfragment WorkoutOrExerciseTotalsPart on WorkoutOrExerciseTotals {\n  reps\n  weight\n  distance\n  duration\n  restTime\n  personalBestsAchieved\n}\n\nfragment EntityAssetsPart on EntityAssets {\n  images\n  videos\n}\n\nfragment WorkoutSetStatisticPart on WorkoutSetStatistic {\n  reps\n  pace\n  oneRm\n  weight\n  volume\n  duration\n  distance\n}\n\nfragment WorkoutSetRecordPart on WorkoutSetRecord {\n  lot\n  personalBests\n  statistic {\n    ...WorkoutSetStatisticPart\n  }\n}\n\nfragment WorkoutSummaryPart on WorkoutSummary {\n  total {\n    ...WorkoutOrExerciseTotalsPart\n  }\n  exercises {\n    id\n    lot\n    numSets\n    bestSet {\n      ...WorkoutSetRecordPart\n    }\n  }\n  focused {\n    lots {\n      lot\n      exercises\n    }\n    levels {\n      level\n      exercises\n    }\n    forces {\n      force\n      exercises\n    }\n    muscles {\n      muscle\n      exercises\n    }\n    equipments {\n      equipment\n      exercises\n    }\n  }\n}\n\nfragment CollectionPart on Collection {\n  id\n  name\n  userId\n}\n\nfragment ReviewItemPart on ReviewItem {\n  id\n  rating\n  postedOn\n  isSpoiler\n  visibility\n  textOriginal\n  textRendered\n  seenItemsAssociatedWith\n  postedBy {\n    id\n    name\n  }\n  comments {\n    id\n    text\n    likedBy\n    createdOn\n    user {\n      id\n      name\n    }\n  }\n  showExtraInformation {\n    season\n    episode\n  }\n  podcastExtraInformation {\n    episode\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n  mangaExtraInformation {\n    ...SeenMangaExtraInformationPart\n  }\n}\n\nfragment WorkoutInformationPart on WorkoutInformation {\n  comment\n  assets {\n    ...EntityAssetsPart\n  }\n  supersets {\n    color\n    exercises\n  }\n  exercises {\n    id\n    lot\n    notes\n    total {\n      ...WorkoutOrExerciseTotalsPart\n    }\n    assets {\n      ...EntityAssetsPart\n    }\n    sets {\n      lot\n      rpe\n      note\n      restTime\n      confirmedAt\n      personalBests\n      statistic {\n        ...WorkoutSetStatisticPart\n      }\n    }\n  }\n}\n\nfragment SetRestTimersPart on SetRestTimersSettings {\n  drop\n  warmup\n  normal\n  failure\n}\n\nfragment PersonDetailsGroupedByRolePart on PersonDetailsGroupedByRole {\n  name\n  count\n  items {\n    entityId\n    character\n  }\n}": types.SeenPodcastExtraInformationPartFragmentDoc,
 };
 
@@ -43,7 +43,7 @@ export function graphql(source: string): unknown;
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserMetadataRecommendations {\n  refreshUserMetadataRecommendations\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}"): (typeof documents)["mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserMetadataRecommendations {\n  refreshUserMetadataRecommendations\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}"];
+export function graphql(source: "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}\n\nmutation MarkNotificationsAsAddressed($notificationIds: [String!]!) {\n  markNotificationsAsAddressed(notificationIds: $notificationIds)\n}"): (typeof documents)["mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}\n\nmutation MarkNotificationsAsAddressed($notificationIds: [String!]!) {\n  markNotificationsAsAddressed(notificationIds: $notificationIds)\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
@@ -75,7 +75,7 @@ export function graphql(source: "query UserMetadataDetails($metadataId: String!)
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      caloriesBurnt\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"): (typeof documents)["query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      caloriesBurnt\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"];
+export function graphql(source: "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations($shouldRefresh: Boolean) {\n  userMetadataRecommendations(shouldRefresh: $shouldRefresh)\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      caloriesBurnt\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}\n\nquery UserPendingNotifications {\n  userPendingNotifications {\n    id\n    message\n    createdOn\n  }\n}"): (typeof documents)["query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations($shouldRefresh: Boolean) {\n  userMetadataRecommendations(shouldRefresh: $shouldRefresh)\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      caloriesBurnt\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}\n\nquery UserPendingNotifications {\n  userPendingNotifications {\n    id\n    message\n    createdOn\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts
index 7186813603..4906f9cb2f 100644
--- a/libs/generated/src/graphql/backend/graphql.ts
+++ b/libs/generated/src/graphql/backend/graphql.ts
@@ -972,6 +972,7 @@ export type MediaCollectionContentsResults = {
 
 export type MediaFilter = {
   collections?: InputMaybe<Array<Scalars['String']['input']>>;
+  dateRange?: InputMaybe<ApplicationDateRangeInput>;
   general?: InputMaybe<MediaGeneralFilter>;
 };
 
@@ -1239,6 +1240,8 @@ export type MutationRoot = {
   loginUser: LoginResult;
   /** Mark an entity as partial. */
   markEntityAsPartial: Scalars['Boolean']['output'];
+  /** Mark user notifications as addressed. */
+  markNotificationsAsAddressed: Scalars['Boolean']['output'];
   /** Merge an exercise into another. */
   mergeExercise: Scalars['Boolean']['output'];
   /**
@@ -1250,8 +1253,6 @@ export type MutationRoot = {
   presignedPutS3Url: PresignedPutUrlResponse;
   /** Get an access token using an access link. */
   processAccessLink: ProcessAccessLinkResult;
-  /** Refresh the user metadata recommendations. */
-  refreshUserMetadataRecommendations: Scalars['Boolean']['output'];
   /**
    * Create a new user for the service. Also set their `lot` as admin if
    * they are the first user.
@@ -1454,6 +1455,11 @@ export type MutationRootMarkEntityAsPartialArgs = {
 };
 
 
+export type MutationRootMarkNotificationsAsAddressedArgs = {
+  notificationIds: Array<Scalars['String']['input']>;
+};
+
+
 export type MutationRootMergeExerciseArgs = {
   mergeFrom: Scalars['String']['input'];
   mergeInto: Scalars['String']['input'];
@@ -1809,6 +1815,8 @@ export type QueryRoot = {
   userMetadataRecommendations: Array<Scalars['String']['output']>;
   /** Get all the notification platforms for the currently logged in user. */
   userNotificationPlatforms: Array<NotificationPlatform>;
+  /** Get all pending display notifications for the currently logged in user. */
+  userPendingNotifications: Array<UserNotification>;
   /** Get details that can be displayed to a user for a creator. */
   userPersonDetails: UserPersonDetails;
   /** Get upcoming calendar events for the given filter. */
@@ -1951,6 +1959,11 @@ export type QueryRootUserMetadataGroupDetailsArgs = {
 };
 
 
+export type QueryRootUserMetadataRecommendationsArgs = {
+  shouldRefresh?: InputMaybe<Scalars['Boolean']['input']>;
+};
+
+
 export type QueryRootUserPersonDetailsArgs = {
   personId: Scalars['String']['input'];
 };
@@ -2664,6 +2677,12 @@ export type UserMetadataGroupDetails = {
   reviews: Array<ReviewItem>;
 };
 
+export type UserNotification = {
+  createdOn: Scalars['DateTime']['output'];
+  id: Scalars['String']['output'];
+  message: Scalars['String']['output'];
+};
+
 export enum UserNotificationContent {
   MetadataChaptersOrEpisodesChanged = 'METADATA_CHAPTERS_OR_EPISODES_CHANGED',
   MetadataEpisodeImagesChanged = 'METADATA_EPISODE_IMAGES_CHANGED',
@@ -2674,6 +2693,7 @@ export enum UserNotificationContent {
   MetadataReleaseDateChanged = 'METADATA_RELEASE_DATE_CHANGED',
   MetadataStatusChanged = 'METADATA_STATUS_CHANGED',
   NewWorkoutCreated = 'NEW_WORKOUT_CREATED',
+  OutdatedSeenEntries = 'OUTDATED_SEEN_ENTRIES',
   PersonMetadataAssociated = 'PERSON_METADATA_ASSOCIATED',
   PersonMetadataGroupAssociated = 'PERSON_METADATA_GROUP_ASSOCIATED',
   ReviewPosted = 'REVIEW_POSTED'
@@ -3367,11 +3387,6 @@ export type MergeExerciseMutationVariables = Exact<{
 
 export type MergeExerciseMutation = { mergeExercise: boolean };
 
-export type RefreshUserMetadataRecommendationsMutationVariables = Exact<{ [key: string]: never; }>;
-
-
-export type RefreshUserMetadataRecommendationsMutation = { refreshUserMetadataRecommendations: boolean };
-
 export type MarkEntityAsPartialMutationVariables = Exact<{
   input: MarkEntityAsPartialInput;
 }>;
@@ -3379,6 +3394,13 @@ export type MarkEntityAsPartialMutationVariables = Exact<{
 
 export type MarkEntityAsPartialMutation = { markEntityAsPartial: boolean };
 
+export type MarkNotificationsAsAddressedMutationVariables = Exact<{
+  notificationIds: Array<Scalars['String']['input']> | Scalars['String']['input'];
+}>;
+
+
+export type MarkNotificationsAsAddressedMutation = { markNotificationsAsAddressed: boolean };
+
 export type MetadataDetailsQueryVariables = Exact<{
   metadataId: Scalars['String']['input'];
 }>;
@@ -3481,7 +3503,9 @@ export type UsersListQueryVariables = Exact<{
 
 export type UsersListQuery = { usersList: Array<{ id: string, lot: UserLot, name: string, isDisabled?: boolean | null }> };
 
-export type UserMetadataRecommendationsQueryVariables = Exact<{ [key: string]: never; }>;
+export type UserMetadataRecommendationsQueryVariables = Exact<{
+  shouldRefresh?: InputMaybe<Scalars['Boolean']['input']>;
+}>;
 
 
 export type UserMetadataRecommendationsQuery = { userMetadataRecommendations: Array<string> };
@@ -3653,6 +3677,11 @@ export type UserAnalyticsParametersQueryVariables = Exact<{ [key: string]: never
 
 export type UserAnalyticsParametersQuery = { userAnalyticsParameters: { endDate?: string | null, startDate?: string | null } };
 
+export type UserPendingNotificationsQueryVariables = Exact<{ [key: string]: never; }>;
+
+
+export type UserPendingNotificationsQuery = { userPendingNotifications: Array<{ id: string, message: string, createdOn: string }> };
+
 export type SeenPodcastExtraInformationPartFragment = { episode: number };
 
 export type SeenShowExtraInformationPartFragment = { episode: number, season: number };
@@ -3756,8 +3785,8 @@ export const ProcessAccessLinkDocument = {"kind":"Document","definitions":[{"kin
 export const RevokeAccessLinkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeAccessLink"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"accessLinkId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"revokeAccessLink"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"accessLinkId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"accessLinkId"}}}]}]}}]} as unknown as DocumentNode<RevokeAccessLinkMutation, RevokeAccessLinkMutationVariables>;
 export const UpdateUserExerciseSettingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUserExerciseSettings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateUserExerciseSettings"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateUserExerciseSettings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<UpdateUserExerciseSettingsMutation, UpdateUserExerciseSettingsMutationVariables>;
 export const MergeExerciseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MergeExercise"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mergeFrom"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mergeInto"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mergeExercise"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"mergeFrom"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mergeFrom"}}},{"kind":"Argument","name":{"kind":"Name","value":"mergeInto"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mergeInto"}}}]}]}}]} as unknown as DocumentNode<MergeExerciseMutation, MergeExerciseMutationVariables>;
-export const RefreshUserMetadataRecommendationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RefreshUserMetadataRecommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"refreshUserMetadataRecommendations"}}]}}]} as unknown as DocumentNode<RefreshUserMetadataRecommendationsMutation, RefreshUserMetadataRecommendationsMutationVariables>;
 export const MarkEntityAsPartialDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkEntityAsPartial"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MarkEntityAsPartialInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markEntityAsPartial"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<MarkEntityAsPartialMutation, MarkEntityAsPartialMutationVariables>;
+export const MarkNotificationsAsAddressedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkNotificationsAsAddressed"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"notificationIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markNotificationsAsAddressed"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"notificationIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"notificationIds"}}}]}]}}]} as unknown as DocumentNode<MarkNotificationsAsAddressedMutation, MarkNotificationsAsAddressedMutationVariables>;
 export const MetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"isNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"suggestions"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"providerRating"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserId"}},{"kind":"Field","name":{"kind":"Name","value":"productionStatus"}},{"kind":"Field","name":{"kind":"Name","value":"originalLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"animeSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movieSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"genres"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"part"}}]}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"languages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"bookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}},{"kind":"Field","name":{"kind":"Name","value":"isCompilation"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volumes"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"videoId"}},{"kind":"Field","name":{"kind":"Name","value":"source"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"creators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"seasons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"backdropImages"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"length"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGameSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"platforms"}}]}},{"kind":"Field","name":{"kind":"Name","value":"musicSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"viewCount"}},{"kind":"Field","name":{"kind":"Name","value":"byVariousArtists"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataDetailsQuery, MetadataDetailsQueryVariables>;
 export const PersonDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PersonDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"personId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"personId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"personId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"associatedMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"associatedMetadataGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"place"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"website"}},{"kind":"Field","name":{"kind":"Name","value":"deathDate"}},{"kind":"Field","name":{"kind":"Name","value":"birthDate"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}},{"kind":"Field","name":{"kind":"Name","value":"alternateNames"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PersonDetailsGroupedByRole"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}}]} as unknown as DocumentNode<PersonDetailsQuery, PersonDetailsQueryVariables>;
 export const UserAnalyticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserAnalytics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserAnalyticsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userAnalytics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hours"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hour"}},{"kind":"Field","name":{"kind":"Name","value":"entities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"entityLot"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLot"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"activities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"groupedBy"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"day"}},{"kind":"Field","name":{"kind":"Name","value":"bookCount"}},{"kind":"Field","name":{"kind":"Name","value":"showCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"movieCount"}},{"kind":"Field","name":{"kind":"Name","value":"musicCount"}},{"kind":"Field","name":{"kind":"Name","value":"animeCount"}},{"kind":"Field","name":{"kind":"Name","value":"mangaCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"podcastCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalBookPages"}},{"kind":"Field","name":{"kind":"Name","value":"audioBookCount"}},{"kind":"Field","name":{"kind":"Name","value":"videoGameCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"totalShowDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMovieDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMusicDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"totalPodcastDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"userMeasurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVideoGameDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalAudioBookDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalPersonReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVisualNovelDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"totalCollectionReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataGroupReviewCount"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"workoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"workoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"workoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"measurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCaloriesBurnt"}},{"kind":"Field","name":{"kind":"Name","value":"workoutExercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"exercise"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workoutMuscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workoutEquipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserAnalyticsQuery, UserAnalyticsQueryVariables>;
@@ -3774,7 +3803,7 @@ export const UserCollectionsListDocument = {"kind":"Document","definitions":[{"k
 export const UserIntegrationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserIntegrations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userIntegrations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"provider"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"isDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"maximumProgress"}},{"kind":"Field","name":{"kind":"Name","value":"minimumProgress"}},{"kind":"Field","name":{"kind":"Name","value":"lastTriggeredOn"}},{"kind":"Field","name":{"kind":"Name","value":"syncToOwnedCollection"}}]}}]}}]} as unknown as DocumentNode<UserIntegrationsQuery, UserIntegrationsQueryVariables>;
 export const UserNotificationPlatformsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserNotificationPlatforms"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userNotificationPlatforms"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"isDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]} as unknown as DocumentNode<UserNotificationPlatformsQuery, UserNotificationPlatformsQueryVariables>;
 export const UsersListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UsersList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"usersList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isDisabled"}}]}}]}}]} as unknown as DocumentNode<UsersListQuery, UsersListQueryVariables>;
-export const UserMetadataRecommendationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataRecommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userMetadataRecommendations"}}]}}]} as unknown as DocumentNode<UserMetadataRecommendationsQuery, UserMetadataRecommendationsQueryVariables>;
+export const UserMetadataRecommendationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataRecommendations"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"shouldRefresh"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userMetadataRecommendations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"shouldRefresh"},"value":{"kind":"Variable","name":{"kind":"Name","value":"shouldRefresh"}}}]}]}}]} as unknown as DocumentNode<UserMetadataRecommendationsQuery, UserMetadataRecommendationsQueryVariables>;
 export const UserUpcomingCalendarEventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserUpcomingCalendarEvents"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserUpcomingCalendarEventInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userUpcomingCalendarEvents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalendarEventPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenShowExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenShowExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenPodcastExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalendarEventPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GraphqlCalendarEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"metadataId"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLot"}},{"kind":"Field","name":{"kind":"Name","value":"episodeName"}},{"kind":"Field","name":{"kind":"Name","value":"metadataTitle"}},{"kind":"Field","name":{"kind":"Name","value":"metadataImage"}},{"kind":"Field","name":{"kind":"Name","value":"calendarEventId"}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}}]}}]} as unknown as DocumentNode<UserUpcomingCalendarEventsQuery, UserUpcomingCalendarEventsQueryVariables>;
 export const UserCalendarEventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserCalendarEvents"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserCalendarEventInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userCalendarEvents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalendarEventPart"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenShowExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenShowExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenPodcastExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalendarEventPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GraphqlCalendarEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"metadataId"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLot"}},{"kind":"Field","name":{"kind":"Name","value":"episodeName"}},{"kind":"Field","name":{"kind":"Name","value":"metadataTitle"}},{"kind":"Field","name":{"kind":"Name","value":"metadataImage"}},{"kind":"Field","name":{"kind":"Name","value":"calendarEventId"}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}}]}}]} as unknown as DocumentNode<UserCalendarEventsQuery, UserCalendarEventsQueryVariables>;
 export const MetadataPartialDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataPartialDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataPartialDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]} as unknown as DocumentNode<MetadataPartialDetailsQuery, MetadataPartialDetailsQueryVariables>;
@@ -3799,4 +3828,5 @@ export const UserWorkoutDetailsDocument = {"kind":"Document","definitions":[{"ki
 export const UserWorkoutsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutsList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutsList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"}}]}}]}}]} as unknown as DocumentNode<UserWorkoutsListQuery, UserWorkoutsListQueryVariables>;
 export const UserWorkoutTemplateDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutTemplateDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workoutTemplateId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutTemplateDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workoutTemplateId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workoutTemplateId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"information"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutInformationPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetStatisticPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetStatistic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"pace"}},{"kind":"Field","name":{"kind":"Name","value":"oneRm"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetRecordPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"}},{"kind":"Field","name":{"kind":"Name","value":"statistic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetStatisticPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CollectionPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"rpe"}},{"kind":"Field","name":{"kind":"Name","value":"note"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"}},{"kind":"Field","name":{"kind":"Name","value":"statistic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetStatisticPart"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutTemplateDetailsQuery, UserWorkoutTemplateDetailsQueryVariables>;
 export const UserWorkoutTemplatesListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutTemplatesList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutTemplatesList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"}}]}}]}}]} as unknown as DocumentNode<UserWorkoutTemplatesListQuery, UserWorkoutTemplatesListQueryVariables>;
-export const UserAnalyticsParametersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserAnalyticsParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userAnalyticsParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"endDate"}},{"kind":"Field","name":{"kind":"Name","value":"startDate"}}]}}]}}]} as unknown as DocumentNode<UserAnalyticsParametersQuery, UserAnalyticsParametersQueryVariables>;
\ No newline at end of file
+export const UserAnalyticsParametersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserAnalyticsParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userAnalyticsParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"endDate"}},{"kind":"Field","name":{"kind":"Name","value":"startDate"}}]}}]}}]} as unknown as DocumentNode<UserAnalyticsParametersQuery, UserAnalyticsParametersQueryVariables>;
+export const UserPendingNotificationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPendingNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userPendingNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]} as unknown as DocumentNode<UserPendingNotificationsQuery, UserPendingNotificationsQueryVariables>;
\ No newline at end of file
diff --git a/libs/generated/src/graphql/backend/types.generated.ts b/libs/generated/src/graphql/backend/types.generated.ts
index 9d9c0f6b2c..041678c78e 100644
--- a/libs/generated/src/graphql/backend/types.generated.ts
+++ b/libs/generated/src/graphql/backend/types.generated.ts
@@ -997,6 +997,7 @@ export type MediaCollectionContentsResults = {
 
 export type MediaFilter = {
   collections?: InputMaybe<Array<Scalars['String']['input']>>;
+  dateRange?: InputMaybe<ApplicationDateRangeInput>;
   general?: InputMaybe<MediaGeneralFilter>;
 };
 
@@ -1278,6 +1279,8 @@ export type MutationRoot = {
   loginUser: LoginResult;
   /** Mark an entity as partial. */
   markEntityAsPartial: Scalars['Boolean']['output'];
+  /** Mark user notifications as addressed. */
+  markNotificationsAsAddressed: Scalars['Boolean']['output'];
   /** Merge an exercise into another. */
   mergeExercise: Scalars['Boolean']['output'];
   /**
@@ -1289,8 +1292,6 @@ export type MutationRoot = {
   presignedPutS3Url: PresignedPutUrlResponse;
   /** Get an access token using an access link. */
   processAccessLink: ProcessAccessLinkResult;
-  /** Refresh the user metadata recommendations. */
-  refreshUserMetadataRecommendations: Scalars['Boolean']['output'];
   /**
    * Create a new user for the service. Also set their `lot` as admin if
    * they are the first user.
@@ -1493,6 +1494,11 @@ export type MutationRootMarkEntityAsPartialArgs = {
 };
 
 
+export type MutationRootMarkNotificationsAsAddressedArgs = {
+  notificationIds: Array<Scalars['String']['input']>;
+};
+
+
 export type MutationRootMergeExerciseArgs = {
   mergeFrom: Scalars['String']['input'];
   mergeInto: Scalars['String']['input'];
@@ -1863,6 +1869,8 @@ export type QueryRoot = {
   userMetadataRecommendations: Array<Scalars['String']['output']>;
   /** Get all the notification platforms for the currently logged in user. */
   userNotificationPlatforms: Array<NotificationPlatform>;
+  /** Get all pending display notifications for the currently logged in user. */
+  userPendingNotifications: Array<UserNotification>;
   /** Get details that can be displayed to a user for a creator. */
   userPersonDetails: UserPersonDetails;
   /** Get upcoming calendar events for the given filter. */
@@ -2005,6 +2013,11 @@ export type QueryRootUserMetadataGroupDetailsArgs = {
 };
 
 
+export type QueryRootUserMetadataRecommendationsArgs = {
+  shouldRefresh?: InputMaybe<Scalars['Boolean']['input']>;
+};
+
+
 export type QueryRootUserPersonDetailsArgs = {
   personId: Scalars['String']['input'];
 };
@@ -2758,6 +2771,13 @@ export type UserMetadataGroupDetails = {
   reviews: Array<ReviewItem>;
 };
 
+export type UserNotification = {
+  __typename?: 'UserNotification';
+  createdOn: Scalars['DateTime']['output'];
+  id: Scalars['String']['output'];
+  message: Scalars['String']['output'];
+};
+
 export enum UserNotificationContent {
   MetadataChaptersOrEpisodesChanged = 'METADATA_CHAPTERS_OR_EPISODES_CHANGED',
   MetadataEpisodeImagesChanged = 'METADATA_EPISODE_IMAGES_CHANGED',
@@ -2768,6 +2788,7 @@ export enum UserNotificationContent {
   MetadataReleaseDateChanged = 'METADATA_RELEASE_DATE_CHANGED',
   MetadataStatusChanged = 'METADATA_STATUS_CHANGED',
   NewWorkoutCreated = 'NEW_WORKOUT_CREATED',
+  OutdatedSeenEntries = 'OUTDATED_SEEN_ENTRIES',
   PersonMetadataAssociated = 'PERSON_METADATA_ASSOCIATED',
   PersonMetadataGroupAssociated = 'PERSON_METADATA_GROUP_ASSOCIATED',
   ReviewPosted = 'REVIEW_POSTED'
diff --git a/libs/graphql/src/backend/mutations/combined.gql b/libs/graphql/src/backend/mutations/combined.gql
index d5a7762f37..3219017531 100644
--- a/libs/graphql/src/backend/mutations/combined.gql
+++ b/libs/graphql/src/backend/mutations/combined.gql
@@ -263,10 +263,10 @@ mutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {
 	mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)
 }
 
-mutation RefreshUserMetadataRecommendations {
-	refreshUserMetadataRecommendations
-}
-
 mutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {
 	markEntityAsPartial(input: $input)
 }
+
+mutation MarkNotificationsAsAddressed($notificationIds: [String!]!) {
+	markNotificationsAsAddressed(notificationIds: $notificationIds)
+}
diff --git a/libs/graphql/src/backend/queries/combined.gql b/libs/graphql/src/backend/queries/combined.gql
index d4ff9008a3..8b49868356 100644
--- a/libs/graphql/src/backend/queries/combined.gql
+++ b/libs/graphql/src/backend/queries/combined.gql
@@ -85,8 +85,8 @@ query UsersList($query: String) {
 	}
 }
 
-query UserMetadataRecommendations {
-	userMetadataRecommendations
+query UserMetadataRecommendations($shouldRefresh: Boolean) {
+	userMetadataRecommendations(shouldRefresh: $shouldRefresh)
 }
 
 query UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {
@@ -478,3 +478,11 @@ query UserAnalyticsParameters {
 		startDate
 	}
 }
+
+query UserPendingNotifications {
+	userPendingNotifications {
+		id
+		message
+		createdOn
+	}
+}
diff --git a/libs/transactional/package.json b/libs/transactional/package.json
index fe1c29996f..459b52b474 100644
--- a/libs/transactional/package.json
+++ b/libs/transactional/package.json
@@ -2,11 +2,11 @@
 	"name": "@ryot/transactional",
 	"private": true,
 	"dependencies": {
-		"@react-email/components": "0.0.31",
+		"@react-email/components": "0.0.32",
 		"@ryot/ts-utils": "workspace:*",
 		"react": "18.3.1",
 		"react-dom": "18.3.1",
-		"react-email": "3.0.4"
+		"react-email": "3.0.6"
 	},
 	"devDependencies": {
 		"@types/react": "18.3.18",
diff --git a/libs/ts-utils/package.json b/libs/ts-utils/package.json
index 35a18fdcc9..787dd699e1 100644
--- a/libs/ts-utils/package.json
+++ b/libs/ts-utils/package.json
@@ -13,6 +13,6 @@
 		"zod": "3.24.1"
 	},
 	"devDependencies": {
-		"@types/lodash": "4.17.13"
+		"@types/lodash": "4.17.14"
 	}
 }
diff --git a/package.json b/package.json
index 3ae78c3a23..db571114af 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
 	"devDependencies": {
 		"@biomejs/biome": "1.9.4",
 		"tsconfig-moon": "1.3.0",
-		"typescript": "5.7.2"
+		"typescript": "5.7.3"
 	},
 	"resolutions": {
 		"@types/react": "18.3.4"
diff --git a/yarn.lock b/yarn.lock
index b6c66759e3..db5a5cde6f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3946,134 +3946,134 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@mantine/carousel@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/carousel@npm:7.15.2"
+"@mantine/carousel@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/carousel@npm:7.16.1"
   peerDependencies:
-    "@mantine/core": 7.15.2
-    "@mantine/hooks": 7.15.2
+    "@mantine/core": 7.16.1
+    "@mantine/hooks": 7.16.1
     embla-carousel-react: ">=7.0.0"
     react: ^18.x || ^19.x
     react-dom: ^18.x || ^19.x
-  checksum: 10/77f6378a94e3dcdef79f81c22cc99d7e1bfc1129dc690e635d73b864c9a38a8caf3b01e81a9c5e5ccaa589ee48b3019bfbcc0eb9ada5744575b1fc765792e245
+  checksum: 10/7e5fe116b8280890c78ed006fb71ff72a1d6cdd21923cafdfe58cf90a4192c2312e7dba2351679af4313121bd08956c83706dcf52888b728d90f1d3dbe98c531
   languageName: node
   linkType: hard
 
-"@mantine/charts@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/charts@npm:7.15.2"
+"@mantine/charts@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/charts@npm:7.16.1"
   peerDependencies:
-    "@mantine/core": 7.15.2
-    "@mantine/hooks": 7.15.2
+    "@mantine/core": 7.16.1
+    "@mantine/hooks": 7.16.1
     react: ^18.x || ^19.x
     react-dom: ^18.x || ^19.x
     recharts: ^2.13.3
-  checksum: 10/8332549a616a15b067223445aea7d010e3a659cc6711e449391387707001dac7109a24d0f8df2d2c734b2d22d2113550e2c813b93173576857a37842254cdf7c
+  checksum: 10/ad29ed762c2ddbdc222e713ca4fbbce7770ee5be739391b2f20c88cbfbc50b17044e6f999318d977db8c4cde9ee456b8480532852c0b789e8cf2c14cf8336524
   languageName: node
   linkType: hard
 
-"@mantine/code-highlight@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/code-highlight@npm:7.15.2"
+"@mantine/code-highlight@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/code-highlight@npm:7.16.1"
   dependencies:
     clsx: "npm:^2.1.1"
     highlight.js: "npm:^11.10.0"
   peerDependencies:
-    "@mantine/core": 7.15.2
-    "@mantine/hooks": 7.15.2
+    "@mantine/core": 7.16.1
+    "@mantine/hooks": 7.16.1
     react: ^18.x || ^19.x
     react-dom: ^18.x || ^19.x
-  checksum: 10/4aac43c7f0e660b268f989bd135ef9c48b55e66df8f1e08d14f8d6bf6980fe8f5216ecf4170048aa628f00790c3ecaedf2097e6304d8200adfe51204fe7afc49
+  checksum: 10/6215cf3085521e71ecb23ae6a33601722982b0e11138fc3cd26a34473bbd6cb857b5e527abd4ff7e9fe35e8b13d2e18673720dcf62eeaa3985fb5b16a5a1b9e5
   languageName: node
   linkType: hard
 
-"@mantine/core@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/core@npm:7.15.2"
+"@mantine/core@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/core@npm:7.16.1"
   dependencies:
     "@floating-ui/react": "npm:^0.26.28"
     clsx: "npm:^2.1.1"
-    react-number-format: "npm:^5.4.2"
-    react-remove-scroll: "npm:^2.6.0"
+    react-number-format: "npm:^5.4.3"
+    react-remove-scroll: "npm:^2.6.2"
     react-textarea-autosize: "npm:8.5.6"
     type-fest: "npm:^4.27.0"
   peerDependencies:
-    "@mantine/hooks": 7.15.2
+    "@mantine/hooks": 7.16.1
     react: ^18.x || ^19.x
     react-dom: ^18.x || ^19.x
-  checksum: 10/21a9337235d78910af65fb48527abf6ffff6886e8832dab11c805515e2f657eecbfe9127414680bdf4d7167520bbd9ee32685d78d22fd5efe339ddb1482e6765
+  checksum: 10/930d829a251693b4aa8cd43570aa5e99f3482819a65c8d212e9e93fcb4fb7b9f4cbb70fa3223f9a65333ba64a0eb00a80cd09849a8479c81cfd42398349f4ad3
   languageName: node
   linkType: hard
 
-"@mantine/dates@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/dates@npm:7.15.2"
+"@mantine/dates@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/dates@npm:7.16.1"
   dependencies:
     clsx: "npm:^2.1.1"
   peerDependencies:
-    "@mantine/core": 7.15.2
-    "@mantine/hooks": 7.15.2
+    "@mantine/core": 7.16.1
+    "@mantine/hooks": 7.16.1
     dayjs: ">=1.0.0"
     react: ^18.x || ^19.x
     react-dom: ^18.x || ^19.x
-  checksum: 10/5a249b313349d03a2c5ca0c94ab9e0296458b750a1b6e238e86ce8d900f138cdaf2e0a35af88b01ff518095abdbb21591970cc457c9642e4fdab3e8d8decaefb
+  checksum: 10/f7c31b9336fd3a86afe618f2ce8553e3e8aadbbff1f66b4d5cd8bd62f462b916238256dd7f4019f13ce3a527135f45389d65145d4e4e3d99db0edb821cf26b6f
   languageName: node
   linkType: hard
 
-"@mantine/form@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/form@npm:7.15.2"
+"@mantine/form@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/form@npm:7.16.1"
   dependencies:
     fast-deep-equal: "npm:^3.1.3"
     klona: "npm:^2.0.6"
   peerDependencies:
     react: ^18.x || ^19.x
-  checksum: 10/a492e6cbeee3658f4897f25a38bc6d279dd6bed4173d27124c688f24d0c9cb63e657b2b3c94883136db1ad52187d7864361684f8e8e529128399c6724a8f30c5
+  checksum: 10/c6cd25ea36ccb59f1f2413e6332bae7cf1df5cf4544b3c55352520f3c9b1403d89210c9adcd15dc0d36e976d486ca8228a3c98495ace5017ab08f76e59f4fbe8
   languageName: node
   linkType: hard
 
-"@mantine/hooks@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/hooks@npm:7.15.2"
+"@mantine/hooks@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/hooks@npm:7.16.1"
   peerDependencies:
     react: ^18.x || ^19.x
-  checksum: 10/abccce939ac05f1e77e1f2fde33de1c80cf13dbc5693c5d78420178d09671b1e708f3f355346f261c654726a212f3755a17ad6d1e1493d215112117eed28b673
+  checksum: 10/2ceefb019089a7d2e801f8ea335f7756aa0ac706eafe1086fa09513eb6d586f4ef2a6d2ba745c1a790e577cb57c88b8befcc3c47dd684b97b520a92992ae6c6b
   languageName: node
   linkType: hard
 
-"@mantine/modals@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/modals@npm:7.15.2"
+"@mantine/modals@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/modals@npm:7.16.1"
   peerDependencies:
-    "@mantine/core": 7.15.2
-    "@mantine/hooks": 7.15.2
+    "@mantine/core": 7.16.1
+    "@mantine/hooks": 7.16.1
     react: ^18.x || ^19.x
     react-dom: ^18.x || ^19.x
-  checksum: 10/45ae58861b8168a5810b5478b9e495efc286c39f4fa8daea65016ec986abda805d5b0880a853096b86d44dec88e0ccce21d667ab58872e0948df61917fa4ec1e
+  checksum: 10/14f7ac654f5070bdca917512ea0ee72729a2a2b3ff1caf8cdca2d7f9a97d8229df4fc16330ab862a99df3039d5199af8741b4e628f58a4bc60cc2852d64e33fc
   languageName: node
   linkType: hard
 
-"@mantine/notifications@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/notifications@npm:7.15.2"
+"@mantine/notifications@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/notifications@npm:7.16.1"
   dependencies:
-    "@mantine/store": "npm:7.15.2"
+    "@mantine/store": "npm:7.16.1"
     react-transition-group: "npm:4.4.5"
   peerDependencies:
-    "@mantine/core": 7.15.2
-    "@mantine/hooks": 7.15.2
+    "@mantine/core": 7.16.1
+    "@mantine/hooks": 7.16.1
     react: ^18.x || ^19.x
     react-dom: ^18.x || ^19.x
-  checksum: 10/de04f674a5f6a40e29946dd831a7ecf2e202c938f2912f250f0dd308acb91fb347cf30b4028413f697bb87cdefa97c48cce49425d5b0f4068f6134902563998a
+  checksum: 10/3114c30205a486b5e6289f91869cb397f06c0fd19c2e2e7036d094be621bdd73a247ccdb1a4366023d26e46a16c6d3b82e2dfa6c43247cfef61e2dd132937349
   languageName: node
   linkType: hard
 
-"@mantine/store@npm:7.15.2":
-  version: 7.15.2
-  resolution: "@mantine/store@npm:7.15.2"
+"@mantine/store@npm:7.16.1":
+  version: 7.16.1
+  resolution: "@mantine/store@npm:7.16.1"
   peerDependencies:
     react: ^18.x || ^19.x
-  checksum: 10/03ed5443251226a7664fd9164258471012beff86672bce8e2c5ab531d473db21962cf1bf3f34cf77985c89f01f8eefd65d0defaef3142cb6d7fd2342a3a2ca21
+  checksum: 10/e11bbdb45b3a407b61284c88defe4f689c1ea3c6b4791cd065fb38db26a71862e3a646c6a029ad9d4e26b878e2300a08a7696ac507ef8a8483e4604dd7898ca0
   languageName: node
   linkType: hard
 
@@ -4102,65 +4102,65 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@next/env@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/env@npm:15.0.4"
-  checksum: 10/c5a3a7695c7ec99f53520e1174f57eb04adc5e1eb41451db8eb8d4a576e64f7f46d0c5360593f83fd0e671af8f4fdb7235090315d7dbbd1e18a1c147aec728b5
+"@next/env@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/env@npm:15.1.2"
+  checksum: 10/eff61ed96b0d55e5feaef36c0d5b3f81dc94e9526fe18d5bc2f94fc8e6b5215dfa60df3f5b9feafee003aaf5150a0f05fab7985a345a476bb636e5a98184a0b9
   languageName: node
   linkType: hard
 
-"@next/swc-darwin-arm64@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-darwin-arm64@npm:15.0.4"
+"@next/swc-darwin-arm64@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-darwin-arm64@npm:15.1.2"
   conditions: os=darwin & cpu=arm64
   languageName: node
   linkType: hard
 
-"@next/swc-darwin-x64@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-darwin-x64@npm:15.0.4"
+"@next/swc-darwin-x64@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-darwin-x64@npm:15.1.2"
   conditions: os=darwin & cpu=x64
   languageName: node
   linkType: hard
 
-"@next/swc-linux-arm64-gnu@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-linux-arm64-gnu@npm:15.0.4"
+"@next/swc-linux-arm64-gnu@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-linux-arm64-gnu@npm:15.1.2"
   conditions: os=linux & cpu=arm64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@next/swc-linux-arm64-musl@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-linux-arm64-musl@npm:15.0.4"
+"@next/swc-linux-arm64-musl@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-linux-arm64-musl@npm:15.1.2"
   conditions: os=linux & cpu=arm64 & libc=musl
   languageName: node
   linkType: hard
 
-"@next/swc-linux-x64-gnu@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-linux-x64-gnu@npm:15.0.4"
+"@next/swc-linux-x64-gnu@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-linux-x64-gnu@npm:15.1.2"
   conditions: os=linux & cpu=x64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@next/swc-linux-x64-musl@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-linux-x64-musl@npm:15.0.4"
+"@next/swc-linux-x64-musl@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-linux-x64-musl@npm:15.1.2"
   conditions: os=linux & cpu=x64 & libc=musl
   languageName: node
   linkType: hard
 
-"@next/swc-win32-arm64-msvc@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-win32-arm64-msvc@npm:15.0.4"
+"@next/swc-win32-arm64-msvc@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-win32-arm64-msvc@npm:15.1.2"
   conditions: os=win32 & cpu=arm64
   languageName: node
   linkType: hard
 
-"@next/swc-win32-x64-msvc@npm:15.0.4":
-  version: 15.0.4
-  resolution: "@next/swc-win32-x64-msvc@npm:15.0.4"
+"@next/swc-win32-x64-msvc@npm:15.1.2":
+  version: 15.1.2
+  resolution: "@next/swc-win32-x64-msvc@npm:15.1.2"
   conditions: os=win32 & cpu=x64
   languageName: node
   linkType: hard
@@ -4275,10 +4275,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@paddle/paddle-node-sdk@npm:2.2.2":
-  version: 2.2.2
-  resolution: "@paddle/paddle-node-sdk@npm:2.2.2"
-  checksum: 10/9d10da108d3700a8e5de3fe52e7715d0b15f1acc701612088e73f7b2fa3321bf7440b8d8546702f40dd26fb6738e82cff3d8d42e101ff5e441701870ba4e7528
+"@paddle/paddle-node-sdk@npm:2.4.0":
+  version: 2.4.0
+  resolution: "@paddle/paddle-node-sdk@npm:2.4.0"
+  checksum: 10/0a3451afdea2e6a9d4b10d2a2d18f1bf96b40b03730bcd5e814431a6e7d43f03b8f4459780e0e289e515104ab8bc613ef9da07bba3db7704c49d02f0c0fd350a
   languageName: node
   linkType: hard
 
@@ -5310,9 +5310,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@react-email/components@npm:0.0.31":
-  version: 0.0.31
-  resolution: "@react-email/components@npm:0.0.31"
+"@react-email/components@npm:0.0.32":
+  version: 0.0.32
+  resolution: "@react-email/components@npm:0.0.32"
   dependencies:
     "@react-email/body": "npm:0.0.11"
     "@react-email/button": "npm:0.0.19"
@@ -5329,14 +5329,14 @@ __metadata:
     "@react-email/link": "npm:0.0.12"
     "@react-email/markdown": "npm:0.0.14"
     "@react-email/preview": "npm:0.0.12"
-    "@react-email/render": "npm:1.0.3"
+    "@react-email/render": "npm:1.0.4"
     "@react-email/row": "npm:0.0.12"
     "@react-email/section": "npm:0.0.16"
     "@react-email/tailwind": "npm:1.0.4"
     "@react-email/text": "npm:0.0.11"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/2850943e9e326ea3e9d6a2e55665c5ab6a00f962ff5f01541cca61abcf45770a4cc6a59de503a60eb4cff257805406470ad8a3969b2b5994754bfe1872830d14
+  checksum: 10/0a57d122a71581e36f352479165487299d45585656faea5bc66db27637a152ff0bc1edd97da3d78a131d799a3cba188ad7ec85b54987c53c7b24747fdca48bfe
   languageName: node
   linkType: hard
 
@@ -5432,17 +5432,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@react-email/render@npm:1.0.3":
-  version: 1.0.3
-  resolution: "@react-email/render@npm:1.0.3"
+"@react-email/render@npm:1.0.4":
+  version: 1.0.4
+  resolution: "@react-email/render@npm:1.0.4"
   dependencies:
     html-to-text: "npm:9.0.5"
-    prettier: "npm:3.3.3"
+    prettier: "npm:3.4.2"
     react-promise-suspense: "npm:0.3.4"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
     react-dom: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/01f26433ea50548e61aa5a3f3ad41e3aabc514147bce4157417c5e9c280566b150328e4cce65c0fb8d382cc9bd421a8ab4b1883732d4ef7b3c84212268753dcd
+  checksum: 10/468da62f5fefb75eef813406be1b7f5c73bb60c135c8afb1d0adf511d5b31f807d19821396c2df75a1884998a7ea71b10d99439a2c9b4872adfa273ae70b3af7
   languageName: node
   linkType: hard
 
@@ -5980,15 +5980,15 @@ __metadata:
     "@formkit/auto-animate": "npm:0.8.2"
     "@hello-pangea/dnd": "npm:17.0.0"
     "@lukemorales/query-key-factory": "npm:1.3.4"
-    "@mantine/carousel": "npm:7.15.2"
-    "@mantine/charts": "npm:7.15.2"
-    "@mantine/code-highlight": "npm:7.15.2"
-    "@mantine/core": "npm:7.15.2"
-    "@mantine/dates": "npm:7.15.2"
-    "@mantine/form": "npm:7.15.2"
-    "@mantine/hooks": "npm:7.15.2"
-    "@mantine/modals": "npm:7.15.2"
-    "@mantine/notifications": "npm:7.15.2"
+    "@mantine/carousel": "npm:7.16.1"
+    "@mantine/charts": "npm:7.16.1"
+    "@mantine/code-highlight": "npm:7.16.1"
+    "@mantine/core": "npm:7.16.1"
+    "@mantine/dates": "npm:7.16.1"
+    "@mantine/form": "npm:7.16.1"
+    "@mantine/hooks": "npm:7.16.1"
+    "@mantine/modals": "npm:7.16.1"
+    "@mantine/notifications": "npm:7.16.1"
     "@remix-pwa/dev": "npm:3.1.0"
     "@remix-pwa/sw": "npm:3.0.10"
     "@remix-pwa/worker-runtime": "npm:2.1.4"
@@ -6000,8 +6000,8 @@ __metadata:
     "@ryot/graphql": "workspace:*"
     "@ryot/ts-utils": "workspace:*"
     "@tabler/icons-react": "npm:3.17.0"
-    "@tanstack/react-query": "npm:5.62.11"
-    "@tanstack/react-query-devtools": "npm:5.62.11"
+    "@tanstack/react-query": "npm:5.64.2"
+    "@tanstack/react-query-devtools": "npm:5.64.2"
     "@types/cookie": "npm:1.0.0"
     "@types/crypto-js": "npm:4.2.2"
     "@types/howler": "npm:2.2.12"
@@ -6022,13 +6022,13 @@ __metadata:
     html2canvas: "npm:1.4.1"
     humanize-duration-ts: "npm:2.1.1"
     immer: "npm:10.1.1"
-    isbot: "npm:5.1.19"
-    jotai: "npm:2.11.0"
+    isbot: "npm:5.1.21"
+    jotai: "npm:2.11.1"
     js-cookie: "npm:3.0.5"
     jwt-decode: "npm:4.0.0"
     mantine-datatable: "npm:7.15.1"
     nanoid: "npm:5.0.9"
-    postcss: "npm:8.4.49"
+    postcss: "npm:8.5.1"
     postcss-preset-mantine: "npm:1.17.0"
     postcss-simple-vars: "npm:7.0.1"
     react: "npm:18.3.1"
@@ -6041,14 +6041,14 @@ __metadata:
     tailwind-merge: "npm:2.6.0"
     tiny-invariant: "npm:1.3.3"
     ts-essentials: "npm:10.0.4"
-    ts-pattern: "npm:5.6.0"
-    typescript: "npm:5.7.2"
+    ts-pattern: "npm:5.6.2"
+    typescript: "npm:5.7.3"
     typescript-plugin-css-modules: "npm:5.1.0"
     typescript-remix-routes-plugin: "npm:1.0.1"
     ufo: "npm:1.5.4"
     usehooks-ts: "npm:3.1.0"
-    uuid: "npm:11.0.3"
-    vite: "npm:5.4.11"
+    uuid: "npm:11.0.5"
+    vite: "npm:5.4.14"
     vite-tsconfig-paths: "npm:5.1.4"
     zod: "npm:3.24.1"
     zodix: "npm:0.4.4"
@@ -6081,7 +6081,7 @@ __metadata:
   dependencies:
     "@biomejs/biome": "npm:1.9.4"
     tsconfig-moon: "npm:1.3.0"
-    typescript: "npm:5.7.2"
+    typescript: "npm:5.7.3"
   languageName: unknown
   linkType: soft
 
@@ -6089,13 +6089,13 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ryot/transactional@workspace:libs/transactional"
   dependencies:
-    "@react-email/components": "npm:0.0.31"
+    "@react-email/components": "npm:0.0.32"
     "@ryot/ts-utils": "workspace:*"
     "@types/react": "npm:18.3.18"
     "@types/react-dom": "npm:18.3.5"
     react: "npm:18.3.1"
     react-dom: "npm:18.3.1"
-    react-email: "npm:3.0.4"
+    react-email: "npm:3.0.6"
   languageName: unknown
   linkType: soft
 
@@ -6105,7 +6105,7 @@ __metadata:
   dependencies:
     "@conform-to/zod": "npm:1.2.2"
     "@ryot/generated": "workspace:*"
-    "@types/lodash": "npm:4.17.13"
+    "@types/lodash": "npm:4.17.14"
     clsx: "npm:2.1.1"
     dayjs: "npm:1.11.13"
     humanize-duration-ts: "npm:2.1.1"
@@ -6123,11 +6123,11 @@ __metadata:
     "@conform-to/zod": "npm:1.2.2"
     "@isaacs/ttlcache": "npm:1.4.1"
     "@paddle/paddle-js": "npm:1.3.3"
-    "@paddle/paddle-node-sdk": "npm:2.2.2"
+    "@paddle/paddle-node-sdk": "npm:2.4.0"
     "@radix-ui/react-label": "npm:2.1.1"
     "@radix-ui/react-slot": "npm:1.1.1"
     "@radix-ui/react-tooltip": "npm:1.1.6"
-    "@react-email/components": "npm:0.0.31"
+    "@react-email/components": "npm:0.0.32"
     "@remix-run/dev": "npm:2.15.2"
     "@remix-run/node": "npm:2.15.2"
     "@remix-run/react": "npm:2.15.2"
@@ -6137,49 +6137,49 @@ __metadata:
     "@ryot/transactional": "workspace:*"
     "@ryot/ts-utils": "workspace:*"
     "@tabler/icons-react": "npm:3.17.0"
-    "@tailwindcss/typography": "npm:0.5.15"
+    "@tailwindcss/typography": "npm:0.5.16"
     "@types/crypto-js": "npm:4.2.2"
-    "@types/lodash": "npm:4.17.13"
+    "@types/lodash": "npm:4.17.14"
     "@types/nodemailer": "npm:6.4.17"
     "@types/react": "npm:18.3.18"
     "@types/react-dom": "npm:18.3.5"
-    "@unkey/api": "npm:0.29.0"
+    "@unkey/api": "npm:0.32.0"
     autoprefixer: "npm:10.4.20"
     class-variance-authority: "npm:0.7.1"
     clsx: "npm:2.1.1"
     crypto-js: "npm:4.2.0"
     dayjs: "npm:1.11.13"
-    drizzle-kit: "npm:0.30.1"
-    drizzle-orm: "npm:0.38.3"
-    embla-carousel-autoplay: "npm:8.5.1"
-    embla-carousel-react: "npm:8.5.1"
+    drizzle-kit: "npm:0.30.2"
+    drizzle-orm: "npm:0.38.4"
+    embla-carousel-autoplay: "npm:8.5.2"
+    embla-carousel-react: "npm:8.5.2"
     graphql: "npm:16.10.0"
     graphql-request: "npm:7.1.2"
     humanize-duration-ts: "npm:2.1.1"
-    input-otp: "npm:1.4.1"
-    isbot: "npm:5.1.19"
+    input-otp: "npm:1.4.2"
+    isbot: "npm:5.1.21"
     lodash: "npm:4.17.21"
-    lucide-react: "npm:0.469.0"
+    lucide-react: "npm:0.473.0"
     nanoid: "npm:5.0.9"
     next-themes: "npm:0.4.4"
     nodemailer: "npm:6.9.16"
     openid-client: "npm:5.7.1"
-    postcss: "npm:8.4.49"
+    postcss: "npm:8.5.1"
     postgres: "npm:3.4.5"
     react: "npm:18.3.1"
     react-dom: "npm:18.3.1"
     remix-routes: "npm:1.7.7"
     remix-utils: "npm:7.7.0"
-    sonner: "npm:1.7.1"
+    sonner: "npm:1.7.2"
     tailwind-merge: "npm:2.6.0"
     tailwindcss: "npm:3.4.17"
     tailwindcss-animate: "npm:1.0.7"
     tiny-invariant: "npm:1.3.3"
-    ts-pattern: "npm:5.6.0"
-    typescript: "npm:5.7.2"
+    ts-pattern: "npm:5.6.2"
+    typescript: "npm:5.7.3"
     typescript-remix-routes-plugin: "npm:1.0.1"
     ufo: "npm:1.5.4"
-    vite: "npm:5.4.11"
+    vite: "npm:5.4.14"
     vite-tsconfig-paths: "npm:5.1.4"
     zod: "npm:3.24.1"
     zodix: "npm:0.4.4"
@@ -6210,12 +6210,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@swc/helpers@npm:0.5.13":
-  version: 0.5.13
-  resolution: "@swc/helpers@npm:0.5.13"
+"@swc/helpers@npm:0.5.15":
+  version: 0.5.15
+  resolution: "@swc/helpers@npm:0.5.15"
   dependencies:
-    tslib: "npm:^2.4.0"
-  checksum: 10/6ba2f7e215d32d71fce139e2cfc426b3ed7eaa709febdeb07b97260a4c9eea4784cf047cc1271be273990b08220b576b94a42b5780947c0b3be84973a847a24d
+    tslib: "npm:^2.8.0"
+  checksum: 10/e3f32c6deeecfb0fa3f22edff03a7b358e7ce16d27b0f1c8b5cdc3042c5c4ce4da6eac0b781ab7cc4f54696ece657467d56734fb26883439fb00017385364c4c
   languageName: node
   linkType: hard
 
@@ -6237,9 +6237,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@tailwindcss/typography@npm:0.5.15":
-  version: 0.5.15
-  resolution: "@tailwindcss/typography@npm:0.5.15"
+"@tailwindcss/typography@npm:0.5.16":
+  version: 0.5.16
+  resolution: "@tailwindcss/typography@npm:0.5.16"
   dependencies:
     lodash.castarray: "npm:^4.4.0"
     lodash.isplainobject: "npm:^4.0.6"
@@ -6247,44 +6247,44 @@ __metadata:
     postcss-selector-parser: "npm:6.0.10"
   peerDependencies:
     tailwindcss: "*"
-  checksum: 10/8c677e4de25a5362d757c3c0b9d594b63c789ab287cad846a082ca227e0d3435145793301ced8f32d5215d5c21537195e7d87ca08a6e7a8facf47a11e7f07d22
+  checksum: 10/ca6cca2c824b4124223dd28d4bd5cc21dd261fe53a9654b9802bb958badd637313118d8e81978c3509df7dac1826317050fb034bc4357085b451371e31adff6d
   languageName: node
   linkType: hard
 
-"@tanstack/query-core@npm:5.62.9":
-  version: 5.62.9
-  resolution: "@tanstack/query-core@npm:5.62.9"
-  checksum: 10/1a10a256d8c7da657d4c370f46bfc9f874bbaa201f5ef63f2d20f20eb5c9f515228c76e9b75e2510a78541a6dc2903cbb3e1495b81037585a67542d48839d977
+"@tanstack/query-core@npm:5.64.2":
+  version: 5.64.2
+  resolution: "@tanstack/query-core@npm:5.64.2"
+  checksum: 10/6b9c225341d3636de17bfd92414d75a26472e3766c28bf9c0d617d668d573392d3381ca03daba0e59b17714ac88e1595d0b170da4b8a789ea6337c864b38f6c7
   languageName: node
   linkType: hard
 
-"@tanstack/query-devtools@npm:5.62.9":
-  version: 5.62.9
-  resolution: "@tanstack/query-devtools@npm:5.62.9"
-  checksum: 10/33c3617da4db69fa2805dd7cb76ce4ffc769974ca670421d6a5c16f108c80f885d264987f1f3b3b0bb2cf8fdf609e73b2dfc50fe0b735af1bd1cc62e0d4857c7
+"@tanstack/query-devtools@npm:5.64.2":
+  version: 5.64.2
+  resolution: "@tanstack/query-devtools@npm:5.64.2"
+  checksum: 10/273c241037d56ae24d0047f2fd5f5349dc672f76add5c2e30907844e785583354a25ff88904d871c3bc53f74d1036d59014ee9916319b4c5ce4d016e2a64ada5
   languageName: node
   linkType: hard
 
-"@tanstack/react-query-devtools@npm:5.62.11":
-  version: 5.62.11
-  resolution: "@tanstack/react-query-devtools@npm:5.62.11"
+"@tanstack/react-query-devtools@npm:5.64.2":
+  version: 5.64.2
+  resolution: "@tanstack/react-query-devtools@npm:5.64.2"
   dependencies:
-    "@tanstack/query-devtools": "npm:5.62.9"
+    "@tanstack/query-devtools": "npm:5.64.2"
   peerDependencies:
-    "@tanstack/react-query": ^5.62.11
+    "@tanstack/react-query": ^5.64.2
     react: ^18 || ^19
-  checksum: 10/ce2da327f6afbe8a077297294e6fc47f1ca6e4c362c789cc1ef8bb579410ff61ba553c63696beebc3222349eaa37274960d13598fa0cab0dae95679db3530743
+  checksum: 10/86edc68ede1a09c39487c50fd380812d9124819d2aa52e460df8df059e6056e9a0360ba662b0bd732a10fdd09a73c45fab3938d99fb4a27752e311a93c82ab74
   languageName: node
   linkType: hard
 
-"@tanstack/react-query@npm:5.62.11":
-  version: 5.62.11
-  resolution: "@tanstack/react-query@npm:5.62.11"
+"@tanstack/react-query@npm:5.64.2":
+  version: 5.64.2
+  resolution: "@tanstack/react-query@npm:5.64.2"
   dependencies:
-    "@tanstack/query-core": "npm:5.62.9"
+    "@tanstack/query-core": "npm:5.64.2"
   peerDependencies:
     react: ^18 || ^19
-  checksum: 10/3f2c372fb7fa656a4d61599657f6df3d8a9164ade866c456c2139f29fc991ee7fbe2dfcd6e1da87bff6583e528f4914c8f8cf209ece8219c20f2a8c661d98e13
+  checksum: 10/c7ca9c81fd03cdd7434b3954edfeedcc3c0c5d647c9b2f1d65729f9a0535b006abeb34179f0be1df70ebe689a5c0265b6316505a1f8b4d37bee7947577eb76f2
   languageName: node
   linkType: hard
 
@@ -6488,10 +6488,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/lodash@npm:4.17.13":
-  version: 4.17.13
-  resolution: "@types/lodash@npm:4.17.13"
-  checksum: 10/ddb34e20810c71be2d9445bcc4b64ec25b83976738454de709854b79c7f655b03704b76235445699956d65012987720e0e429a35489de65495cdb5420202d905
+"@types/lodash@npm:4.17.14":
+  version: 4.17.14
+  resolution: "@types/lodash@npm:4.17.14"
+  checksum: 10/6ee40725f3e192f5ef1f493caca19210aa7acd7adc3136b8dba84d418a35be0abea0668105aed9f696ad62a54310a9c0d328971ad4b157f5bcda700424ed5aae
   languageName: node
   linkType: hard
 
@@ -6645,12 +6645,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@unkey/api@npm:0.29.0":
-  version: 0.29.0
-  resolution: "@unkey/api@npm:0.29.0"
+"@unkey/api@npm:0.32.0":
+  version: 0.32.0
+  resolution: "@unkey/api@npm:0.32.0"
   dependencies:
     "@unkey/rbac": "npm:^0.3.1"
-  checksum: 10/69596bb34a86151d2f168281ffb35cfe14dcc24ae4974df4a4914accdb3487ce1fb0e56afba1af2884b077d5b25d4e813de78410c28d671d1e5e046c017901f2
+  checksum: 10/3e73ea01891cd0b01b69de022bcbbe714649b9c01f91bafdd3877f2211ca84b56c0acfc2624b6d25a83223b017f20d21466b540855a906927c0e4490ea70e299
   languageName: node
   linkType: hard
 
@@ -7707,6 +7707,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"chokidar@npm:4.0.3":
+  version: 4.0.3
+  resolution: "chokidar@npm:4.0.3"
+  dependencies:
+    readdirp: "npm:^4.0.1"
+  checksum: 10/bf2a575ea5596000e88f5db95461a9d59ad2047e939d5a4aac59dd472d126be8f1c1ff3c7654b477cf532d18f42a97279ef80ee847972fd2a25410bf00b80b59
+  languageName: node
+  linkType: hard
+
 "chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3":
   version: 3.5.3
   resolution: "chokidar@npm:3.5.3"
@@ -7745,15 +7754,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"chokidar@npm:^4.0.1":
-  version: 4.0.1
-  resolution: "chokidar@npm:4.0.1"
-  dependencies:
-    readdirp: "npm:^4.0.1"
-  checksum: 10/62749d2173a60cc5632d6c6e0b7024f33aadce47b06d02e55ad03c7b8daaaf2fc85d4296c047473d04387fd992dab9384cc5263c70a3dc3018b7ebecfb5b5217
-  languageName: node
-  linkType: hard
-
 "chownr@npm:^1.1.1":
   version: 1.1.4
   resolution: "chownr@npm:1.1.4"
@@ -8796,9 +8796,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"drizzle-kit@npm:0.30.1":
-  version: 0.30.1
-  resolution: "drizzle-kit@npm:0.30.1"
+"drizzle-kit@npm:0.30.2":
+  version: 0.30.2
+  resolution: "drizzle-kit@npm:0.30.2"
   dependencies:
     "@drizzle-team/brocli": "npm:^0.10.2"
     "@esbuild-kit/esm-loader": "npm:^2.5.5"
@@ -8806,13 +8806,13 @@ __metadata:
     esbuild-register: "npm:^3.5.0"
   bin:
     drizzle-kit: bin.cjs
-  checksum: 10/6b93d3a00ce812c060d5659052e6f7f6f626a46fea299bf649e30234b694f22ed9464616ff3831768837d0f03f5d6e813c5c2bdd465aa99002a27bb2578bc1e8
+  checksum: 10/64bdb3863bbb26ceaaaabdd1b15c24ef8bf3b6a41e87f23992ef6ed80796b2c66fc9422161b4779ea6961649acfa905815906211b0a7789c05c7c3c5bed33a34
   languageName: node
   linkType: hard
 
-"drizzle-orm@npm:0.38.3":
-  version: 0.38.3
-  resolution: "drizzle-orm@npm:0.38.3"
+"drizzle-orm@npm:0.38.4":
+  version: 0.38.4
+  resolution: "drizzle-orm@npm:0.38.4"
   peerDependencies:
     "@aws-sdk/client-rds-data": ">=3"
     "@cloudflare/workers-types": ">=4"
@@ -8901,7 +8901,7 @@ __metadata:
       optional: true
     sqlite3:
       optional: true
-  checksum: 10/58bf6326eb8a55e463ebc7883effe29516e918f6400cf98e88fe92c87a8b682bf5ab4383f625b931d8c5b1ef5856ce445d84f037402ef8cb47081fdcbeeed250
+  checksum: 10/0fb012b41ad830b280bb5f48430f01473c36a00f568eedae3aa81910cfdb89e36e119d464023c61a40b33d102d6baca00ededca7afff1677c4f9e3ff994f78f1
   languageName: node
   linkType: hard
 
@@ -8991,12 +8991,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"embla-carousel-autoplay@npm:8.5.1":
-  version: 8.5.1
-  resolution: "embla-carousel-autoplay@npm:8.5.1"
+"embla-carousel-autoplay@npm:8.5.2":
+  version: 8.5.2
+  resolution: "embla-carousel-autoplay@npm:8.5.2"
   peerDependencies:
-    embla-carousel: 8.5.1
-  checksum: 10/d1eef320097045d28c1ff1decb78fdb127e880fc0e4afb15e8bb246502473e4feb7e4a46327953090ea659d7d6ec2197406a15dda962b6fa6afc337263c9932b
+    embla-carousel: 8.5.2
+  checksum: 10/39b17a0537700626995573073d431d16a64e8b70056a21163e45f1593a5768420cc7717fcc463ed0ab654f7bb34937ec178c78ae128c74a7d954bf9ea4ce08e8
   languageName: node
   linkType: hard
 
@@ -9011,24 +9011,24 @@ __metadata:
   languageName: node
   linkType: hard
 
-"embla-carousel-react@npm:8.5.1":
-  version: 8.5.1
-  resolution: "embla-carousel-react@npm:8.5.1"
+"embla-carousel-react@npm:8.5.2":
+  version: 8.5.2
+  resolution: "embla-carousel-react@npm:8.5.2"
   dependencies:
-    embla-carousel: "npm:8.5.1"
-    embla-carousel-reactive-utils: "npm:8.5.1"
+    embla-carousel: "npm:8.5.2"
+    embla-carousel-reactive-utils: "npm:8.5.2"
   peerDependencies:
     react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
-  checksum: 10/709297755d159f9c9432ec839a2f7146dd273250c588714358c0d02891d27ddf43470d2ecc75c362b1c979cd94f8d10f3f7f2cb740d4489eb0f1098b8c0641ad
+  checksum: 10/7390154357ffad9eb4641d5d5cd7ed061d33dfaa02c7cfb1b8828e562fe0b7dfb1a0590108db832179a9c4453184b63926d4d4403ba576e8ea6710bad9fd13f0
   languageName: node
   linkType: hard
 
-"embla-carousel-reactive-utils@npm:8.5.1":
-  version: 8.5.1
-  resolution: "embla-carousel-reactive-utils@npm:8.5.1"
+"embla-carousel-reactive-utils@npm:8.5.2":
+  version: 8.5.2
+  resolution: "embla-carousel-reactive-utils@npm:8.5.2"
   peerDependencies:
-    embla-carousel: 8.5.1
-  checksum: 10/33ff0c00db0a72bf49b1dc519947ed928ba88a6acfae7d4d5fbd44ca5d674fa9e6f1d1aa7ecf93840d9a6a3826cf4ce6090b8a89d83932dc478b8034368cedf3
+    embla-carousel: 8.5.2
+  checksum: 10/b46011e7031af6642673a7ded7139559ea8af12bf61a6e9aab1ae8006cabafce78ad0c7a3e1c425e96a0fa22de02849fd6d07e86bc118da26cf84eaedccd8a6f
   languageName: node
   linkType: hard
 
@@ -9039,10 +9039,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"embla-carousel@npm:8.5.1":
-  version: 8.5.1
-  resolution: "embla-carousel@npm:8.5.1"
-  checksum: 10/db14b22dfbfedee0d70a3a63a02be3f41c6d8ba385408d8e7d878a1927b40b144f8584c59b84cae8b52db8f48b0a4148c7c8a7da2bef27997ca728a12e2ce3cd
+"embla-carousel@npm:8.5.2":
+  version: 8.5.2
+  resolution: "embla-carousel@npm:8.5.2"
+  checksum: 10/ed1407d38b86523d4d5abd88ab47804f08e194fb0da0b4b225013ca217d5bb9c40c0899e31261ba2b1748101ae9ccb02a3eaf08d509080f85c9229223f8f8ff3
   languageName: node
   linkType: hard
 
@@ -11024,13 +11024,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"input-otp@npm:1.4.1":
-  version: 1.4.1
-  resolution: "input-otp@npm:1.4.1"
+"input-otp@npm:1.4.2":
+  version: 1.4.2
+  resolution: "input-otp@npm:1.4.2"
   peerDependencies:
     react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
     react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
-  checksum: 10/00bb230e53cddb2d711c107c333bdcf4e02169b5b9060b54b8f41ff9e806eede6b12b24782ea4a4900d9a2e97775723104a3d3458a4e840ff6a8c8a6733fc620
+  checksum: 10/7dfaa30ecda1640a1e6f78be05c52de58e35134f34684709b065abb47cae23c054cb4d87df8af317f8ecb4ef384167f3237aa31cd2601bed5f8ca88ed93b7452
   languageName: node
   linkType: hard
 
@@ -11377,10 +11377,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"isbot@npm:5.1.19":
-  version: 5.1.19
-  resolution: "isbot@npm:5.1.19"
-  checksum: 10/d79b0635f9318d4a0bfc10da16ced2845854f0eae9e12db13d6dbcaf24d3a40ef97dd8d2e03b8703734b09f0e886386baa1723b2d320b425afd9cf9fbd8a32b5
+"isbot@npm:5.1.21":
+  version: 5.1.21
+  resolution: "isbot@npm:5.1.21"
+  checksum: 10/ef19941c34880f06e8d884091c5f500d6a2f942191c3629c605e595c9651f1ff320befe0b5721026ffcb9ba39ed9ee6c4fb77516ebd27b28e7398f4adba75ab5
   languageName: node
   linkType: hard
 
@@ -11488,9 +11488,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jotai@npm:2.11.0":
-  version: 2.11.0
-  resolution: "jotai@npm:2.11.0"
+"jotai@npm:2.11.1":
+  version: 2.11.1
+  resolution: "jotai@npm:2.11.1"
   peerDependencies:
     "@types/react": ">=17.0.0"
     react: ">=17.0.0"
@@ -11499,7 +11499,7 @@ __metadata:
       optional: true
     react:
       optional: true
-  checksum: 10/607c2ab78301291719635f566c2d3c4fa0794a783f429fd07deffb5dd0920b198d25aebc82bc4f6894bc07aff3287fd06a02de394e0802b0ed2ad85f503eac09
+  checksum: 10/6287b10464e8fe31984d2ac4b4e326969bd148eca26d8bee07f47a72c75ccd164497c74fe0ad49b231406f61294757e50f0aaec66a9ef305c401309bba357d72
   languageName: node
   linkType: hard
 
@@ -11929,12 +11929,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lucide-react@npm:0.469.0":
-  version: 0.469.0
-  resolution: "lucide-react@npm:0.469.0"
+"lucide-react@npm:0.473.0":
+  version: 0.473.0
+  resolution: "lucide-react@npm:0.473.0"
   peerDependencies:
     react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
-  checksum: 10/8cf1f5b125354f1ac4304bb0eead52f369fd67866199bc37a63c856369c97003702b90522e257971670e75acaaf1e55c05c8e4960aaa7ba96dc084d3f0505102
+  checksum: 10/e2d2248c451b2c378e02b26727b8e7a2e810a1c2b42efedc2388b423b3d64634e27e69f818a39ddcd0c0fa3aa513ce24d0e389e26c8f1b88fa49f99fd3626ad1
   languageName: node
   linkType: hard
 
@@ -12966,6 +12966,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"nanoid@npm:^3.3.8":
+  version: 3.3.8
+  resolution: "nanoid@npm:3.3.8"
+  bin:
+    nanoid: bin/nanoid.cjs
+  checksum: 10/2d1766606cf0d6f47b6f0fdab91761bb81609b2e3d367027aff45e6ee7006f660fb7e7781f4a34799fe6734f1268eeed2e37a5fdee809ade0c2d4eb11b0f9c40
+  languageName: node
+  linkType: hard
+
 "needle@npm:^3.1.0":
   version: 3.2.0
   resolution: "needle@npm:3.2.0"
@@ -12996,21 +13005,21 @@ __metadata:
   languageName: node
   linkType: hard
 
-"next@npm:15.0.4":
-  version: 15.0.4
-  resolution: "next@npm:15.0.4"
+"next@npm:15.1.2":
+  version: 15.1.2
+  resolution: "next@npm:15.1.2"
   dependencies:
-    "@next/env": "npm:15.0.4"
-    "@next/swc-darwin-arm64": "npm:15.0.4"
-    "@next/swc-darwin-x64": "npm:15.0.4"
-    "@next/swc-linux-arm64-gnu": "npm:15.0.4"
-    "@next/swc-linux-arm64-musl": "npm:15.0.4"
-    "@next/swc-linux-x64-gnu": "npm:15.0.4"
-    "@next/swc-linux-x64-musl": "npm:15.0.4"
-    "@next/swc-win32-arm64-msvc": "npm:15.0.4"
-    "@next/swc-win32-x64-msvc": "npm:15.0.4"
+    "@next/env": "npm:15.1.2"
+    "@next/swc-darwin-arm64": "npm:15.1.2"
+    "@next/swc-darwin-x64": "npm:15.1.2"
+    "@next/swc-linux-arm64-gnu": "npm:15.1.2"
+    "@next/swc-linux-arm64-musl": "npm:15.1.2"
+    "@next/swc-linux-x64-gnu": "npm:15.1.2"
+    "@next/swc-linux-x64-musl": "npm:15.1.2"
+    "@next/swc-win32-arm64-msvc": "npm:15.1.2"
+    "@next/swc-win32-x64-msvc": "npm:15.1.2"
     "@swc/counter": "npm:0.1.3"
-    "@swc/helpers": "npm:0.5.13"
+    "@swc/helpers": "npm:0.5.15"
     busboy: "npm:1.6.0"
     caniuse-lite: "npm:^1.0.30001579"
     postcss: "npm:8.4.31"
@@ -13020,8 +13029,8 @@ __metadata:
     "@opentelemetry/api": ^1.1.0
     "@playwright/test": ^1.41.2
     babel-plugin-react-compiler: "*"
-    react: ^18.2.0 || 19.0.0-rc-66855b96-20241106 || ^19.0.0
-    react-dom: ^18.2.0 || 19.0.0-rc-66855b96-20241106 || ^19.0.0
+    react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+    react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
     sass: ^1.3.0
   dependenciesMeta:
     "@next/swc-darwin-arm64":
@@ -13053,7 +13062,7 @@ __metadata:
       optional: true
   bin:
     next: dist/bin/next
-  checksum: 10/2dc86dcb314ca99f4cc6946a8bdae9add93b60fa3ff27729d070ee8e11d851fff61bf122a095d8efa6662e19e168c095473d22ad79ccb6d825ea8d6203cd600a
+  checksum: 10/174ed53c730876c83fc9d64364cf2e7c884ca0787ab772d7b7a6292d3c687fe54b83db3b5cb98eb63a3e3155e17b5a89b80ad09bfb9921878a541a6a2658a508
   languageName: node
   linkType: hard
 
@@ -14077,14 +14086,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss@npm:8.4.49, postcss@npm:^8.4.47":
-  version: 8.4.49
-  resolution: "postcss@npm:8.4.49"
+"postcss@npm:8.5.1":
+  version: 8.5.1
+  resolution: "postcss@npm:8.5.1"
   dependencies:
-    nanoid: "npm:^3.3.7"
+    nanoid: "npm:^3.3.8"
     picocolors: "npm:^1.1.1"
     source-map-js: "npm:^1.2.1"
-  checksum: 10/28fe1005b1339870e0a5006375ba5ac1213fd69800f79e7db09c398e074421ba6e162898e94f64942fed554037fd292db3811d87835d25ab5ef7f3c9daacb6ca
+  checksum: 10/1fbd28753143f7f03e4604813639918182b15343c7ad0f4e72f3875fc2cc0b8494c887f55dc05008fad5fbf1e1e908ce2edbbce364a91f84dcefb71edf7cd31d
   languageName: node
   linkType: hard
 
@@ -14110,6 +14119,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"postcss@npm:^8.4.47":
+  version: 8.4.49
+  resolution: "postcss@npm:8.4.49"
+  dependencies:
+    nanoid: "npm:^3.3.7"
+    picocolors: "npm:^1.1.1"
+    source-map-js: "npm:^1.2.1"
+  checksum: 10/28fe1005b1339870e0a5006375ba5ac1213fd69800f79e7db09c398e074421ba6e162898e94f64942fed554037fd292db3811d87835d25ab5ef7f3c9daacb6ca
+  languageName: node
+  linkType: hard
+
 "postgres@npm:3.4.5":
   version: 3.4.5
   resolution: "postgres@npm:3.4.5"
@@ -14117,12 +14137,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"prettier@npm:3.3.3":
-  version: 3.3.3
-  resolution: "prettier@npm:3.3.3"
+"prettier@npm:3.4.2":
+  version: 3.4.2
+  resolution: "prettier@npm:3.4.2"
   bin:
     prettier: bin/prettier.cjs
-  checksum: 10/5beac1f30b5b40162532b8e2f7c3a4eb650910a2695e9c8512a62ffdc09dae93190c29db9107fa7f26d1b6c71aad3628ecb9b5de1ecb0911191099be109434d7
+  checksum: 10/a3e806fb0b635818964d472d35d27e21a4e17150c679047f5501e1f23bd4aa806adf660f0c0d35214a210d5d440da6896c2e86156da55f221a57938278dc326e
   languageName: node
   linkType: hard
 
@@ -14375,27 +14395,27 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-email@npm:3.0.4":
-  version: 3.0.4
-  resolution: "react-email@npm:3.0.4"
+"react-email@npm:3.0.6":
+  version: 3.0.6
+  resolution: "react-email@npm:3.0.6"
   dependencies:
     "@babel/core": "npm:7.24.5"
     "@babel/parser": "npm:7.24.5"
     chalk: "npm:4.1.2"
-    chokidar: "npm:^4.0.1"
+    chokidar: "npm:4.0.3"
     commander: "npm:11.1.0"
     debounce: "npm:2.0.0"
     esbuild: "npm:0.19.11"
     glob: "npm:10.3.4"
     log-symbols: "npm:4.1.0"
     mime-types: "npm:2.1.35"
-    next: "npm:15.0.4"
+    next: "npm:15.1.2"
     normalize-path: "npm:3.0.0"
     ora: "npm:5.4.1"
     socket.io: "npm:4.8.0"
   bin:
     email: dist/cli/index.js
-  checksum: 10/201dca72549a9cdcccb4569d255a21c7037f2d4424e5064d42b5ea3218f3552ed0dd58cddeb80ffb95df0990e0abd8b4e4a1b1235efc7b0586a6cf7602958661
+  checksum: 10/ad6b74da99ac24eebe9e5fa6b67116b04b1198afad3a50c8d8f7d1608bff8912ae4a2a73caab52b5b22492fb2707627518c36521895f1c3e8877057019e4c18e
   languageName: node
   linkType: hard
 
@@ -14430,13 +14450,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-number-format@npm:^5.4.2":
-  version: 5.4.2
-  resolution: "react-number-format@npm:5.4.2"
+"react-number-format@npm:^5.4.3":
+  version: 5.4.3
+  resolution: "react-number-format@npm:5.4.3"
   peerDependencies:
-    react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
-    react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
-  checksum: 10/d56bf3e132c7b1c51081e8f8be520344c1e43956e31070bb752e2a0fdb6d73b615695a82d4c4c04e6e1c105ee0d998f35949feef5a542a39f875112b0a746607
+    react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+    react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+  checksum: 10/351c70a8f3dcdc8469f4c27acb7ab18a2010e2bb6ea4eefb0be742aee517ee90a2b14772f1df43393acac158cff9e78268a49909f7f46577ec9acf07f4ec0fdf
   languageName: node
   linkType: hard
 
@@ -14475,7 +14495,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-remove-scroll-bar@npm:^2.3.3, react-remove-scroll-bar@npm:^2.3.6":
+"react-remove-scroll-bar@npm:^2.3.3":
   version: 2.3.6
   resolution: "react-remove-scroll-bar@npm:2.3.6"
   dependencies:
@@ -14491,6 +14511,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-remove-scroll-bar@npm:^2.3.7":
+  version: 2.3.8
+  resolution: "react-remove-scroll-bar@npm:2.3.8"
+  dependencies:
+    react-style-singleton: "npm:^2.2.2"
+    tslib: "npm:^2.0.0"
+  peerDependencies:
+    "@types/react": "*"
+    react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+  checksum: 10/6c0f8cff98b9f49a4ee2263f1eedf12926dced5ce220fbe83bd93544460e2a7ec8ec39b35d1b2a75d2fced0b2d64afeb8e66f830431ca896e05a20585f9fc350
+  languageName: node
+  linkType: hard
+
 "react-remove-scroll@npm:2.5.5":
   version: 2.5.5
   resolution: "react-remove-scroll@npm:2.5.5"
@@ -14510,22 +14546,22 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-remove-scroll@npm:^2.6.0":
-  version: 2.6.0
-  resolution: "react-remove-scroll@npm:2.6.0"
+"react-remove-scroll@npm:^2.6.2":
+  version: 2.6.2
+  resolution: "react-remove-scroll@npm:2.6.2"
   dependencies:
-    react-remove-scroll-bar: "npm:^2.3.6"
+    react-remove-scroll-bar: "npm:^2.3.7"
     react-style-singleton: "npm:^2.2.1"
     tslib: "npm:^2.1.0"
-    use-callback-ref: "npm:^1.3.0"
+    use-callback-ref: "npm:^1.3.3"
     use-sidecar: "npm:^1.1.2"
   peerDependencies:
-    "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
-    react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    "@types/react": "*"
+    react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
   peerDependenciesMeta:
     "@types/react":
       optional: true
-  checksum: 10/9fac79e1c2ed2c85729bfe82f61ef4ae5ce51f478736a13892a9a11e05cbd4e9599f9f0e012cb5fc0719e18dc1dd687ab61f516193228615df636db8b851245e
+  checksum: 10/e6d7e8c42793ae24c9d4a6cb3ebdf3499c1b413fb0c93c3fbf047d875396e944e88a0520dd7db3a20db37125ffadef45b1ce8cb77b74da44daf47c5eb2155c9a
   languageName: node
   linkType: hard
 
@@ -14584,6 +14620,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-style-singleton@npm:^2.2.2":
+  version: 2.2.3
+  resolution: "react-style-singleton@npm:2.2.3"
+  dependencies:
+    get-nonce: "npm:^1.0.0"
+    tslib: "npm:^2.0.0"
+  peerDependencies:
+    "@types/react": "*"
+    react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+  checksum: 10/62498094ff3877a37f351b29e6cad9e38b2eb1ac3c0cb27ebf80aee96554f80b35e17bdb552bcd7ac8b7cb9904fea93ea5668f2057c73d38f90b5d46bb9b27ab
+  languageName: node
+  linkType: hard
+
 "react-textarea-autosize@npm:8.5.6":
   version: 8.5.6
   resolution: "react-textarea-autosize@npm:8.5.6"
@@ -15645,13 +15697,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"sonner@npm:1.7.1":
-  version: 1.7.1
-  resolution: "sonner@npm:1.7.1"
+"sonner@npm:1.7.2":
+  version: 1.7.2
+  resolution: "sonner@npm:1.7.2"
   peerDependencies:
     react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
     react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
-  checksum: 10/a67279e3db9cf0ae235cc316812b22489dcbe2c9488e12313f35c5073520640ffe3c955a61ab1992ed4b4b96ca72087b6687b725305a0e3be9b1279c5a1fc735
+  checksum: 10/c17de412333bff0970a0b38375e034e2df974b1849c58ccb519ea7981aea7f8c0a4c9d8b8059c36a2d2ed9df1f4f571748e3d2037c90f13f0be8bb180eae20a0
   languageName: node
   linkType: hard
 
@@ -16271,10 +16323,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ts-pattern@npm:5.6.0":
-  version: 5.6.0
-  resolution: "ts-pattern@npm:5.6.0"
-  checksum: 10/ee21e10fe4de70b473880fd634627383b9f576db2956ae933e68097ae0d25008ea83330ab87e41bdb522c4187111fd2b004f1cc761986568dcdbc060bedaca98
+"ts-pattern@npm:5.6.2":
+  version: 5.6.2
+  resolution: "ts-pattern@npm:5.6.2"
+  checksum: 10/7eea891a2653650793143e058a6b488c9931444996234bfb71f569528e8aa55decdc13266b710135757614c4eaa1e8cb9015c46ccfd258a5bd81e5e86c2d1dcb
   languageName: node
   linkType: hard
 
@@ -16331,6 +16383,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"tslib@npm:^2.8.0":
+  version: 2.8.1
+  resolution: "tslib@npm:2.8.1"
+  checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7
+  languageName: node
+  linkType: hard
+
 "turbo-stream@npm:2.4.0":
   version: 2.4.0
   resolution: "turbo-stream@npm:2.4.0"
@@ -16434,13 +16493,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typescript@npm:5.7.2":
-  version: 5.7.2
-  resolution: "typescript@npm:5.7.2"
+"typescript@npm:5.7.3":
+  version: 5.7.3
+  resolution: "typescript@npm:5.7.3"
   bin:
     tsc: bin/tsc
     tsserver: bin/tsserver
-  checksum: 10/4caa3904df69db9d4a8bedc31bafc1e19ffb7b24fbde2997a1633ae1398d0de5bdbf8daf602ccf3b23faddf1aeeb9b795223a2ed9c9a4fdcaf07bfde114a401a
+  checksum: 10/6a7e556de91db3d34dc51cd2600e8e91f4c312acd8e52792f243c7818dfadb27bae677175fad6947f9c81efb6c57eb6b2d0c736f196a6ee2f1f7d57b74fc92fa
   languageName: node
   linkType: hard
 
@@ -16454,13 +16513,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typescript@patch:typescript@npm%3A5.7.2#optional!builtin<compat/typescript>":
-  version: 5.7.2
-  resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin<compat/typescript>::version=5.7.2&hash=5adc0c"
+"typescript@patch:typescript@npm%3A5.7.3#optional!builtin<compat/typescript>":
+  version: 5.7.3
+  resolution: "typescript@patch:typescript@npm%3A5.7.3#optional!builtin<compat/typescript>::version=5.7.3&hash=5adc0c"
   bin:
     tsc: bin/tsc
     tsserver: bin/tsserver
-  checksum: 10/ff27fc124bceb8969be722baa38af945b2505767cf794de3e2715e58f61b43780284060287d651fcbbdfb6f917f4653b20f4751991f17e0706db389b9bb3f75d
+  checksum: 10/3ac7b7e3e899273d2fbdce6e24b93d4d53a705ad7a7e4cc83b4c57bcb6d25948abcd2a21994f6b9c73ab03960f395aae95f0458de292a66ce0134233261714c3
   languageName: node
   linkType: hard
 
@@ -16767,6 +16826,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"use-callback-ref@npm:^1.3.3":
+  version: 1.3.3
+  resolution: "use-callback-ref@npm:1.3.3"
+  dependencies:
+    tslib: "npm:^2.0.0"
+  peerDependencies:
+    "@types/react": "*"
+    react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+  checksum: 10/adf06a7b6a27d3651c325ac9b66d2b82ccacaed7450b85b211d123e91d9a23cb5a587fcc6db5b4fd07ac7233e5abf024d30cf02ddc2ec46bca712151c0836151
+  languageName: node
+  linkType: hard
+
 "use-composed-ref@npm:^1.3.0":
   version: 1.3.0
   resolution: "use-composed-ref@npm:1.3.0"
@@ -16883,12 +16957,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"uuid@npm:11.0.3":
-  version: 11.0.3
-  resolution: "uuid@npm:11.0.3"
+"uuid@npm:11.0.5":
+  version: 11.0.5
+  resolution: "uuid@npm:11.0.5"
   bin:
     uuid: dist/esm/bin/uuid
-  checksum: 10/251385563195709eb0697c74a834764eef28e1656d61174e35edbd129288acb4d95a43f4ce8a77b8c2fc128e2b55924296a0945f964b05b9173469d045625ff2
+  checksum: 10/0594ecdff3051e15d4a2c614b4c72e73af373bde0a5d156512353c01156975295d024ae8d7151846d7bd4d22ccd251b16ed51b4318fa71505fb20ad984102dc1
   languageName: node
   linkType: hard
 
@@ -17053,9 +17127,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"vite@npm:5.4.11":
-  version: 5.4.11
-  resolution: "vite@npm:5.4.11"
+"vite@npm:5.4.14":
+  version: 5.4.14
+  resolution: "vite@npm:5.4.14"
   dependencies:
     esbuild: "npm:^0.21.3"
     fsevents: "npm:~2.3.3"
@@ -17092,7 +17166,7 @@ __metadata:
       optional: true
   bin:
     vite: bin/vite.js
-  checksum: 10/719c4dea896e9547958643354003c8c9ea98e5367196d98f5f46cffb3ec963fead3ea5853f5af941c79bbfb73583dec19bbb0d28d2f644b95d7f59c55e22919d
+  checksum: 10/ce382f4059eb6c939823b8f62163794752243755d84c71a4b73ad0f7d4d9f4c7a557a6ef4c78e0640f4bcf5ae5ec6b20c7ee4816419af3c81ba275f478b73468
   languageName: node
   linkType: hard
 


Test patch: