diff --git a/src/apps/dashboard/routes/_asyncRoutes.ts b/src/apps/dashboard/routes/_asyncRoutes.ts index c2e1e4a8d2..0e4d250b8c 100644 --- a/src/apps/dashboard/routes/_asyncRoutes.ts +++ b/src/apps/dashboard/routes/_asyncRoutes.ts @@ -5,6 +5,7 @@ export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [ { path: 'branding', type: AsyncRouteType.Dashboard }, { path: 'playback/trickplay', type: AsyncRouteType.Dashboard }, { path: 'plugins/:pluginId', page: 'plugins/plugin', type: AsyncRouteType.Dashboard }, + { path: 'logs', type: AsyncRouteType.Dashboard }, { path: 'users', type: AsyncRouteType.Dashboard }, { path: 'users/access', type: AsyncRouteType.Dashboard }, { path: 'users/add', type: AsyncRouteType.Dashboard }, diff --git a/src/apps/dashboard/routes/_legacyRoutes.ts b/src/apps/dashboard/routes/_legacyRoutes.ts index ae82c7e5f7..64911dc20a 100644 --- a/src/apps/dashboard/routes/_legacyRoutes.ts +++ b/src/apps/dashboard/routes/_legacyRoutes.ts @@ -49,12 +49,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [ controller: 'dashboard/encodingsettings', view: 'dashboard/encodingsettings.html' } - }, { - path: 'logs', - pageProps: { - controller: 'dashboard/logs', - view: 'dashboard/logs.html' - } }, { path: 'libraries/metadata', pageProps: { diff --git a/src/apps/dashboard/routes/logs.tsx b/src/apps/dashboard/routes/logs.tsx new file mode 100644 index 0000000000..76b1a15046 --- /dev/null +++ b/src/apps/dashboard/routes/logs.tsx @@ -0,0 +1,142 @@ +import React, { FormEvent, useCallback, useEffect, useRef, useState } from 'react'; +import type { LogFile } from '@jellyfin/sdk/lib/generated-client/models/log-file'; +import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api'; +import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api'; +import LogItem from 'components/dashboard/logs/LogItem'; +import Loading from 'components/loading/LoadingComponent'; +import Page from 'components/Page'; +import ButtonElement from 'elements/ButtonElement'; +import CheckBoxElement from 'elements/CheckBoxElement'; +import InputElement from 'elements/InputElement'; +import SectionTitleContainer from 'elements/SectionTitleContainer'; +import { useApi } from 'hooks/useApi'; +import globalize from 'lib/globalize'; +import toast from 'components/toast/toast'; + +const Logs = () => { + const { api } = useApi(); + const [ logs, setLogs ] = useState([]); + const [ loading, setLoading ] = useState(false); + const element = useRef(null); + + const loadLogs = useCallback(() => { + if (!api) return; + + return getSystemApi(api) + .getServerLogs() + .then(({ data }) => { + setLogs(data); + }); + }, [api]); + + const onSubmit = useCallback((e: FormEvent) => { + e.preventDefault(); + if (!api) return; + + const page = element.current; + + if (!page) return; + + getConfigurationApi(api) + .getConfiguration() + .then(({ data: config }) => { + config.EnableSlowResponseWarning = (page.querySelector('.chkSlowResponseWarning') as HTMLInputElement).checked; + config.SlowResponseThresholdMs = parseInt((page.querySelector('#txtSlowResponseWarning') as HTMLInputElement).value, 10); + getConfigurationApi(api) + .updateConfiguration({ serverConfiguration: config }) + .then(() => toast(globalize.translate('SettingsSaved'))) + .catch(err => { + console.error('[logs] failed to update configuration data', err); + }); + }) + .catch(err => { + console.error('[logs] failed to get configuration data', err); + }); + }, [api]); + + useEffect(() => { + if (!api) return; + + loadLogs()?.then(() => { + setLoading(false); + }).catch(err => { + console.error('[logs] An error occurred while fetching logs', err); + }); + + const page = element.current; + + if (!page || loading) return; + + getConfigurationApi(api) + .getConfiguration() + .then(({ data: config }) => { + if (config.EnableSlowResponseWarning) { + (page.querySelector('.chkSlowResponseWarning') as HTMLInputElement).checked = config.EnableSlowResponseWarning; + } + if (config.SlowResponseThresholdMs != null) { + (page.querySelector('#txtSlowResponseWarning') as HTMLInputElement).value = String(config.SlowResponseThresholdMs); + } + }) + .catch(err => { + console.error('[logs] An error occurred while fetching system config', err); + }); + }, [loading, api, loadLogs]); + + if (loading) { + return ; + } + + return ( + +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ {logs.map(log => { + return ; + })} +
+
+
+
+ ); +}; + +export default Logs; diff --git a/src/components/dashboard/logs/LogItem.tsx b/src/components/dashboard/logs/LogItem.tsx new file mode 100644 index 0000000000..be4c0193cc --- /dev/null +++ b/src/components/dashboard/logs/LogItem.tsx @@ -0,0 +1,40 @@ +import type { LogFile } from '@jellyfin/sdk/lib/generated-client/models/log-file'; +import LinkButton from 'elements/emby-button/LinkButton'; +import { useApi } from 'hooks/useApi'; +import React, { FunctionComponent } from 'react'; +import datetime from 'scripts/datetime'; + +type LogItemProps = { + logFile: LogFile; +}; + +const LogItem: FunctionComponent = ({ logFile }: LogItemProps) => { + const { api } = useApi(); + + const getLogFileUrl = () => { + if (!api) return; + + let url = api.basePath + '/System/Logs/Log'; + + url += '?name=' + encodeURIComponent(String(logFile.Name)); + url += '&api_key=' + encodeURIComponent(api.accessToken); + + return url; + }; + + const getDate = () => { + const date = datetime.parseISO8601Date(logFile.DateModified, true); + return datetime.toLocaleDateString(date) + ' ' + datetime.getDisplayTime(date); + }; + + return ( + +
+

{logFile.Name}

+
{getDate()}
+
+
+ ); +}; + +export default LogItem; diff --git a/src/controllers/dashboard/logs.html b/src/controllers/dashboard/logs.html deleted file mode 100644 index a23d7750f4..0000000000 --- a/src/controllers/dashboard/logs.html +++ /dev/null @@ -1,33 +0,0 @@ -
-
-
-
-
-
-

${TabLogs}

-
-
- -
-
- -
-
- -
-
-
-
- -
-
-
-
-
-
-
diff --git a/src/controllers/dashboard/logs.js b/src/controllers/dashboard/logs.js deleted file mode 100644 index 110d4be815..0000000000 --- a/src/controllers/dashboard/logs.js +++ /dev/null @@ -1,63 +0,0 @@ -import datetime from '../../scripts/datetime'; -import loading from '../../components/loading/loading'; -import globalize from '../../lib/globalize'; -import '../../elements/emby-button/emby-button'; -import '../../components/listview/listview.scss'; -import '../../styles/flexstyles.scss'; -import Dashboard from '../../utils/dashboard'; -import alert from '../../components/alert'; - -function onSubmit(event) { - event.preventDefault(); - loading.show(); - const form = this; - ApiClient.getServerConfiguration().then(function (config) { - config.EnableSlowResponseWarning = form.querySelector('#chkSlowResponseWarning').checked; - config.SlowResponseThresholdMs = form.querySelector('#txtSlowResponseWarning').value; - ApiClient.updateServerConfiguration(config).then(function() { - Dashboard.processServerConfigurationUpdateResult(); - }, function () { - alert(globalize.translate('ErrorDefault')); - Dashboard.processServerConfigurationUpdateResult(); - }); - }); - return false; -} - -export default function(view) { - view.querySelector('.logsForm').addEventListener('submit', onSubmit); - view.addEventListener('viewbeforeshow', function() { - loading.show(); - const apiClient = ApiClient; - apiClient.getJSON(apiClient.getUrl('System/Logs')).then(function(logs) { - let html = ''; - html += '
'; - html += logs.map(function(log) { - let logUrl = apiClient.getUrl('System/Logs/Log', { - name: log.Name - }); - logUrl += '&api_key=' + apiClient.accessToken(); - let logHtml = ''; - logHtml += ''; - logHtml += '
'; - logHtml += "

" + log.Name + '

'; - const date = datetime.parseISO8601Date(log.DateModified, true); - let text = datetime.toLocaleDateString(date); - text += ' ' + datetime.getDisplayTime(date); - logHtml += '
' + text + '
'; - logHtml += '
'; - logHtml += '
'; - return logHtml; - }).join(''); - html += '
'; - view.querySelector('.serverLogs').innerHTML = html; - }); - - apiClient.getServerConfiguration().then(function (config) { - view.querySelector('#chkSlowResponseWarning').checked = config.EnableSlowResponseWarning; - view.querySelector('#txtSlowResponseWarning').value = config.SlowResponseThresholdMs; - }); - - loading.hide(); - }); -}