mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-17 19:08:18 -07:00
Merge pull request #3733 from grafixeyehero/Migrate-to-react-router
Migrate to react router
This commit is contained in:
commit
f0d29e8175
@ -1,5 +1,5 @@
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
|
||||
import alert from './alert';
|
||||
import { appRouter } from './appRouter';
|
||||
@ -33,7 +33,6 @@ type ConnectionRequiredProps = {
|
||||
* If a condition fails, this component will navigate to the appropriate page.
|
||||
*/
|
||||
const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
|
||||
children,
|
||||
isAdminRequired = false,
|
||||
isUserRequired = true
|
||||
}) => {
|
||||
@ -162,7 +161,9 @@ const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<>{children}</>
|
||||
<div className='skinBody'>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,7 @@ type PageProps = {
|
||||
id: string, // id is required for libraryMenu
|
||||
title?: string,
|
||||
isBackButtonEnabled?: boolean,
|
||||
isMenuButtonEnabled?: boolean,
|
||||
isNowPlayingBarEnabled?: boolean,
|
||||
isThemeMediaSupported?: boolean
|
||||
};
|
||||
@ -20,6 +21,7 @@ const Page: FunctionComponent<PageProps & HTMLAttributes<HTMLDivElement>> = ({
|
||||
className = '',
|
||||
title,
|
||||
isBackButtonEnabled = true,
|
||||
isMenuButtonEnabled = false,
|
||||
isNowPlayingBarEnabled = true,
|
||||
isThemeMediaSupported = false
|
||||
}) => {
|
||||
@ -60,6 +62,7 @@ const Page: FunctionComponent<PageProps & HTMLAttributes<HTMLDivElement>> = ({
|
||||
className={`page ${className}`}
|
||||
data-title={title}
|
||||
data-backbutton={`${isBackButtonEnabled}`}
|
||||
data-menubutton={`${isMenuButtonEnabled}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
@ -9,7 +9,6 @@ import loading from './loading/loading';
|
||||
import viewManager from './viewManager/viewManager';
|
||||
import ServerConnections from './ServerConnections';
|
||||
import alert from './alert';
|
||||
import reactControllerFactory from './reactControllerFactory';
|
||||
|
||||
export const history = createHashHistory();
|
||||
|
||||
@ -264,9 +263,7 @@ class AppRouter {
|
||||
this.#sendRouteToViewManager(ctx, next, route, controllerFactory);
|
||||
};
|
||||
|
||||
if (route.pageComponent) {
|
||||
onInitComplete(reactControllerFactory);
|
||||
} else if (route.controller) {
|
||||
if (route.controller) {
|
||||
import('../controllers/' + route.controller).then(onInitComplete);
|
||||
} else {
|
||||
onInitComplete();
|
||||
@ -293,7 +290,6 @@ class AppRouter {
|
||||
fullscreen: route.fullscreen,
|
||||
controllerFactory: controllerFactory,
|
||||
options: {
|
||||
pageComponent: route.pageComponent,
|
||||
supportsThemeMedia: route.supportsThemeMedia || false,
|
||||
enableMediaControl: route.enableMediaControl !== false
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import CheckBoxElement from './CheckBoxElement';
|
||||
import CheckBoxElement from '../../../elements/CheckBoxElement';
|
||||
|
||||
type IProps = {
|
||||
containerClassName?: string;
|
||||
@ -18,7 +18,11 @@ const AccessContainer: FunctionComponent<IProps> = ({containerClassName, headerT
|
||||
return (
|
||||
<div className={containerClassName}>
|
||||
<h2>{globalize.translate(headerTitle)}</h2>
|
||||
<CheckBoxElement labelClassName='checkboxContainer' type='checkbox' className={checkBoxClassName} title={checkBoxTitle} />
|
||||
<CheckBoxElement
|
||||
labelClassName='checkboxContainer'
|
||||
className={checkBoxClassName}
|
||||
title={checkBoxTitle}
|
||||
/>
|
||||
<div className={listContainerClassName}>
|
||||
<div className={accessClassName}>
|
||||
<h3 className='checkboxListLabel'>
|
||||
|
@ -1,17 +1,7 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import datetime from '../../../scripts/datetime';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createButtonElement = (index: number) => ({
|
||||
__html: `<button
|
||||
type='button'
|
||||
is='paper-icon-button-light'
|
||||
class='btnDelete listItemButton'
|
||||
data-index='${index}'
|
||||
>
|
||||
<span class='material-icons delete' aria-hidden='true' />
|
||||
</button>`
|
||||
});
|
||||
import IconButtonElement from '../../../elements/IconButtonElement';
|
||||
|
||||
type IProps = {
|
||||
index: number;
|
||||
@ -48,8 +38,12 @@ const AccessScheduleList: FunctionComponent<IProps> = ({index, DayOfWeek, StartH
|
||||
{getDisplayTime(StartHour) + ' - ' + getDisplayTime(EndHour)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement(index)}
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnDelete listItemButton'
|
||||
title='Delete'
|
||||
icon='delete'
|
||||
dataIndex={index}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,15 +1,5 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
const createButtonElement = (tag?: string) => ({
|
||||
__html: `<button
|
||||
type='button'
|
||||
is='paper-icon-button-light'
|
||||
class='blockedTag btnDeleteTag listItemButton'
|
||||
data-tag='${tag}'
|
||||
>
|
||||
<span class='material-icons delete' aria-hidden='true' />
|
||||
</button>`
|
||||
});
|
||||
import IconButtonElement from '../../../elements/IconButtonElement';
|
||||
|
||||
type IProps = {
|
||||
tag?: string;
|
||||
@ -24,11 +14,14 @@ const BlockedTagList: FunctionComponent<IProps> = ({tag}: IProps) => {
|
||||
{tag}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement(tag)}
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='blockedTag btnDeleteTag listItemButton'
|
||||
title='Delete'
|
||||
icon='delete'
|
||||
dataTag={tag}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,32 +0,0 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createButtonElement = ({ type, className, title }: { type?: string, className?: string, title?: string }) => ({
|
||||
__html: `<button
|
||||
is="emby-button"
|
||||
type="${type}"
|
||||
class="${className}"
|
||||
>
|
||||
<span>${title}</span>
|
||||
</button>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
type?: string;
|
||||
className?: string;
|
||||
title?: string
|
||||
}
|
||||
|
||||
const ButtonElement: FunctionComponent<IProps> = ({ type, className, title }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement({
|
||||
type: type,
|
||||
className: className,
|
||||
title: globalize.translate(title)
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonElement;
|
@ -1,36 +0,0 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createCheckBoxElement = ({ labelClassName, type, className, title }: { labelClassName?: string, type?: string, className?: string, title?: string }) => ({
|
||||
__html: `<label class="${labelClassName}">
|
||||
<input
|
||||
is="emby-checkbox"
|
||||
type="${type}"
|
||||
class="${className}"
|
||||
/>
|
||||
<span>${title}</span>
|
||||
</label>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
labelClassName?: string;
|
||||
type?: string;
|
||||
className?: string;
|
||||
title?: string
|
||||
}
|
||||
|
||||
const CheckBoxElement: FunctionComponent<IProps> = ({ labelClassName, type, className, title }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
className='sectioncheckbox'
|
||||
dangerouslySetInnerHTML={createCheckBoxElement({
|
||||
labelClassName: labelClassName ? labelClassName : '',
|
||||
type: type,
|
||||
className: className,
|
||||
title: globalize.translate(title)
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckBoxElement;
|
@ -1,41 +0,0 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
type IProps = {
|
||||
className?: string;
|
||||
Name?: string;
|
||||
Id?: string;
|
||||
ItemType?: string;
|
||||
AppName?: string;
|
||||
checkedAttribute?: string;
|
||||
}
|
||||
|
||||
const createCheckBoxElement = ({className, Name, dataAttributes, AppName, checkedAttribute}: {className?: string, Name?: string, dataAttributes?: string, AppName?: string, checkedAttribute?: string}) => ({
|
||||
__html: `<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
is="emby-checkbox"
|
||||
class="${className}"
|
||||
${dataAttributes} ${checkedAttribute}
|
||||
/>
|
||||
<span>${escapeHtml(Name || '')} ${AppName}</span>
|
||||
</label>`
|
||||
});
|
||||
|
||||
const CheckBoxListItem: FunctionComponent<IProps> = ({className, Name, Id, ItemType, AppName, checkedAttribute}: IProps) => {
|
||||
return (
|
||||
<div
|
||||
className='sectioncheckbox'
|
||||
dangerouslySetInnerHTML={createCheckBoxElement({
|
||||
className: className,
|
||||
Name: Name,
|
||||
dataAttributes: ItemType ? `data-itemtype='${ItemType}'` : `data-id='${Id}'`,
|
||||
AppName: AppName ? `- ${AppName}` : '',
|
||||
checkedAttribute: checkedAttribute
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckBoxListItem;
|
||||
|
@ -1,34 +0,0 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
type IProps = {
|
||||
title: string;
|
||||
className?: string;
|
||||
icon: string,
|
||||
}
|
||||
|
||||
const createButtonElement = ({ className, title, icon }: { className?: string, title: string, icon: string }) => ({
|
||||
__html: `<button
|
||||
is="emby-button"
|
||||
type="button"
|
||||
class="${className}"
|
||||
style="margin-left:1em;"
|
||||
title="${title}"
|
||||
>
|
||||
<span class="material-icons ${icon}" aria-hidden="true"></span>
|
||||
</button>`
|
||||
});
|
||||
|
||||
const SectionTitleButtonElement: FunctionComponent<IProps> = ({ className, title, icon }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement({
|
||||
className: className,
|
||||
title: globalize.translate(title),
|
||||
icon: icon
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionTitleButtonElement;
|
@ -1,35 +0,0 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import SectionTitleButtonElement from './SectionTitleButtonElement';
|
||||
import SectionTitleLinkElement from './SectionTitleLinkElement';
|
||||
|
||||
type IProps = {
|
||||
title: string;
|
||||
isBtnVisible?: boolean;
|
||||
titleLink?: string;
|
||||
}
|
||||
|
||||
const SectionTitleContainer: FunctionComponent<IProps> = ({title, isBtnVisible = false, titleLink}: IProps) => {
|
||||
return (
|
||||
<div className='verticalSection'>
|
||||
<div className='sectionTitleContainer flex align-items-center'>
|
||||
<h2 className='sectionTitle'>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
{isBtnVisible && <SectionTitleButtonElement
|
||||
className='fab btnAddUser submit sectionTitleButton'
|
||||
title='ButtonAddUser'
|
||||
icon='add'
|
||||
/>}
|
||||
|
||||
<SectionTitleLinkElement
|
||||
className='raised button-alt headerHelpButton'
|
||||
title='Help'
|
||||
url={titleLink}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionTitleContainer;
|
@ -1,44 +0,0 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createSelectElement = ({ className, label, option }: { className?: string, label: string, option: string[] }) => ({
|
||||
__html: `<select
|
||||
class="${className}"
|
||||
is="emby-select"
|
||||
label="${label}"
|
||||
>
|
||||
${option}
|
||||
</select>`
|
||||
});
|
||||
|
||||
type ProvidersArr = {
|
||||
Name?: string;
|
||||
Id?: string;
|
||||
}
|
||||
|
||||
type IProps = {
|
||||
className?: string;
|
||||
label?: string;
|
||||
currentProviderId: string;
|
||||
providers: ProvidersArr[]
|
||||
}
|
||||
|
||||
const SelectElement: FunctionComponent<IProps> = ({ className, label, currentProviderId, providers }: IProps) => {
|
||||
const renderOption = providers.map((provider) => {
|
||||
const selected = provider.Id === currentProviderId || providers.length < 2 ? ' selected' : '';
|
||||
return '<option value="' + provider.Id + '"' + selected + '>' + escapeHtml(provider.Name) + '</option>';
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createSelectElement({
|
||||
className: className,
|
||||
label: globalize.translate(label),
|
||||
option: renderOption
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectElement;
|
@ -1,47 +0,0 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createSelectElement = ({ className, label, option }: { className?: string, label: string, option: string }) => ({
|
||||
__html: `<select
|
||||
class="${className}"
|
||||
is="emby-select"
|
||||
label="${label}"
|
||||
>
|
||||
<option value=''></option>
|
||||
${option}
|
||||
</select>`
|
||||
});
|
||||
|
||||
type RatingsArr = {
|
||||
Name: string;
|
||||
Value: number;
|
||||
}
|
||||
|
||||
type IProps = {
|
||||
className?: string;
|
||||
label?: string;
|
||||
parentalRatings: RatingsArr[];
|
||||
}
|
||||
|
||||
const SelectMaxParentalRating: FunctionComponent<IProps> = ({ className, label, parentalRatings }: IProps) => {
|
||||
const renderOption = () => {
|
||||
let content = '';
|
||||
for (const rating of parentalRatings) {
|
||||
content += `<option value='${rating.Value}'>${escapeHtml(rating.Name)}</option>`;
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createSelectElement({
|
||||
className: className,
|
||||
label: globalize.translate(label),
|
||||
option: renderOption()
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectMaxParentalRating;
|
@ -1,35 +0,0 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
const createSelectElement = ({ className, id, label }: { className?: string, id?: string, label: string }) => ({
|
||||
__html: `<select
|
||||
class="${className}"
|
||||
is="emby-select"
|
||||
id="${id}"
|
||||
label="${label}"
|
||||
>
|
||||
<option value='CreateAndJoinGroups'>${globalize.translate('LabelSyncPlayAccessCreateAndJoinGroups')}</option>
|
||||
<option value='JoinGroups'>${globalize.translate('LabelSyncPlayAccessJoinGroups')}</option>
|
||||
<option value='None'>${globalize.translate('LabelSyncPlayAccessNone')}</option>
|
||||
</select>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
className?: string;
|
||||
id?: string;
|
||||
label?: string
|
||||
}
|
||||
|
||||
const SelectSyncPlayAccessElement: FunctionComponent<IProps> = ({ className, id, label }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createSelectElement({
|
||||
className: className,
|
||||
id: id,
|
||||
label: globalize.translate(label)
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectSyncPlayAccessElement;
|
@ -4,6 +4,8 @@ import { formatDistanceToNow } from 'date-fns';
|
||||
import { localeWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import cardBuilder from '../../cardbuilder/cardBuilder';
|
||||
import IconButtonElement from '../../../elements/IconButtonElement';
|
||||
import escapeHTML from 'escape-html';
|
||||
|
||||
const createLinkElement = ({ user, renderImgUrl }: { user: UserDto, renderImgUrl: string }) => ({
|
||||
__html: `<a
|
||||
@ -15,16 +17,6 @@ const createLinkElement = ({ user, renderImgUrl }: { user: UserDto, renderImgUrl
|
||||
</a>`
|
||||
});
|
||||
|
||||
const createButtonElement = () => ({
|
||||
__html: `<button
|
||||
is="paper-icon-button-light"
|
||||
type="button"
|
||||
class="btnUserMenu flex-shrink-zero"
|
||||
>
|
||||
<span class="material-icons more_vert" aria-hidden="true"></span>
|
||||
</button>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
user?: UserDto;
|
||||
}
|
||||
@ -81,16 +73,20 @@ const UserCardBox: FunctionComponent<IProps> = ({ user = {} }: IProps) => {
|
||||
/>
|
||||
</div>
|
||||
<div className='cardFooter visualCardBox-cardFooter'>
|
||||
<div className='cardText flex align-items-center'>
|
||||
<div className='flex-grow' style={{overflow: 'hidden', textOverflow: 'ellipsis'}}>
|
||||
{user.Name}
|
||||
</div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement()}
|
||||
<div
|
||||
style={{textAlign: 'right', float: 'right', paddingTop: '5px'}}
|
||||
>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnUserMenu flex-shrink-zero'
|
||||
icon='more_vert'
|
||||
/>
|
||||
</div>
|
||||
<div className='cardText'>
|
||||
<span>{escapeHTML(user.Name)}</span>
|
||||
</div>
|
||||
<div className='cardText cardText-secondary'>
|
||||
{lastSeen != '' ? lastSeen : ''}
|
||||
<span>{lastSeen != '' ? lastSeen : ''}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,9 +6,9 @@ import LibraryMenu from '../../../scripts/libraryMenu';
|
||||
import confirm from '../../confirm/confirm';
|
||||
import loading from '../../loading/loading';
|
||||
import toast from '../../toast/toast';
|
||||
import ButtonElement from './ButtonElement';
|
||||
import CheckBoxElement from './CheckBoxElement';
|
||||
import InputElement from './InputElement';
|
||||
import ButtonElement from '../../../elements/ButtonElement';
|
||||
import CheckBoxElement from '../../../elements/CheckBoxElement';
|
||||
import InputElement from '../../../elements/InputElement';
|
||||
|
||||
type IProps = {
|
||||
userId: string;
|
||||
@ -40,11 +40,11 @@ const UserPasswordForm: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
let showLocalAccessSection = false;
|
||||
|
||||
if (user.HasConfiguredPassword) {
|
||||
(page.querySelector('.btnResetPassword') as HTMLDivElement).classList.remove('hide');
|
||||
(page.querySelector('#btnResetPassword') as HTMLDivElement).classList.remove('hide');
|
||||
(page.querySelector('#fldCurrentPassword') as HTMLDivElement).classList.remove('hide');
|
||||
showLocalAccessSection = true;
|
||||
} else {
|
||||
(page.querySelector('.btnResetPassword') as HTMLDivElement).classList.add('hide');
|
||||
(page.querySelector('#btnResetPassword') as HTMLDivElement).classList.add('hide');
|
||||
(page.querySelector('#fldCurrentPassword') as HTMLDivElement).classList.add('hide');
|
||||
}
|
||||
|
||||
@ -65,11 +65,11 @@ const UserPasswordForm: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
|
||||
if (user.HasConfiguredEasyPassword) {
|
||||
txtEasyPassword.placeholder = '******';
|
||||
(page.querySelector('.btnResetEasyPassword') as HTMLDivElement).classList.remove('hide');
|
||||
(page.querySelector('#btnResetEasyPassword') as HTMLDivElement).classList.remove('hide');
|
||||
} else {
|
||||
txtEasyPassword.removeAttribute('placeholder');
|
||||
txtEasyPassword.placeholder = '';
|
||||
(page.querySelector('.btnResetEasyPassword') as HTMLDivElement).classList.add('hide');
|
||||
(page.querySelector('#btnResetEasyPassword') as HTMLDivElement).classList.add('hide');
|
||||
}
|
||||
|
||||
const chkEnableLocalEasyPassword = page.querySelector('.chkEnableLocalEasyPassword') as HTMLInputElement;
|
||||
@ -206,8 +206,8 @@ const UserPasswordForm: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
(page.querySelector('.updatePasswordForm') as HTMLFormElement).addEventListener('submit', onSubmit);
|
||||
(page.querySelector('.localAccessForm') as HTMLFormElement).addEventListener('submit', onLocalAccessSubmit);
|
||||
|
||||
(page.querySelector('.btnResetEasyPassword') as HTMLButtonElement).addEventListener('click', resetEasyPassword);
|
||||
(page.querySelector('.btnResetPassword') as HTMLButtonElement).addEventListener('click', resetPassword);
|
||||
(page.querySelector('#btnResetEasyPassword') as HTMLButtonElement).addEventListener('click', resetEasyPassword);
|
||||
(page.querySelector('#btnResetPassword') as HTMLButtonElement).addEventListener('click', resetPassword);
|
||||
}, [loadUser, userId]);
|
||||
|
||||
return (
|
||||
@ -250,7 +250,8 @@ const UserPasswordForm: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
/>
|
||||
<ButtonElement
|
||||
type='button'
|
||||
className='raised btnResetPassword button-cancel block hide'
|
||||
id='btnResetPassword'
|
||||
className='raised button-cancel block hide'
|
||||
title='ResetPassword'
|
||||
/>
|
||||
</div>
|
||||
@ -281,7 +282,6 @@ const UserPasswordForm: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
<br />
|
||||
<div className='checkboxContainer checkboxContainer-withDescription'>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableLocalEasyPassword'
|
||||
title='LabelInNetworkSignInWithEasyPassword'
|
||||
/>
|
||||
@ -297,7 +297,8 @@ const UserPasswordForm: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
/>
|
||||
<ButtonElement
|
||||
type='button'
|
||||
className='raised btnResetEasyPassword button-cancel block hide'
|
||||
id='btnResetEasyPassword'
|
||||
className='raised button-cancel block hide'
|
||||
title='ButtonResetEasyPassword'
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,17 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
export default (view, params, { detail }) => {
|
||||
if (detail.options?.pageComponent) {
|
||||
// Fetch and render the page component to the view
|
||||
import(/* webpackChunkName: "[request]" */ `./pages/${detail.options.pageComponent}`)
|
||||
.then(({ default: component }) => {
|
||||
ReactDOM.render(React.createElement(component, params), view);
|
||||
});
|
||||
|
||||
// Unmount component when view is destroyed
|
||||
view.addEventListener('viewdestroy', () => {
|
||||
ReactDOM.unmountComponentAtNode(view);
|
||||
});
|
||||
}
|
||||
};
|
@ -21,9 +21,9 @@ viewContainer.setOnBeforeChange(function (newView, isRestored, options) {
|
||||
newView.initComplete = true;
|
||||
|
||||
if (typeof options.controllerFactory === 'function') {
|
||||
new options.controllerFactory(newView, eventDetail.detail.params, eventDetail);
|
||||
new options.controllerFactory(newView, eventDetail.detail.params);
|
||||
} else if (options.controllerFactory && typeof options.controllerFactory.default === 'function') {
|
||||
new options.controllerFactory.default(newView, eventDetail.detail.params, eventDetail);
|
||||
new options.controllerFactory.default(newView, eventDetail.detail.params);
|
||||
}
|
||||
|
||||
if (!options.controllerFactory || dispatchPageEvents) {
|
||||
|
@ -1,3 +0,0 @@
|
||||
<div id="editUserPage" data-role="page" class="page type-interior">
|
||||
|
||||
</div>
|
@ -1,3 +0,0 @@
|
||||
<div id="userLibraryAccessPage" data-role="page" class="page type-interior">
|
||||
|
||||
</div>
|
@ -1,3 +0,0 @@
|
||||
<div id="newUserPage" data-role="page" class="page type-interior">
|
||||
|
||||
</div>
|
@ -1,3 +0,0 @@
|
||||
<div id="userParentalControlPage" data-role="page" class="page type-interior">
|
||||
|
||||
</div>
|
@ -1,3 +0,0 @@
|
||||
<div id="userPasswordPage" data-role="page" class="page type-interior userPasswordPage">
|
||||
|
||||
</div>
|
@ -1,3 +0,0 @@
|
||||
<div id="userProfilesPage" data-role="page" class="page type-interior userProfilesPage fullWidthContent">
|
||||
|
||||
</div>
|
@ -3,7 +3,7 @@
|
||||
<div class="readOnlyContent" style="margin: 0 auto;">
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<h2 class="sectionTitle headerUsername" style="padding-left:.25em;"></h2>
|
||||
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkMyProfile listItem-border">
|
||||
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkUserProfile listItem-border">
|
||||
<div class="listItem">
|
||||
<span class="material-icons listItemIcon listItemIcon-transparent person" aria-hidden="true"></span>
|
||||
<div class="listItemBody">
|
||||
|
@ -26,7 +26,7 @@ export default function (view, params) {
|
||||
const userId = params.userId || Dashboard.getCurrentUserId();
|
||||
const page = this;
|
||||
|
||||
page.querySelector('.lnkMyProfile').setAttribute('href', '#/myprofile.html?userId=' + userId);
|
||||
page.querySelector('.lnkUserProfile').setAttribute('href', '#/userprofile.html?userId=' + userId);
|
||||
page.querySelector('.lnkDisplayPreferences').setAttribute('href', '#/mypreferencesdisplay.html?userId=' + userId);
|
||||
page.querySelector('.lnkHomePreferences').setAttribute('href', '#/mypreferenceshome.html?userId=' + userId);
|
||||
page.querySelector('.lnkPlaybackPreferences').setAttribute('href', '#/mypreferencesplayback.html?userId=' + userId);
|
||||
|
@ -1,3 +0,0 @@
|
||||
<div id="userProfilePage" data-role="page" class="page libraryPage userPreferencesPage userPasswordPage noSecondaryNavPage" data-title="${Profile}" data-menubutton="false">
|
||||
|
||||
</div>
|
41
src/elements/ButtonElement.tsx
Normal file
41
src/elements/ButtonElement.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
const createButtonElement = ({ type, id, className, title, leftIcon, rightIcon }: IProps) => ({
|
||||
__html: `<button
|
||||
is="emby-button"
|
||||
type="${type}"
|
||||
${id}
|
||||
class="${className}"
|
||||
>
|
||||
${leftIcon}
|
||||
<span>${title}</span>
|
||||
${rightIcon}
|
||||
</button>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
type?: string;
|
||||
id?: string;
|
||||
className?: string;
|
||||
title?: string;
|
||||
leftIcon?: string;
|
||||
rightIcon?: string;
|
||||
}
|
||||
|
||||
const ButtonElement: FunctionComponent<IProps> = ({ type, id, className, title, leftIcon, rightIcon }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement({
|
||||
type: type,
|
||||
id: id ? `id="${id}"` : '',
|
||||
className: className,
|
||||
title: globalize.translate(title),
|
||||
leftIcon: leftIcon ? `<span class="material-icons ${leftIcon}" aria-hidden="true"></span>` : '',
|
||||
rightIcon: rightIcon ? `<span class="material-icons ${rightIcon}" aria-hidden="true"></span>` : ''
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonElement;
|
57
src/elements/CheckBoxElement.tsx
Normal file
57
src/elements/CheckBoxElement.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import escapeHTML from 'escape-html';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
const createCheckBoxElement = ({ labelClassName, className, id, dataFilter, dataItemType, dataId, checkedAttribute, renderContent }: { labelClassName?: string, type?: string, className?: string, id?: string, dataFilter?: string, dataItemType?: string, dataId?: string, checkedAttribute?: string, renderContent?: string }) => ({
|
||||
__html: `<label ${labelClassName}>
|
||||
<input
|
||||
is="emby-checkbox"
|
||||
type="checkbox"
|
||||
class="${className}"
|
||||
${id}
|
||||
${dataFilter}
|
||||
${dataItemType}
|
||||
${dataId}
|
||||
${checkedAttribute}
|
||||
/>
|
||||
${renderContent}
|
||||
</label>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
labelClassName?: string;
|
||||
className?: string;
|
||||
elementId?: string;
|
||||
dataFilter?: string;
|
||||
itemType?: string;
|
||||
itemId?: string;
|
||||
itemAppName?: string;
|
||||
itemCheckedAttribute?: string;
|
||||
itemName?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
const CheckBoxElement: FunctionComponent<IProps> = ({ labelClassName, className, elementId, dataFilter, itemType, itemId, itemAppName, itemCheckedAttribute, itemName, title }: IProps) => {
|
||||
const appName = itemAppName ? `- ${itemAppName}` : '';
|
||||
const renderContent = itemName ?
|
||||
`<span>${escapeHTML(itemName || '')} ${appName}</span>` :
|
||||
`<span>${globalize.translate(title)}</span>`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className='sectioncheckbox'
|
||||
dangerouslySetInnerHTML={createCheckBoxElement({
|
||||
labelClassName: labelClassName ? `class='${labelClassName}'` : '',
|
||||
className: className,
|
||||
id: elementId ? `id='${elementId}'` : '',
|
||||
dataFilter: dataFilter ? `data-filter='${dataFilter}'` : '',
|
||||
dataItemType: itemType ? `data-itemtype='${itemType}'` : '',
|
||||
dataId: itemId ? `data-id='${itemId}'` : '',
|
||||
checkedAttribute: itemCheckedAttribute ? itemCheckedAttribute : '',
|
||||
renderContent: renderContent
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckBoxElement;
|
47
src/elements/IconButtonElement.tsx
Normal file
47
src/elements/IconButtonElement.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
type IProps = {
|
||||
is?: string;
|
||||
id?: string;
|
||||
title?: string;
|
||||
className?: string;
|
||||
icon?: string,
|
||||
dataIndex?: string | number;
|
||||
dataTag?: string | number;
|
||||
dataProfileid?: string | number;
|
||||
}
|
||||
|
||||
const createIconButtonElement = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid }: IProps) => ({
|
||||
__html: `<button
|
||||
is="${is}"
|
||||
type="button"
|
||||
${id}
|
||||
class="${className}"
|
||||
${title}
|
||||
${dataIndex}
|
||||
${dataTag}
|
||||
${dataProfileid}
|
||||
>
|
||||
<span class="material-icons ${icon}" aria-hidden="true"></span>
|
||||
</button>`
|
||||
});
|
||||
|
||||
const IconButtonElement: FunctionComponent<IProps> = ({ is, id, className, title, icon, dataIndex, dataTag, dataProfileid }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createIconButtonElement({
|
||||
is: is,
|
||||
id: id ? `id="${id}"` : '',
|
||||
className: className,
|
||||
title: title ? `title="${globalize.translate(title)}"` : '',
|
||||
icon: icon,
|
||||
dataIndex: dataIndex ? `data-index="${dataIndex}"` : '',
|
||||
dataTag: dataTag ? `data-tag="${dataTag}"` : '',
|
||||
dataProfileid: dataProfileid ? `data-profileid="${dataProfileid}"` : ''
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default IconButtonElement;
|
@ -1,5 +1,5 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
const createInputElement = ({ type, id, label, options }: { type?: string, id?: string, label?: string, options?: string }) => ({
|
||||
__html: `<input
|
41
src/elements/SectionTitleContainer.tsx
Normal file
41
src/elements/SectionTitleContainer.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import IconButtonElement from './IconButtonElement';
|
||||
import SectionTitleLinkElement from './SectionTitleLinkElement';
|
||||
|
||||
type IProps = {
|
||||
SectionClassName?: string;
|
||||
title?: string;
|
||||
isBtnVisible?: boolean;
|
||||
btnId?: string;
|
||||
btnClassName?: string;
|
||||
btnTitle?: string;
|
||||
btnIcon?: string;
|
||||
isLinkVisible?: boolean;
|
||||
url?: string;
|
||||
}
|
||||
const SectionTitleContainer: FunctionComponent<IProps> = ({SectionClassName, title, isBtnVisible = false, btnId, btnClassName, btnTitle, btnIcon, isLinkVisible = true, url}: IProps) => {
|
||||
return (
|
||||
<div className={`${SectionClassName} sectionTitleContainer flex align-items-center`}>
|
||||
<h2 className='sectionTitle'>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
{isBtnVisible && <IconButtonElement
|
||||
is='emby-button'
|
||||
id={btnId}
|
||||
className={btnClassName}
|
||||
title={btnTitle}
|
||||
icon={btnIcon}
|
||||
/>}
|
||||
|
||||
{isLinkVisible && <SectionTitleLinkElement
|
||||
className='raised button-alt headerHelpButton'
|
||||
title='Help'
|
||||
url={url}
|
||||
/>}
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionTitleContainer;
|
@ -1,5 +1,5 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
const createLinkElement = ({ className, title, href }: { className?: string, title?: string, href?: string }) => ({
|
||||
__html: `<a
|
38
src/elements/SelectElement.tsx
Normal file
38
src/elements/SelectElement.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
const createSelectElement = ({ name, id, required, label, option }: { name?: string, id?: string, required?: string, label?: string, option?: React.ReactNode }) => ({
|
||||
__html: `<select
|
||||
is="emby-select"
|
||||
${name}
|
||||
id="${id}"
|
||||
${required}
|
||||
label="${label}"
|
||||
>
|
||||
${option}
|
||||
</select>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
name?: string;
|
||||
id?: string;
|
||||
required?: string;
|
||||
label?: string;
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const SelectElement: FunctionComponent<IProps> = ({ name, id, required, label, children }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createSelectElement({
|
||||
name: name ? `name='${name}'` : '',
|
||||
id: id,
|
||||
required: required ? `required='${required}'` : '',
|
||||
label: globalize.translate(label),
|
||||
option: children
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectElement;
|
@ -2,19 +2,34 @@ import React from 'react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
|
||||
import ConnectionRequired from '../components/ConnectionRequired';
|
||||
import SearchPage from './search';
|
||||
import UserNew from './user/usernew';
|
||||
import Search from './search';
|
||||
import UserEdit from './user/useredit';
|
||||
import UserLibraryAccess from './user/userlibraryaccess';
|
||||
import UserParentalControl from './user/userparentalcontrol';
|
||||
import UserPassword from './user/userpassword';
|
||||
import UserProfile from './user/userprofile';
|
||||
import UserProfiles from './user/userprofiles';
|
||||
|
||||
const AppRoutes = () => (
|
||||
<Routes>
|
||||
<Route path='/'>
|
||||
<Route
|
||||
path='search.html'
|
||||
element={
|
||||
<ConnectionRequired>
|
||||
<SearchPage />
|
||||
</ConnectionRequired>
|
||||
}
|
||||
/>
|
||||
{/* User routes */}
|
||||
<Route path='/' element={<ConnectionRequired />}>
|
||||
<Route path='search.html' element={<Search />} />
|
||||
<Route path='userprofile.html' element={<UserProfile />} />
|
||||
</Route>
|
||||
|
||||
{/* Admin routes */}
|
||||
<Route path='/' element={<ConnectionRequired isAdminRequired={true} />}>
|
||||
<Route path='usernew.html' element={<UserNew />} />
|
||||
<Route path='userprofiles.html' element={<UserProfiles />} />
|
||||
<Route path='useredit.html' element={<UserEdit />} />
|
||||
<Route path='userlibraryaccess.html' element={<UserLibraryAccess />} />
|
||||
<Route path='userparentalcontrol.html' element={<UserParentalControl />} />
|
||||
<Route path='userpassword.html' element={<UserPassword />} />
|
||||
</Route>
|
||||
|
||||
{/* Suppress warnings for unhandled routes */}
|
||||
<Route path='*' element={null} />
|
||||
</Route>
|
||||
|
@ -8,7 +8,7 @@ import SearchSuggestions from '../components/search/SearchSuggestions';
|
||||
import LiveTVSearchResults from '../components/search/LiveTVSearchResults';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
const SearchPage: FunctionComponent = () => {
|
||||
const Search: FunctionComponent = () => {
|
||||
const [ query, setQuery ] = useState<string>();
|
||||
const [ searchParams ] = useSearchParams();
|
||||
|
||||
@ -41,4 +41,4 @@ const SearchPage: FunctionComponent = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchPage;
|
||||
export default Search;
|
||||
|
@ -3,30 +3,33 @@ import React, { FunctionComponent, useCallback, useEffect, useState, useRef } fr
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import LibraryMenu from '../../scripts/libraryMenu';
|
||||
import ButtonElement from '../dashboard/users/ButtonElement';
|
||||
import CheckBoxElement from '../dashboard/users/CheckBoxElement';
|
||||
import CheckBoxListItem from '../dashboard/users/CheckBoxListItem';
|
||||
import InputElement from '../dashboard/users/InputElement';
|
||||
import LinkEditUserPreferences from '../dashboard/users/LinkEditUserPreferences';
|
||||
import SectionTitleContainer from '../dashboard/users/SectionTitleContainer';
|
||||
import SelectElement from '../dashboard/users/SelectElement';
|
||||
import SelectSyncPlayAccessElement from '../dashboard/users/SelectSyncPlayAccessElement';
|
||||
import SectionTabs from '../dashboard/users/SectionTabs';
|
||||
import loading from '../loading/loading';
|
||||
import toast from '../toast/toast';
|
||||
import ButtonElement from '../../elements/ButtonElement';
|
||||
import CheckBoxElement from '../../elements/CheckBoxElement';
|
||||
import InputElement from '../../elements/InputElement';
|
||||
import LinkEditUserPreferences from '../../components/dashboard/users/LinkEditUserPreferences';
|
||||
import SectionTitleContainer from '../../elements/SectionTitleContainer';
|
||||
import SectionTabs from '../../components/dashboard/users/SectionTabs';
|
||||
import loading from '../../components/loading/loading';
|
||||
import toast from '../../components/toast/toast';
|
||||
import { getParameterByName } from '../../utils/url';
|
||||
import escapeHTML from 'escape-html';
|
||||
import SelectElement from '../../elements/SelectElement';
|
||||
import Page from '../../components/Page';
|
||||
|
||||
type ItemsArr = {
|
||||
Name?: string;
|
||||
Id?: string;
|
||||
type ResetProvider = AuthProvider & {
|
||||
checkedAttribute: string
|
||||
}
|
||||
|
||||
const UserEditPage: FunctionComponent = () => {
|
||||
type AuthProvider = {
|
||||
Name?: string;
|
||||
Id?: string;
|
||||
}
|
||||
|
||||
const UserEdit: FunctionComponent = () => {
|
||||
const [ userName, setUserName ] = useState('');
|
||||
const [ deleteFoldersAccess, setDeleteFoldersAccess ] = useState<ItemsArr[]>([]);
|
||||
const [ authProviders, setAuthProviders ] = useState([]);
|
||||
const [ passwordResetProviders, setPasswordResetProviders ] = useState([]);
|
||||
const [ deleteFoldersAccess, setDeleteFoldersAccess ] = useState<ResetProvider[]>([]);
|
||||
const [ authProviders, setAuthProviders ] = useState<AuthProvider[]>([]);
|
||||
const [ passwordResetProviders, setPasswordResetProviders ] = useState<ResetProvider[]>([]);
|
||||
|
||||
const [ authenticationProviderId, setAuthenticationProviderId ] = useState('');
|
||||
const [ passwordResetProviderId, setPasswordResetProviderId ] = useState('');
|
||||
@ -91,7 +94,7 @@ const UserEditPage: FunctionComponent = () => {
|
||||
})).then(function (channelsResult) {
|
||||
let isChecked;
|
||||
let checkedAttribute;
|
||||
const itemsArr: ItemsArr[] = [];
|
||||
const itemsArr: ResetProvider[] = [];
|
||||
|
||||
for (const folder of mediaFolders) {
|
||||
isChecked = user.Policy.EnableContentDeletion || user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id) != -1;
|
||||
@ -172,7 +175,7 @@ const UserEditPage: FunctionComponent = () => {
|
||||
(page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value = user.Policy.LoginAttemptsBeforeLockout || '0';
|
||||
(page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value = user.Policy.MaxActiveSessions || '0';
|
||||
if (window.ApiClient.isMinServerVersion('10.6.0')) {
|
||||
(page.querySelector('#selectSyncPlayAccess') as HTMLInputElement).value = user.Policy.SyncPlayAccess;
|
||||
(page.querySelector('#selectSyncPlayAccess') as HTMLSelectElement).value = user.Policy.SyncPlayAccess;
|
||||
}
|
||||
loading.hide();
|
||||
}, [loadAuthProviders, loadPasswordResetProviders, loadDeleteFolders ]);
|
||||
@ -227,8 +230,8 @@ const UserEditPage: FunctionComponent = () => {
|
||||
user.Policy.RemoteClientBitrateLimit = Math.floor(1e6 * parseFloat((page.querySelector('#txtRemoteClientBitrateLimit') as HTMLInputElement).value || '0'));
|
||||
user.Policy.LoginAttemptsBeforeLockout = parseInt((page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value || '0');
|
||||
user.Policy.MaxActiveSessions = parseInt((page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value || '0');
|
||||
user.Policy.AuthenticationProviderId = (page.querySelector('.selectLoginProvider') as HTMLInputElement).value;
|
||||
user.Policy.PasswordResetProviderId = (page.querySelector('.selectPasswordResetProvider') as HTMLInputElement).value;
|
||||
user.Policy.AuthenticationProviderId = (page.querySelector('#selectLoginProvider') as HTMLSelectElement).value;
|
||||
user.Policy.PasswordResetProviderId = (page.querySelector('#selectPasswordResetProvider') as HTMLSelectElement).value;
|
||||
user.Policy.EnableContentDeletion = (page.querySelector('.chkEnableDeleteAllFolders') as HTMLInputElement).checked;
|
||||
user.Policy.EnableContentDeletionFromFolders = user.Policy.EnableContentDeletion ? [] : Array.prototype.filter.call(page.querySelectorAll('.chkFolder'), function (c) {
|
||||
return c.checked;
|
||||
@ -236,7 +239,7 @@ const UserEditPage: FunctionComponent = () => {
|
||||
return c.getAttribute('data-id');
|
||||
});
|
||||
if (window.ApiClient.isMinServerVersion('10.6.0')) {
|
||||
user.Policy.SyncPlayAccess = (page.querySelector('#selectSyncPlayAccess') as HTMLInputElement).value as SyncPlayUserAccessType;
|
||||
user.Policy.SyncPlayAccess = (page.querySelector('#selectSyncPlayAccess') as HTMLSelectElement).value as SyncPlayUserAccessType;
|
||||
}
|
||||
window.ApiClient.updateUser(user).then(function () {
|
||||
window.ApiClient.updateUserPolicy(user.Id || '', user.Policy || {}).then(function () {
|
||||
@ -270,18 +273,42 @@ const UserEditPage: FunctionComponent = () => {
|
||||
|
||||
(page.querySelector('.editUserProfileForm') as HTMLFormElement).addEventListener('submit', onSubmit);
|
||||
|
||||
(page.querySelector('.button-cancel') as HTMLButtonElement).addEventListener('click', function() {
|
||||
(page.querySelector('#btnCancel') as HTMLButtonElement).addEventListener('click', function() {
|
||||
window.history.back();
|
||||
});
|
||||
}, [loadData]);
|
||||
|
||||
const optionLoginProvider = authProviders.map((provider) => {
|
||||
const selected = provider.Id === authenticationProviderId || authProviders.length < 2 ? ' selected' : '';
|
||||
return `<option value="${provider.Id}"${selected}>${escapeHTML(provider.Name)}</option>`;
|
||||
});
|
||||
|
||||
const optionPasswordResetProvider = passwordResetProviders.map((provider) => {
|
||||
const selected = provider.Id === passwordResetProviderId || passwordResetProviders.length < 2 ? ' selected' : '';
|
||||
return `<option value="${provider.Id}"${selected}>${escapeHTML(provider.Name)}</option>`;
|
||||
});
|
||||
|
||||
const optionSyncPlayAccess = () => {
|
||||
let content = '';
|
||||
content += `<option value='CreateAndJoinGroups'>${globalize.translate('LabelSyncPlayAccessCreateAndJoinGroups')}</option>`;
|
||||
content += `<option value='JoinGroups'>${globalize.translate('LabelSyncPlayAccessJoinGroups')}</option>`;
|
||||
content += `<option value='None'>${globalize.translate('LabelSyncPlayAccessNone')}</option>`;
|
||||
return content;
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='content-primary'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
titleLink='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
<Page
|
||||
id='editUserPage'
|
||||
className='mainAnimatedPage type-interior'
|
||||
>
|
||||
<div ref={element} className='content-primary'>
|
||||
<div className='verticalSection'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
url='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SectionTabs activeTab='useredit'/>
|
||||
<div
|
||||
className='lnkEditUserPreferencesContainer'
|
||||
@ -313,29 +340,29 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</div>
|
||||
<div className='selectContainer fldSelectLoginProvider hide'>
|
||||
<SelectElement
|
||||
className= 'selectLoginProvider'
|
||||
label= 'LabelAuthProvider'
|
||||
currentProviderId={authenticationProviderId}
|
||||
providers={authProviders}
|
||||
/>
|
||||
id='selectLoginProvider'
|
||||
label='LabelAuthProvider'
|
||||
>
|
||||
{optionLoginProvider}
|
||||
</SelectElement>
|
||||
|
||||
<div className='fieldDescription'>
|
||||
{globalize.translate('AuthProviderHelp')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='selectContainer fldSelectPasswordResetProvider hide'>
|
||||
<SelectElement
|
||||
className= 'selectPasswordResetProvider'
|
||||
label= 'LabelPasswordResetProvider'
|
||||
currentProviderId={passwordResetProviderId}
|
||||
providers={passwordResetProviders}
|
||||
/>
|
||||
id='selectPasswordResetProvider'
|
||||
label='LabelPasswordResetProvider'
|
||||
>
|
||||
{optionPasswordResetProvider}
|
||||
</SelectElement>
|
||||
<div className='fieldDescription'>
|
||||
{globalize.translate('PasswordResetProviderHelp')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='checkboxContainer checkboxContainer-withDescription fldRemoteAccess hide'>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkRemoteAccess'
|
||||
title='AllowRemoteAccess'
|
||||
/>
|
||||
@ -345,7 +372,6 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</div>
|
||||
<CheckBoxElement
|
||||
labelClassName='checkboxContainer'
|
||||
type='checkbox'
|
||||
className='chkIsAdmin'
|
||||
title='OptionAllowUserToManageServer'
|
||||
/>
|
||||
@ -355,12 +381,10 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</h2>
|
||||
<div className='checkboxList paperList' style={{padding: '.5em 1em'}}>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableLiveTvAccess'
|
||||
title='OptionAllowBrowsingLiveTv'
|
||||
/>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkManageLiveTv'
|
||||
title='OptionAllowManageLiveTv'
|
||||
/>
|
||||
@ -372,27 +396,22 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</h2>
|
||||
<div className='checkboxList paperList' style={{padding: '.5em 1em'}}>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableMediaPlayback'
|
||||
title='OptionAllowMediaPlayback'
|
||||
/>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableAudioPlaybackTranscoding'
|
||||
title='OptionAllowAudioPlaybackTranscoding'
|
||||
/>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableVideoPlaybackTranscoding'
|
||||
title='OptionAllowVideoPlaybackTranscoding'
|
||||
/>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableVideoPlaybackRemuxing'
|
||||
title='OptionAllowVideoPlaybackRemuxing'
|
||||
/>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkForceRemoteSourceTranscoding'
|
||||
title='OptionForceRemoteSourceTranscoding'
|
||||
/>
|
||||
@ -420,11 +439,12 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</div>
|
||||
<div className='verticalSection'>
|
||||
<div className='selectContainer fldSelectSyncPlayAccess'>
|
||||
<SelectSyncPlayAccessElement
|
||||
className='selectSyncPlayAccess'
|
||||
<SelectElement
|
||||
id='selectSyncPlayAccess'
|
||||
label='LabelSyncPlayAccess'
|
||||
/>
|
||||
>
|
||||
{optionSyncPlayAccess()}
|
||||
</SelectElement>
|
||||
<div className='fieldDescription'>
|
||||
{globalize.translate('SyncPlayAccessHelp')}
|
||||
</div>
|
||||
@ -437,18 +457,17 @@ const UserEditPage: FunctionComponent = () => {
|
||||
<div className='checkboxList paperList checkboxList-paperList'>
|
||||
<CheckBoxElement
|
||||
labelClassName='checkboxContainer'
|
||||
type='checkbox'
|
||||
className='chkEnableDeleteAllFolders'
|
||||
title='AllLibraries'
|
||||
/>
|
||||
<div className='deleteAccess'>
|
||||
{deleteFoldersAccess.map(Item => (
|
||||
<CheckBoxListItem
|
||||
<CheckBoxElement
|
||||
key={Item.Id}
|
||||
className='chkFolder'
|
||||
Id={Item.Id}
|
||||
Name={Item.Name}
|
||||
checkedAttribute={Item.checkedAttribute}
|
||||
itemId={Item.Id}
|
||||
itemName={Item.Name}
|
||||
itemCheckedAttribute={Item.checkedAttribute}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -460,12 +479,10 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</h2>
|
||||
<div className='checkboxList paperList' style={{padding: '.5em 1em'}}>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableRemoteControlOtherUsers'
|
||||
title='OptionAllowRemoteControlOthers'
|
||||
/>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkRemoteControlSharedDevices'
|
||||
title='OptionAllowRemoteSharedDevices'
|
||||
/>
|
||||
@ -479,7 +496,6 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</h2>
|
||||
<div className='checkboxContainer checkboxContainer-withDescription'>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkEnableDownloading'
|
||||
title='OptionAllowContentDownload'
|
||||
/>
|
||||
@ -489,7 +505,6 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</div>
|
||||
<div className='checkboxContainer checkboxContainer-withDescription' id='fldIsEnabled'>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkDisabled'
|
||||
title='OptionDisableUser'
|
||||
/>
|
||||
@ -499,7 +514,6 @@ const UserEditPage: FunctionComponent = () => {
|
||||
</div>
|
||||
<div className='checkboxContainer checkboxContainer-withDescription' id='fldIsHidden'>
|
||||
<CheckBoxElement
|
||||
type='checkbox'
|
||||
className='chkIsHidden'
|
||||
title='OptionHideUser'
|
||||
/>
|
||||
@ -550,14 +564,16 @@ const UserEditPage: FunctionComponent = () => {
|
||||
/>
|
||||
<ButtonElement
|
||||
type='button'
|
||||
className='raised button-cancel block btnCancel'
|
||||
id='btnCancel'
|
||||
className='raised button-cancel block'
|
||||
title='ButtonCancel'
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default UserEditPage;
|
||||
export default UserEdit;
|
@ -1,16 +1,17 @@
|
||||
import { UserDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react';
|
||||
|
||||
import loading from '../loading/loading';
|
||||
import loading from '../../components/loading/loading';
|
||||
import libraryMenu from '../../scripts/libraryMenu';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import toast from '../toast/toast';
|
||||
import SectionTabs from '../dashboard/users/SectionTabs';
|
||||
import CheckBoxListItem from '../dashboard/users/CheckBoxListItem';
|
||||
import ButtonElement from '../dashboard/users/ButtonElement';
|
||||
import toast from '../../components/toast/toast';
|
||||
import SectionTabs from '../../components/dashboard/users/SectionTabs';
|
||||
import ButtonElement from '../../elements/ButtonElement';
|
||||
import { getParameterByName } from '../../utils/url';
|
||||
import SectionTitleContainer from '../dashboard/users/SectionTitleContainer';
|
||||
import AccessContainer from '../dashboard/users/AccessContainer';
|
||||
import SectionTitleContainer from '../../elements/SectionTitleContainer';
|
||||
import AccessContainer from '../../components/dashboard/users/AccessContainer';
|
||||
import CheckBoxElement from '../../elements/CheckBoxElement';
|
||||
import Page from '../../components/Page';
|
||||
|
||||
type ItemsArr = {
|
||||
Name?: string;
|
||||
@ -19,7 +20,7 @@ type ItemsArr = {
|
||||
checkedAttribute?: string
|
||||
}
|
||||
|
||||
const UserLibraryAccessPage: FunctionComponent = () => {
|
||||
const UserLibraryAccess: FunctionComponent = () => {
|
||||
const [ userName, setUserName ] = useState('');
|
||||
const [channelsItems, setChannelsItems] = useState<ItemsArr[]>([]);
|
||||
const [mediaFoldersItems, setMediaFoldersItems] = useState<ItemsArr[]>([]);
|
||||
@ -226,12 +227,17 @@ const UserLibraryAccessPage: FunctionComponent = () => {
|
||||
}, [loadData]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='content-primary'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
titleLink='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
<Page
|
||||
id='userLibraryAccessPage'
|
||||
className='mainAnimatedPage type-interior'
|
||||
>
|
||||
<div ref={element} className='content-primary'>
|
||||
<div className='verticalSection'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
url='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
</div>
|
||||
<SectionTabs activeTab='userlibraryaccess'/>
|
||||
<form className='userLibraryAccessForm'>
|
||||
<AccessContainer
|
||||
@ -245,12 +251,12 @@ const UserLibraryAccessPage: FunctionComponent = () => {
|
||||
description='LibraryAccessHelp'
|
||||
>
|
||||
{mediaFoldersItems.map(Item => (
|
||||
<CheckBoxListItem
|
||||
<CheckBoxElement
|
||||
key={Item.Id}
|
||||
className='chkFolder'
|
||||
Id={Item.Id}
|
||||
Name={Item.Name}
|
||||
checkedAttribute={Item.checkedAttribute}
|
||||
itemId={Item.Id}
|
||||
itemName={Item.Name}
|
||||
itemCheckedAttribute={Item.checkedAttribute}
|
||||
/>
|
||||
))}
|
||||
</AccessContainer>
|
||||
@ -266,12 +272,12 @@ const UserLibraryAccessPage: FunctionComponent = () => {
|
||||
description='ChannelAccessHelp'
|
||||
>
|
||||
{channelsItems.map(Item => (
|
||||
<CheckBoxListItem
|
||||
<CheckBoxElement
|
||||
key={Item.Id}
|
||||
className='chkChannel'
|
||||
Id={Item.Id}
|
||||
Name={Item.Name}
|
||||
checkedAttribute={Item.checkedAttribute}
|
||||
itemId={Item.Id}
|
||||
itemName={Item.Name}
|
||||
itemCheckedAttribute={Item.checkedAttribute}
|
||||
/>
|
||||
))}
|
||||
</AccessContainer>
|
||||
@ -287,13 +293,13 @@ const UserLibraryAccessPage: FunctionComponent = () => {
|
||||
description='DeviceAccessHelp'
|
||||
>
|
||||
{devicesItems.map(Item => (
|
||||
<CheckBoxListItem
|
||||
<CheckBoxElement
|
||||
key={Item.Id}
|
||||
className='chkDevice'
|
||||
Id={Item.Id}
|
||||
Name={Item.Name}
|
||||
AppName={Item.AppName}
|
||||
checkedAttribute={Item.checkedAttribute}
|
||||
itemId={Item.Id}
|
||||
itemName={Item.Name}
|
||||
itemAppName={Item.AppName}
|
||||
itemCheckedAttribute={Item.checkedAttribute}
|
||||
/>
|
||||
))}
|
||||
</AccessContainer>
|
||||
@ -307,8 +313,9 @@ const UserLibraryAccessPage: FunctionComponent = () => {
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default UserLibraryAccessPage;
|
||||
export default UserLibraryAccess;
|
@ -2,13 +2,14 @@ import React, { FunctionComponent, useCallback, useEffect, useState, useRef } fr
|
||||
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import loading from '../loading/loading';
|
||||
import toast from '../toast/toast';
|
||||
import SectionTitleContainer from '../dashboard/users/SectionTitleContainer';
|
||||
import InputElement from '../dashboard/users/InputElement';
|
||||
import CheckBoxListItem from '../dashboard/users/CheckBoxListItem';
|
||||
import ButtonElement from '../dashboard/users/ButtonElement';
|
||||
import AccessContainer from '../dashboard/users/AccessContainer';
|
||||
import loading from '../../components/loading/loading';
|
||||
import toast from '../../components/toast/toast';
|
||||
import SectionTitleContainer from '../../elements/SectionTitleContainer';
|
||||
import InputElement from '../../elements/InputElement';
|
||||
import ButtonElement from '../../elements/ButtonElement';
|
||||
import AccessContainer from '../../components/dashboard/users/AccessContainer';
|
||||
import CheckBoxElement from '../../elements/CheckBoxElement';
|
||||
import Page from '../../components/Page';
|
||||
|
||||
type userInput = {
|
||||
Name?: string;
|
||||
@ -20,7 +21,7 @@ type ItemsArr = {
|
||||
Id?: string;
|
||||
}
|
||||
|
||||
const NewUserPage: FunctionComponent = () => {
|
||||
const UserNew: FunctionComponent = () => {
|
||||
const [ channelsItems, setChannelsItems ] = useState<ItemsArr[]>([]);
|
||||
const [ mediaFoldersItems, setMediaFoldersItems ] = useState<ItemsArr[]>([]);
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
@ -169,18 +170,24 @@ const NewUserPage: FunctionComponent = () => {
|
||||
|
||||
(page.querySelector('.newUserProfileForm') as HTMLFormElement).addEventListener('submit', onSubmit);
|
||||
|
||||
(page.querySelector('.button-cancel') as HTMLButtonElement).addEventListener('click', function() {
|
||||
(page.querySelector('#btnCancel') as HTMLButtonElement).addEventListener('click', function() {
|
||||
window.history.back();
|
||||
});
|
||||
}, [loadUser]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='content-primary'>
|
||||
<SectionTitleContainer
|
||||
title={globalize.translate('HeaderAddUser')}
|
||||
titleLink='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
<Page
|
||||
id='newUserPage'
|
||||
className='mainAnimatedPage type-interior'
|
||||
>
|
||||
<div ref={element} className='content-primary'>
|
||||
<div className='verticalSection'>
|
||||
<SectionTitleContainer
|
||||
title={globalize.translate('HeaderAddUser')}
|
||||
url='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form className='newUserProfileForm'>
|
||||
<div className='inputContainer'>
|
||||
<InputElement
|
||||
@ -208,12 +215,11 @@ const NewUserPage: FunctionComponent = () => {
|
||||
description='LibraryAccessHelp'
|
||||
>
|
||||
{mediaFoldersItems.map(Item => (
|
||||
<CheckBoxListItem
|
||||
<CheckBoxElement
|
||||
key={Item.Id}
|
||||
className='chkFolder'
|
||||
Id={Item.Id}
|
||||
Name={Item.Name}
|
||||
checkedAttribute=''
|
||||
itemId={Item.Id}
|
||||
itemName={Item.Name}
|
||||
/>
|
||||
))}
|
||||
</AccessContainer>
|
||||
@ -229,12 +235,11 @@ const NewUserPage: FunctionComponent = () => {
|
||||
description='ChannelAccessHelp'
|
||||
>
|
||||
{channelsItems.map(Item => (
|
||||
<CheckBoxListItem
|
||||
<CheckBoxElement
|
||||
key={Item.Id}
|
||||
className='chkChannel'
|
||||
Id={Item.Id}
|
||||
Name={Item.Name}
|
||||
checkedAttribute=''
|
||||
itemId={Item.Id}
|
||||
itemName={Item.Name}
|
||||
/>
|
||||
))}
|
||||
</AccessContainer>
|
||||
@ -246,14 +251,16 @@ const NewUserPage: FunctionComponent = () => {
|
||||
/>
|
||||
<ButtonElement
|
||||
type='button'
|
||||
className='raised button-cancel block btnCancel'
|
||||
id='btnCancel'
|
||||
className='raised button-cancel block'
|
||||
title='ButtonCancel'
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default NewUserPage;
|
||||
export default UserNew;
|
@ -1,25 +1,21 @@
|
||||
import { AccessSchedule, DynamicDayOfWeek, UserDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import { AccessSchedule, DynamicDayOfWeek, ParentalRating, UserDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import LibraryMenu from '../../scripts/libraryMenu';
|
||||
import AccessScheduleList from '../dashboard/users/AccessScheduleList';
|
||||
import BlockedTagList from '../dashboard/users/BlockedTagList';
|
||||
import ButtonElement from '../dashboard/users/ButtonElement';
|
||||
import CheckBoxListItem from '../dashboard/users/CheckBoxListItem';
|
||||
import SectionTitleButtonElement from '../dashboard/users/SectionTitleButtonElement';
|
||||
import SectionTitleContainer from '../dashboard/users/SectionTitleContainer';
|
||||
import SelectMaxParentalRating from '../dashboard/users/SelectMaxParentalRating';
|
||||
import SectionTabs from '../dashboard/users/SectionTabs';
|
||||
import loading from '../loading/loading';
|
||||
import toast from '../toast/toast';
|
||||
import AccessScheduleList from '../../components/dashboard/users/AccessScheduleList';
|
||||
import BlockedTagList from '../../components/dashboard/users/BlockedTagList';
|
||||
import ButtonElement from '../../elements/ButtonElement';
|
||||
import SectionTitleContainer from '../../elements/SectionTitleContainer';
|
||||
import SectionTabs from '../../components/dashboard/users/SectionTabs';
|
||||
import loading from '../../components/loading/loading';
|
||||
import toast from '../../components/toast/toast';
|
||||
import { getParameterByName } from '../../utils/url';
|
||||
import CheckBoxElement from '../../elements/CheckBoxElement';
|
||||
import escapeHTML from 'escape-html';
|
||||
import SelectElement from '../../elements/SelectElement';
|
||||
import Page from '../../components/Page';
|
||||
|
||||
type RatingsArr = {
|
||||
Name: string;
|
||||
Value: number;
|
||||
}
|
||||
|
||||
type ItemsArr = {
|
||||
type UnratedItem = {
|
||||
name: string;
|
||||
value: string;
|
||||
checkedAttribute: string
|
||||
@ -27,8 +23,8 @@ type ItemsArr = {
|
||||
|
||||
const UserParentalControl: FunctionComponent = () => {
|
||||
const [ userName, setUserName ] = useState('');
|
||||
const [ parentalRatings, setParentalRatings ] = useState<RatingsArr[]>([]);
|
||||
const [ unratedItems, setUnratedItems ] = useState<ItemsArr[]>([]);
|
||||
const [ parentalRatings, setParentalRatings ] = useState<ParentalRating[]>([]);
|
||||
const [ unratedItems, setUnratedItems ] = useState<UnratedItem[]>([]);
|
||||
const [ accessSchedules, setAccessSchedules ] = useState<AccessSchedule[]>([]);
|
||||
const [ blockedTags, setBlockedTags ] = useState([]);
|
||||
|
||||
@ -36,7 +32,7 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
|
||||
const populateRatings = useCallback((allParentalRatings) => {
|
||||
let rating;
|
||||
const ratings: RatingsArr[] = [];
|
||||
const ratings: ParentalRating[] = [];
|
||||
|
||||
for (let i = 0, length = allParentalRatings.length; i < length; i++) {
|
||||
rating = allParentalRatings[i];
|
||||
@ -90,7 +86,7 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
value: 'Series'
|
||||
}];
|
||||
|
||||
const itemsArr: ItemsArr[] = [];
|
||||
const itemsArr: UnratedItem[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
const isChecked = user.Policy.BlockUnratedItems.indexOf(item.value) != -1;
|
||||
@ -181,7 +177,7 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
}
|
||||
}
|
||||
|
||||
(page.querySelector('.selectMaxParentalRating') as HTMLInputElement).value = ratingValue;
|
||||
(page.querySelector('#selectMaxParentalRating') as HTMLSelectElement).value = ratingValue;
|
||||
|
||||
if (user.Policy.IsAdministrator) {
|
||||
(page.querySelector('.accessScheduleSection') as HTMLDivElement).classList.add('hide');
|
||||
@ -226,7 +222,7 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
throw new Error('Unexpected null user.Policy');
|
||||
}
|
||||
|
||||
user.Policy.MaxParentalRating = parseInt((page.querySelector('.selectMaxParentalRating') as HTMLInputElement).value || '0', 10) || null;
|
||||
user.Policy.MaxParentalRating = parseInt((page.querySelector('#selectMaxParentalRating') as HTMLSelectElement).value || '0', 10) || null;
|
||||
user.Policy.BlockUnratedItems = Array.prototype.filter.call(page.querySelectorAll('.chkUnratedItem'), function (i) {
|
||||
return i.checked;
|
||||
}).map(function (i) {
|
||||
@ -299,7 +295,7 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
(page.querySelector('.btnAddSchedule') as HTMLButtonElement).addEventListener('click', function () {
|
||||
(page.querySelector('#btnAddSchedule') as HTMLButtonElement).addEventListener('click', function () {
|
||||
showSchedulePopup({
|
||||
Id: 0,
|
||||
UserId: '',
|
||||
@ -309,28 +305,43 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
}, -1);
|
||||
});
|
||||
|
||||
(page.querySelector('.btnAddBlockedTag') as HTMLButtonElement).addEventListener('click', function () {
|
||||
(page.querySelector('#btnAddBlockedTag') as HTMLButtonElement).addEventListener('click', function () {
|
||||
showBlockedTagPopup();
|
||||
});
|
||||
|
||||
(page.querySelector('.userParentalControlForm') as HTMLFormElement).addEventListener('submit', onSubmit);
|
||||
}, [loadBlockedTags, loadData, renderAccessSchedule]);
|
||||
|
||||
const optionMaxParentalRating = () => {
|
||||
let content = '';
|
||||
content += '<option value=\'\'></option>';
|
||||
for (const rating of parentalRatings) {
|
||||
content += `<option value='${rating.Value}'>${escapeHTML(rating.Name)}</option>`;
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='content-primary'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
titleLink='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
<Page
|
||||
id='userParentalControlPage'
|
||||
className='mainAnimatedPage type-interior'
|
||||
>
|
||||
<div ref={element} className='content-primary'>
|
||||
<div className='verticalSection'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
url='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
</div>
|
||||
<SectionTabs activeTab='userparentalcontrol'/>
|
||||
<form className='userParentalControlForm'>
|
||||
<div className='selectContainer'>
|
||||
<SelectMaxParentalRating
|
||||
className= 'selectMaxParentalRating'
|
||||
label= 'LabelMaxParentalRating'
|
||||
parentalRatings={parentalRatings}
|
||||
/>
|
||||
<SelectElement
|
||||
id='selectMaxParentalRating'
|
||||
label='LabelMaxParentalRating'
|
||||
>
|
||||
{optionMaxParentalRating()}
|
||||
</SelectElement>
|
||||
<div className='fieldDescription'>
|
||||
{globalize.translate('MaxParentalRatingHelp')}
|
||||
</div>
|
||||
@ -342,12 +353,12 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
</h3>
|
||||
<div className='checkboxList paperList' style={{ padding: '.5em 1em' }}>
|
||||
{unratedItems.map(Item => {
|
||||
return <CheckBoxListItem
|
||||
return <CheckBoxElement
|
||||
key={Item.value}
|
||||
className='chkUnratedItem'
|
||||
ItemType={Item.value}
|
||||
Name={Item.name}
|
||||
checkedAttribute={Item.checkedAttribute}
|
||||
itemType={Item.value}
|
||||
itemName={Item.name}
|
||||
itemCheckedAttribute={Item.checkedAttribute}
|
||||
/>;
|
||||
})}
|
||||
</div>
|
||||
@ -355,19 +366,16 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
</div>
|
||||
<br />
|
||||
<div className='verticalSection' style={{marginBottom: '2em'}}>
|
||||
<div
|
||||
className='detailSectionHeader sectionTitleContainer'
|
||||
style={{display: 'flex', alignItems: 'center', paddingBottom: '1em'}}
|
||||
>
|
||||
<h2 className='sectionTitle'>
|
||||
{globalize.translate('LabelBlockContentWithTags')}
|
||||
</h2>
|
||||
<SectionTitleButtonElement
|
||||
className='fab btnAddBlockedTag submit'
|
||||
title='Add'
|
||||
icon='add'
|
||||
/>
|
||||
</div>
|
||||
<SectionTitleContainer
|
||||
SectionClassName='detailSectionHeader'
|
||||
title={globalize.translate('LabelBlockContentWithTags')}
|
||||
isBtnVisible={true}
|
||||
btnId='btnAddBlockedTag'
|
||||
btnClassName='fab submit sectionTitleButton'
|
||||
btnTitle='Add'
|
||||
btnIcon='add'
|
||||
isLinkVisible={false}
|
||||
/>
|
||||
<div className='blockedTags' style={{marginTop: '.5em'}}>
|
||||
{blockedTags.map((tag, index) => {
|
||||
return <BlockedTagList
|
||||
@ -378,19 +386,15 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className='accessScheduleSection verticalSection' style={{marginBottom: '2em'}}>
|
||||
<div
|
||||
className='sectionTitleContainer'
|
||||
style={{display: 'flex', alignItems: 'center', paddingBottom: '1em'}}
|
||||
>
|
||||
<h2 className='sectionTitle'>
|
||||
{globalize.translate('HeaderAccessSchedule')}
|
||||
</h2>
|
||||
<SectionTitleButtonElement
|
||||
className='fab btnAddSchedule submit'
|
||||
title='Add'
|
||||
icon='add'
|
||||
/>
|
||||
</div>
|
||||
<SectionTitleContainer
|
||||
title={globalize.translate('HeaderAccessSchedule')}
|
||||
isBtnVisible={true}
|
||||
btnId='btnAddSchedule'
|
||||
btnClassName='fab submit sectionTitleButton'
|
||||
btnTitle='Add'
|
||||
btnIcon='add'
|
||||
isLinkVisible={false}
|
||||
/>
|
||||
<p>{globalize.translate('HeaderAccessScheduleHelp')}</p>
|
||||
<div className='accessScheduleList paperList'>
|
||||
{accessSchedules.map((accessSchedule, index) => {
|
||||
@ -414,7 +418,8 @@ const UserParentalControl: FunctionComponent = () => {
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
);
|
||||
};
|
||||
|
@ -1,19 +1,23 @@
|
||||
import React, { FunctionComponent, useCallback, useEffect, useState } from 'react';
|
||||
import SectionTabs from '../dashboard/users/SectionTabs';
|
||||
import UserPasswordForm from '../dashboard/users/UserPasswordForm';
|
||||
import SectionTabs from '../../components/dashboard/users/SectionTabs';
|
||||
import UserPasswordForm from '../../components/dashboard/users/UserPasswordForm';
|
||||
import { getParameterByName } from '../../utils/url';
|
||||
import SectionTitleContainer from '../dashboard/users/SectionTitleContainer';
|
||||
import SectionTitleContainer from '../../elements/SectionTitleContainer';
|
||||
import Page from '../../components/Page';
|
||||
import loading from '../../components/loading/loading';
|
||||
|
||||
const UserPasswordPage: FunctionComponent = () => {
|
||||
const UserPassword: FunctionComponent = () => {
|
||||
const userId = getParameterByName('userId');
|
||||
const [ userName, setUserName ] = useState('');
|
||||
|
||||
const loadUser = useCallback(() => {
|
||||
loading.show();
|
||||
window.ApiClient.getUser(userId).then(function (user) {
|
||||
if (!user.Name) {
|
||||
throw new Error('Unexpected null user.Name');
|
||||
}
|
||||
setUserName(user.Name);
|
||||
loading.hide();
|
||||
});
|
||||
}, [userId]);
|
||||
useEffect(() => {
|
||||
@ -21,12 +25,17 @@ const UserPasswordPage: FunctionComponent = () => {
|
||||
}, [loadUser]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Page
|
||||
id='userPasswordPage'
|
||||
className='mainAnimatedPage type-interior userPasswordPage'
|
||||
>
|
||||
<div className='content-primary'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
titleLink='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
<div className='verticalSection'>
|
||||
<SectionTitleContainer
|
||||
title={userName}
|
||||
url='https://docs.jellyfin.org/general/server/users/'
|
||||
/>
|
||||
</div>
|
||||
<SectionTabs activeTab='userpassword'/>
|
||||
<div className='readOnlyContent'>
|
||||
<UserPasswordForm
|
||||
@ -34,8 +43,9 @@ const UserPasswordPage: FunctionComponent = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default UserPasswordPage;
|
||||
export default UserPassword;
|
@ -4,18 +4,17 @@ import React, { FunctionComponent, useEffect, useState, useRef, useCallback } fr
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import LibraryMenu from '../../scripts/libraryMenu';
|
||||
import { appHost } from '../apphost';
|
||||
import confirm from '../confirm/confirm';
|
||||
import ButtonElement from '../dashboard/users/ButtonElement';
|
||||
import UserPasswordForm from '../dashboard/users/UserPasswordForm';
|
||||
import loading from '../loading/loading';
|
||||
import toast from '../toast/toast';
|
||||
import { appHost } from '../../components/apphost';
|
||||
import confirm from '../../components/confirm/confirm';
|
||||
import ButtonElement from '../../elements/ButtonElement';
|
||||
import UserPasswordForm from '../../components/dashboard/users/UserPasswordForm';
|
||||
import loading from '../../components/loading/loading';
|
||||
import toast from '../../components/toast/toast';
|
||||
import { getParameterByName } from '../../utils/url';
|
||||
import Page from '../../components/Page';
|
||||
|
||||
type IProps = {
|
||||
userId: string;
|
||||
}
|
||||
|
||||
const UserProfilePage: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
const UserProfile: FunctionComponent = () => {
|
||||
const userId = getParameterByName('userId');
|
||||
const [ userName, setUserName ] = useState('');
|
||||
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
@ -57,11 +56,11 @@ const UserProfilePage: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
}
|
||||
|
||||
if (user.PrimaryImageTag) {
|
||||
(page.querySelector('.btnAddImage') as HTMLButtonElement).classList.add('hide');
|
||||
(page.querySelector('.btnDeleteImage') as HTMLButtonElement).classList.remove('hide');
|
||||
(page.querySelector('#btnAddImage') as HTMLButtonElement).classList.add('hide');
|
||||
(page.querySelector('#btnDeleteImage') as HTMLButtonElement).classList.remove('hide');
|
||||
} else if (appHost.supports('fileinput') && (loggedInUser?.Policy?.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) {
|
||||
(page.querySelector('.btnDeleteImage') as HTMLButtonElement).classList.add('hide');
|
||||
(page.querySelector('.btnAddImage') as HTMLButtonElement).classList.remove('hide');
|
||||
(page.querySelector('#btnDeleteImage') as HTMLButtonElement).classList.add('hide');
|
||||
(page.querySelector('#btnAddImage') as HTMLButtonElement).classList.remove('hide');
|
||||
}
|
||||
});
|
||||
loading.hide();
|
||||
@ -120,7 +119,7 @@ const UserProfilePage: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
(page.querySelector('.btnDeleteImage') as HTMLButtonElement).addEventListener('click', function () {
|
||||
(page.querySelector('#btnDeleteImage') as HTMLButtonElement).addEventListener('click', function () {
|
||||
confirm(
|
||||
globalize.translate('DeleteImageConfirmation'),
|
||||
globalize.translate('DeleteImage')
|
||||
@ -133,7 +132,7 @@ const UserProfilePage: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
});
|
||||
});
|
||||
|
||||
(page.querySelector('.btnAddImage') as HTMLButtonElement).addEventListener('click', function () {
|
||||
(page.querySelector('#btnAddImage') as HTMLButtonElement).addEventListener('click', function () {
|
||||
const uploadImage = page.querySelector('#uploadImage') as HTMLInputElement;
|
||||
uploadImage.value = '';
|
||||
uploadImage.click();
|
||||
@ -145,13 +144,18 @@ const UserProfilePage: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
}, [reloadUser, userId]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='padded-left padded-right padded-bottom-page'>
|
||||
<Page
|
||||
id='userProfilePage'
|
||||
title={globalize.translate('Profile')}
|
||||
className='mainAnimatedPage libraryPage userPreferencesPage userPasswordPage noSecondaryNavPage'
|
||||
>
|
||||
<div ref={element} className='padded-left padded-right padded-bottom-page'>
|
||||
<div
|
||||
className='readOnlyContent'
|
||||
style={{margin: '0 auto', marginBottom: '1.8em', padding: '0 1em', display: 'flex', flexDirection: 'row', alignItems: 'center'}}
|
||||
>
|
||||
<div
|
||||
className='imagePlaceHolder'
|
||||
style={{position: 'relative', display: 'inline-block', maxWidth: 200 }}
|
||||
>
|
||||
<input
|
||||
@ -172,12 +176,14 @@ const UserProfilePage: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
<br />
|
||||
<ButtonElement
|
||||
type='button'
|
||||
className='raised btnAddImage hide'
|
||||
id='btnAddImage'
|
||||
className='raised button-submit hide'
|
||||
title='ButtonAddImage'
|
||||
/>
|
||||
<ButtonElement
|
||||
type='button'
|
||||
className='raised btnDeleteImage hide'
|
||||
id='btnDeleteImage'
|
||||
className='raised hide'
|
||||
title='DeleteImage'
|
||||
/>
|
||||
</div>
|
||||
@ -186,8 +192,9 @@ const UserProfilePage: FunctionComponent<IProps> = ({userId}: IProps) => {
|
||||
userId={userId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default UserProfilePage;
|
||||
export default UserProfile;
|
@ -2,16 +2,17 @@ import { UserDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, {FunctionComponent, useEffect, useState, useRef} from 'react';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import loading from '../loading/loading';
|
||||
import loading from '../../components/loading/loading';
|
||||
import dom from '../../scripts/dom';
|
||||
import confirm from '../../components/confirm/confirm';
|
||||
import UserCardBox from '../dashboard/users/UserCardBox';
|
||||
import SectionTitleContainer from '../dashboard/users/SectionTitleContainer';
|
||||
import UserCardBox from '../../components/dashboard/users/UserCardBox';
|
||||
import SectionTitleContainer from '../../elements/SectionTitleContainer';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../components/cardbuilder/card.scss';
|
||||
import '../../components/indicators/indicators.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import Page from '../../components/Page';
|
||||
|
||||
type MenuEntry = {
|
||||
name?: string;
|
||||
@ -19,7 +20,7 @@ type MenuEntry = {
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
const UserProfilesPage: FunctionComponent = () => {
|
||||
const UserProfiles: FunctionComponent = () => {
|
||||
const [ users, setUsers ] = useState<UserDto[]>([]);
|
||||
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
@ -124,19 +125,28 @@ const UserProfilesPage: FunctionComponent = () => {
|
||||
}
|
||||
});
|
||||
|
||||
(page.querySelector('.btnAddUser') as HTMLButtonElement).addEventListener('click', function() {
|
||||
(page.querySelector('#btnAddUser') as HTMLButtonElement).addEventListener('click', function() {
|
||||
Dashboard.navigate('usernew.html');
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='content-primary'>
|
||||
<SectionTitleContainer
|
||||
title={globalize.translate('HeaderUsers')}
|
||||
isBtnVisible={true}
|
||||
titleLink='https://docs.jellyfin.org/general/server/users/adding-managing-users.html'
|
||||
/>
|
||||
<Page
|
||||
id='userProfilesPage'
|
||||
className='mainAnimatedPage type-interior userProfilesPage fullWidthContent'
|
||||
>
|
||||
<div ref={element} className='content-primary'>
|
||||
<div className='verticalSection'>
|
||||
<SectionTitleContainer
|
||||
title={globalize.translate('HeaderUsers')}
|
||||
isBtnVisible={true}
|
||||
btnId='btnAddUser'
|
||||
btnClassName='fab submit sectionTitleButton'
|
||||
btnTitle='ButtonAddUser'
|
||||
btnIcon='add'
|
||||
url='https://docs.jellyfin.org/general/server/users/adding-managing-users.html'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='localUsers itemsContainer vertical-wrap'>
|
||||
{users.map(user => {
|
||||
@ -144,8 +154,9 @@ const UserProfilesPage: FunctionComponent = () => {
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default UserProfilesPage;
|
||||
export default UserProfiles;
|
@ -355,14 +355,14 @@ import '../assets/css/flexstyles.scss';
|
||||
}
|
||||
}
|
||||
|
||||
function refreshDashboardInfoInDrawer(apiClient) {
|
||||
function refreshDashboardInfoInDrawer(page, apiClient) {
|
||||
currentDrawerType = 'admin';
|
||||
loadNavDrawer();
|
||||
|
||||
if (navDrawerScrollContainer.querySelector('.adminDrawerLogo')) {
|
||||
updateDashboardMenuSelectedItem();
|
||||
updateDashboardMenuSelectedItem(page);
|
||||
} else {
|
||||
createDashboardMenu(apiClient);
|
||||
createDashboardMenu(page, apiClient);
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,9 +370,9 @@ import '../assets/css/flexstyles.scss';
|
||||
return window.location.href.toString().toLowerCase().indexOf(url.toLowerCase()) !== -1;
|
||||
}
|
||||
|
||||
function updateDashboardMenuSelectedItem() {
|
||||
function updateDashboardMenuSelectedItem(page) {
|
||||
const links = navDrawerScrollContainer.querySelectorAll('.navMenuOption');
|
||||
const currentViewId = viewManager.currentView().id;
|
||||
const currentViewId = page.id;
|
||||
|
||||
for (let i = 0, length = links.length; i < length; i++) {
|
||||
let link = links[i];
|
||||
@ -590,7 +590,7 @@ import '../assets/css/flexstyles.scss';
|
||||
});
|
||||
}
|
||||
|
||||
function createDashboardMenu(apiClient) {
|
||||
function createDashboardMenu(page, apiClient) {
|
||||
return getToolsMenuHtml(apiClient).then(function (toolsMenuHtml) {
|
||||
let html = '';
|
||||
html += '<a class="adminDrawerLogo clearLink" is="emby-linkbutton" href="#/home.html">';
|
||||
@ -598,7 +598,7 @@ import '../assets/css/flexstyles.scss';
|
||||
html += '</a>';
|
||||
html += toolsMenuHtml;
|
||||
navDrawerScrollContainer.innerHTML = html;
|
||||
updateDashboardMenuSelectedItem();
|
||||
updateDashboardMenuSelectedItem(page);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1017,7 +1017,7 @@ import '../assets/css/flexstyles.scss';
|
||||
mainDrawerButton.classList.remove('hide');
|
||||
}
|
||||
|
||||
refreshDashboardInfoInDrawer(apiClient);
|
||||
refreshDashboardInfoInDrawer(page, apiClient);
|
||||
} else {
|
||||
if (mainDrawerButton) {
|
||||
if (enableLibraryNavDrawer || (isHomePage && enableLibraryNavDrawerHome)) {
|
||||
|
@ -77,13 +77,6 @@ import { appRouter } from '../components/appRouter';
|
||||
controller: 'user/menu/index'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/myprofile.html',
|
||||
path: 'user/profile/index.html',
|
||||
autoFocus: false,
|
||||
pageComponent: 'UserProfilePage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/mypreferencescontrols.html',
|
||||
path: 'user/controls/index.html',
|
||||
@ -429,53 +422,6 @@ import { appRouter } from '../components/appRouter';
|
||||
controller: 'shows/tvrecommended'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/useredit.html',
|
||||
path: 'dashboard/users/useredit.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
pageComponent: 'UserEditPage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/userlibraryaccess.html',
|
||||
path: 'dashboard/users/userlibraryaccess.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
pageComponent: 'UserLibraryAccessPage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/usernew.html',
|
||||
path: 'dashboard/users/usernew.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
pageComponent: 'NewUserPage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/userparentalcontrol.html',
|
||||
path: 'dashboard/users/userparentalcontrol.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
pageComponent: 'UserParentalControl'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/userpassword.html',
|
||||
path: 'dashboard/users/userpassword.html',
|
||||
autoFocus: false,
|
||||
pageComponent: 'UserPasswordPage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/userprofiles.html',
|
||||
path: 'dashboard/users/userprofiles.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
pageComponent: 'UserProfilesPage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/wizardremoteaccess.html',
|
||||
path: 'wizard/remote/index.html',
|
||||
|
Loading…
Reference in New Issue
Block a user