diff --git a/src/apps/dashboard/AppLayout.tsx b/src/apps/dashboard/AppLayout.tsx
index cf3a0da9cf..9030465b32 100644
--- a/src/apps/dashboard/AppLayout.tsx
+++ b/src/apps/dashboard/AppLayout.tsx
@@ -2,6 +2,8 @@ import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import { type Theme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import React, { FC, useCallback, useEffect, useState } from 'react';
import { Outlet, useLocation } from 'react-router-dom';
@@ -10,6 +12,7 @@ import AppToolbar from 'components/toolbar/AppToolbar';
import ElevationScroll from 'components/ElevationScroll';
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
import { useApi } from 'hooks/useApi';
+import { useLocale } from 'hooks/useLocale';
import AppTabs from './components/AppTabs';
import AppDrawer from './components/drawer/AppDrawer';
@@ -23,6 +26,7 @@ export const Component: FC = () => {
const [ isDrawerActive, setIsDrawerActive ] = useState(false);
const location = useLocation();
const { user } = useApi();
+ const { dateFnsLocale } = useLocale();
const isMediumScreen = useMediaQuery((t: Theme) => t.breakpoints.up('md'));
const isDrawerAvailable = Boolean(user)
@@ -43,52 +47,54 @@ export const Component: FC = () => {
}, []);
return (
-
-
-
+
+
+
+
+
+
+
+
+
+ {
+ isDrawerAvailable && (
+
+ )
+ }
+
+
-
-
-
-
-
-
- {
- isDrawerAvailable && (
-
- )
- }
-
-
-
-
-
+
+
+
+
-
+
);
};
diff --git a/src/apps/dashboard/routes/activity.tsx b/src/apps/dashboard/routes/activity.tsx
index 630cd4ba95..666d08441c 100644
--- a/src/apps/dashboard/routes/activity.tsx
+++ b/src/apps/dashboard/routes/activity.tsx
@@ -1,5 +1,6 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry';
+import { LogLevel } from '@jellyfin/sdk/lib/generated-client/models/log-level';
import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto';
import Box from '@mui/material/Box';
import ToggleButton from '@mui/material/ToggleButton';
@@ -68,7 +69,15 @@ const Activity = () => {
[userId]: user
};
}, {});
- }, [usersData]);
+ }, [ usersData ]);
+
+ const userNames = useMemo(() => {
+ const names: string[] = [];
+ usersData?.forEach(user => {
+ if (user.Name) names.push(user.Name);
+ });
+ return names;
+ }, [ usersData ]);
const UserCell = getUserCell(users);
@@ -92,8 +101,10 @@ const Activity = () => {
enableResizing: false,
muiTableBodyCellProps: {
align: 'center'
- }
- }], [ activityView, users, UserCell ]);
+ },
+ filterVariant: 'multi-select',
+ filterSelectOptions: userNames
+ }], [ activityView, userNames, users, UserCell ]);
const columns = useMemo[]>(() => [
{
@@ -101,7 +112,8 @@ const Activity = () => {
accessorFn: row => parseISO8601Date(row.Date),
header: globalize.translate('LabelTime'),
size: 160,
- Cell: ({ cell }) => toLocaleString(cell.getValue())
+ Cell: ({ cell }) => toLocaleString(cell.getValue()),
+ filterVariant: 'datetime-range'
},
{
accessorKey: 'Severity',
@@ -111,7 +123,9 @@ const Activity = () => {
enableResizing: false,
muiTableBodyCellProps: {
align: 'center'
- }
+ },
+ filterVariant: 'multi-select',
+ filterSelectOptions: Object.values(LogLevel).map(level => globalize.translate(`LogLevel.${level}`))
},
...userColumn,
{
diff --git a/src/hooks/useLocale.tsx b/src/hooks/useLocale.tsx
new file mode 100644
index 0000000000..a7b8bc625c
--- /dev/null
+++ b/src/hooks/useLocale.tsx
@@ -0,0 +1,40 @@
+import type { Locale } from 'date-fns';
+import enUS from 'date-fns/locale/en-US';
+import { useEffect, useMemo, useState } from 'react';
+
+import { getDefaultLanguage, normalizeLocaleName } from 'lib/globalize';
+import { fetchLocale, normalizeLocale } from 'utils/dateFnsLocale';
+
+import { useUserSettings } from './useUserSettings';
+
+export function useLocale() {
+ const { dateTimeLocale: dateTimeSetting, language } = useUserSettings();
+ const [ dateFnsLocale, setDateFnsLocale ] = useState(enUS);
+
+ const locale = useMemo(() => (
+ normalizeLocaleName(language || getDefaultLanguage())
+ ), [ language ]);
+
+ const dateTimeLocale = useMemo(() => (
+ dateTimeSetting ? normalizeLocaleName(dateTimeSetting) : locale
+ ), [ dateTimeSetting, locale ]);
+
+ useEffect(() => {
+ const fetchDateFnsLocale = async () => {
+ try {
+ const dfLocale = await fetchLocale(normalizeLocale(dateTimeLocale));
+ setDateFnsLocale(dfLocale);
+ } catch (err) {
+ console.warn('[useLocale] failed to fetch dateFns locale', err);
+ }
+ };
+
+ void fetchDateFnsLocale();
+ }, [ dateTimeLocale ]);
+
+ return {
+ locale,
+ dateTimeLocale,
+ dateFnsLocale
+ };
+}
diff --git a/src/lib/globalize/index.js b/src/lib/globalize/index.js
index aad779a28d..fc76a209df 100644
--- a/src/lib/globalize/index.js
+++ b/src/lib/globalize/index.js
@@ -25,7 +25,7 @@ export function getCurrentDateTimeLocale() {
return currentDateTimeCulture;
}
-function getDefaultLanguage() {
+export function getDefaultLanguage() {
const culture = document.documentElement.getAttribute('data-culture');
if (culture) {
return culture;
@@ -127,7 +127,7 @@ function ensureTranslation(translationInfo, culture) {
});
}
-function normalizeLocaleName(culture) {
+export function normalizeLocaleName(culture) {
return culture.replace('_', '-').toLowerCase();
}
diff --git a/src/utils/dateFnsLocale.ts b/src/utils/dateFnsLocale.ts
index 425ac7b25c..e0e66b05f9 100644
--- a/src/utils/dateFnsLocale.ts
+++ b/src/utils/dateFnsLocale.ts
@@ -67,11 +67,21 @@ const DEFAULT_LOCALE = 'en-US';
let localeString = DEFAULT_LOCALE;
let locale = enUS;
+export function fetchLocale(localeName: string) {
+ return import(`date-fns/locale/${localeName}/index.js`);
+}
+
+export function normalizeLocale(localeName: string) {
+ return LOCALE_MAP[localeName]
+ || LOCALE_MAP[localeName.replace(/-.*/, '')]
+ || DEFAULT_LOCALE;
+}
+
export async function updateLocale(newLocale: string) {
console.debug('[dateFnsLocale] updating date-fns locale', newLocale);
- localeString = LOCALE_MAP[newLocale] || LOCALE_MAP[newLocale.replace(/-.*/, '')] || DEFAULT_LOCALE;
+ localeString = normalizeLocale(newLocale);
console.debug('[dateFnsLocale] mapped to date-fns locale', localeString);
- locale = await import(`date-fns/locale/${localeString}/index.js`);
+ locale = await fetchLocale(localeString);
}
export function getLocale() {