From ccecc4a4b11366c046a23d3a1472c8d68192e07e Mon Sep 17 00:00:00 2001 From: grafixeyehero <32230989+grafixeyehero@users.noreply.github.com> Date: Sat, 2 Oct 2021 18:10:14 +0300 Subject: [PATCH 1/8] convert NewUserPage to react --- .../users/ElementComponent/ButtonElement.tsx | 32 +++ .../ElementComponent/CheckBoxElement.tsx | 33 +++ .../users/ElementComponent/InputElement.tsx | 34 +++ .../SectionTitleLinkElement.tsx | 34 +++ .../users/NewUserPage/ChannelAccess.tsx | 32 +++ .../users/NewUserPage/FolderAccess.tsx | 32 +++ src/components/pages/NewUserPage.tsx | 241 ++++++++++++++++++ src/controllers/dashboard/users/usernew.html | 59 ----- src/controllers/dashboard/users/usernew.js | 130 ---------- src/scripts/routes.js | 2 +- 10 files changed, 439 insertions(+), 190 deletions(-) create mode 100644 src/components/DashboardComponent/users/ElementComponent/ButtonElement.tsx create mode 100644 src/components/DashboardComponent/users/ElementComponent/CheckBoxElement.tsx create mode 100644 src/components/DashboardComponent/users/ElementComponent/InputElement.tsx create mode 100644 src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx create mode 100644 src/components/DashboardComponent/users/NewUserPage/ChannelAccess.tsx create mode 100644 src/components/DashboardComponent/users/NewUserPage/FolderAccess.tsx create mode 100644 src/components/pages/NewUserPage.tsx delete mode 100644 src/controllers/dashboard/users/usernew.js diff --git a/src/components/DashboardComponent/users/ElementComponent/ButtonElement.tsx b/src/components/DashboardComponent/users/ElementComponent/ButtonElement.tsx new file mode 100644 index 0000000000..14a9ecb1e4 --- /dev/null +++ b/src/components/DashboardComponent/users/ElementComponent/ButtonElement.tsx @@ -0,0 +1,32 @@ +import React, { FunctionComponent } from 'react'; +import globalize from '../../../../scripts/globalize'; + +const createButtonElement = ({ type, className, title }) => ({ + __html: `` +}); + +type IProps = { + type?: string; + className?: string; + title?: string +} + +const ButtonElement: FunctionComponent = ({ type, className, title }: IProps) => { + return ( +
+ ); +}; + +export default ButtonElement; diff --git a/src/components/DashboardComponent/users/ElementComponent/CheckBoxElement.tsx b/src/components/DashboardComponent/users/ElementComponent/CheckBoxElement.tsx new file mode 100644 index 0000000000..27ed0c0ebb --- /dev/null +++ b/src/components/DashboardComponent/users/ElementComponent/CheckBoxElement.tsx @@ -0,0 +1,33 @@ +import React, { FunctionComponent } from 'react'; +import globalize from '../../../../scripts/globalize'; + +const createCheckBoxElement = ({ type, className, title }) => ({ + __html: `` +}); + +type IProps = { + type?: string; + className?: string; + title?: string +} + +const CheckBoxElement: FunctionComponent = ({ type, className, title }: IProps) => { + return ( +
+ ); +}; + +export default CheckBoxElement; diff --git a/src/components/DashboardComponent/users/ElementComponent/InputElement.tsx b/src/components/DashboardComponent/users/ElementComponent/InputElement.tsx new file mode 100644 index 0000000000..127a633c69 --- /dev/null +++ b/src/components/DashboardComponent/users/ElementComponent/InputElement.tsx @@ -0,0 +1,34 @@ +import React, { FunctionComponent } from 'react'; +import globalize from '../../../../scripts/globalize'; + +const createInputElement = ({ type, id, label, options }) => ({ + __html: `` +}); + +type IProps = { + type?: string; + id?: string; + label?: string; + options?: string +} + +const InputElement: FunctionComponent = ({ type, id, label, ...rest }: IProps) => { + return ( +
+ ); +}; + +export default InputElement; diff --git a/src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx b/src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx new file mode 100644 index 0000000000..68dc565796 --- /dev/null +++ b/src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx @@ -0,0 +1,34 @@ +import React, { FunctionComponent } from 'react'; +import globalize from '../../../../scripts/globalize'; + +const createLinkElement = ({ className, title, href }) => ({ + __html: ` + ${title} + ` +}); + +type IProps = { + title?: string; + className?: string; + url?: string +} + +const SectionTitleLinkElement: FunctionComponent = ({ className, title, url }: IProps) => { + return ( +
+ ); +}; + +export default SectionTitleLinkElement; diff --git a/src/components/DashboardComponent/users/NewUserPage/ChannelAccess.tsx b/src/components/DashboardComponent/users/NewUserPage/ChannelAccess.tsx new file mode 100644 index 0000000000..08ac859967 --- /dev/null +++ b/src/components/DashboardComponent/users/NewUserPage/ChannelAccess.tsx @@ -0,0 +1,32 @@ +import React, { FunctionComponent } from 'react'; + +const createCheckBoxElement = ({Name, Id}) => ({ + __html: `` +}); + +type IProps = { + Name?: string; + Id?: string; +} + +const ChannelAccess: FunctionComponent = ({Name, Id}: IProps) => { + return ( +
+ ); +}; + +export default ChannelAccess; + diff --git a/src/components/DashboardComponent/users/NewUserPage/FolderAccess.tsx b/src/components/DashboardComponent/users/NewUserPage/FolderAccess.tsx new file mode 100644 index 0000000000..d5371ee8f1 --- /dev/null +++ b/src/components/DashboardComponent/users/NewUserPage/FolderAccess.tsx @@ -0,0 +1,32 @@ +import React, { FunctionComponent } from 'react'; + +const createCheckBoxElement = ({Name, Id}) => ({ + __html: `` +}); + +type IProps = { + Name?: string; + Id?: string; +} + +const FolderAccess: FunctionComponent = ({Name, Id}: IProps) => { + return ( +
+ ); +}; + +export default FolderAccess; + diff --git a/src/components/pages/NewUserPage.tsx b/src/components/pages/NewUserPage.tsx new file mode 100644 index 0000000000..f27f73391a --- /dev/null +++ b/src/components/pages/NewUserPage.tsx @@ -0,0 +1,241 @@ +import React, { FunctionComponent, useEffect, useState, useRef } from 'react'; + +import Dashboard from '../../scripts/clientUtils'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import toast from '../toast/toast'; + +import SectionTitleLinkElement from '../DashboardComponent/users/ElementComponent/SectionTitleLinkElement'; +import InputElement from '../DashboardComponent/users/ElementComponent/InputElement'; +import CheckBoxElement from '../DashboardComponent/users/ElementComponent/CheckBoxElement'; +import FolderAccess from '../DashboardComponent/users/NewUserPage/FolderAccess'; +import ChannelAccess from '../DashboardComponent/users/NewUserPage/ChannelAccess'; +import ButtonElement from '../DashboardComponent/users/ElementComponent/ButtonElement'; + +type userInput = { + Name?: string; + Password?: string; +} + +const NewUserPage: FunctionComponent = () => { + const [ channelsResult, setChannelsResult ] = useState([]); + const [ mediaFoldersResult, setMediaFoldersResult ] = useState([]); + const element = useRef(null); + + useEffect(() => { + const loadUser = () => { + element.current.querySelector('#txtUsername').value = ''; + element.current.querySelector('#txtPassword').value = ''; + loading.show(); + const promiseFolders = window.ApiClient.getJSON(window.ApiClient.getUrl('Library/MediaFolders', { + IsHidden: false + })); + const promiseChannels = window.ApiClient.getJSON(window.ApiClient.getUrl('Channels')); + // eslint-disable-next-line compat/compat + Promise.all([promiseFolders, promiseChannels]).then(function (responses) { + loadMediaFolders(responses[0].Items); + loadChannels(responses[1].Items); + loading.hide(); + }); + }; + + loadUser(); + + const loadMediaFolders = (mediaFolders) => { + setMediaFoldersResult(mediaFolders); + + const folderAccess = element?.current?.querySelector('.folderAccess'); + folderAccess.dispatchEvent(new CustomEvent('create')); + + element.current.querySelector('.chkEnableAllFolders').checked = false; + }; + + const loadChannels = (channels) => { + setChannelsResult(channels); + + const channelAccess = element?.current?.querySelector('.channelAccess'); + channelAccess.dispatchEvent(new CustomEvent('create')); + + if (channels.length) { + element?.current?.querySelector('.channelAccessContainer').classList.remove('hide'); + } else { + element?.current?.querySelector('.channelAccessContainer').classList.add('hide'); + } + + element.current.querySelector('.chkEnableAllChannels').checked = false; + }; + + const saveUser = () => { + const userInput: userInput = {}; + userInput.Name = element?.current?.querySelector('#txtUsername').value; + userInput.Password = element?.current?.querySelector('#txtPassword').value; + window.ApiClient.createUser(userInput).then(function (user) { + user.Policy.EnableAllFolders = element?.current?.querySelector('.chkEnableAllFolders').checked; + user.Policy.EnabledFolders = []; + + if (!user.Policy.EnableAllFolders) { + user.Policy.EnabledFolders = Array.prototype.filter.call(element?.current?.querySelectorAll('.chkFolder'), function (i) { + return i.checked; + }).map(function (i) { + return i.getAttribute('data-id'); + }); + } + + user.Policy.EnableAllChannels = element?.current?.querySelector('.chkEnableAllChannels').checked; + user.Policy.EnabledChannels = []; + + if (!user.Policy.EnableAllChannels) { + user.Policy.EnabledChannels = Array.prototype.filter.call(element?.current?.querySelectorAll('.chkChannel'), function (i) { + return i.checked; + }).map(function (i) { + return i.getAttribute('data-id'); + }); + } + + window.ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () { + Dashboard.navigate('useredit.html?userId=' + user.Id); + }); + }, function () { + toast(globalize.translate('ErrorDefault')); + loading.hide(); + }); + }; + + const onSubmit = (e) => { + loading.show(); + saveUser(); + e.preventDefault(); + e.stopPropagation(); + return false; + }; + + const chkEnableAllChannels = element?.current?.querySelector('.chkEnableAllChannels'); + chkEnableAllChannels.addEventListener('change', function (this: HTMLInputElement) { + if (this.checked) { + element?.current?.querySelector('.channelAccessListContainer').classList.add('hide'); + } else { + element?.current?.querySelector('.channelAccessListContainer').classList.remove('hide'); + } + }); + + const chkEnableAllFolders = element?.current?.querySelector('.chkEnableAllFolders'); + chkEnableAllFolders.addEventListener('change', function (this: HTMLInputElement) { + if (this.checked) { + element?.current?.querySelector('.folderAccessListContainer').classList.add('hide'); + } else { + element?.current?.querySelector('.folderAccessListContainer').classList.remove('hide'); + } + }); + + element?.current?.querySelector('.newUserProfileForm').addEventListener('submit', onSubmit); + + element?.current?.querySelector('.button-cancel').addEventListener('click', function() { + window.history.back(); + }); + }, []); + + return ( +
+
+
+
+

+ {globalize.translate('ButtonAddUser')} +

+ +
+
+
+
+ +
+
+ +
+ +
+

{globalize.translate('HeaderLibraryAccess')}

+ +
+
+

+ {globalize.translate('HeaderLibraries')} +

+
+ {mediaFoldersResult.map((folder, index: number)=> ( + + ))} +
+
+
+ {globalize.translate('LibraryAccessHelp')} +
+
+
+
+

{globalize.translate('HeaderChannelAccess')}

+ +
+
+

+ {globalize.translate('Channels')} +

+
+ {channelsResult.map((folder, index: number)=> ( + + ))} +
+
+
+ {globalize.translate('ChannelAccessHelp')} +
+
+
+
+ + +
+
+
+
+ ); +}; + +export default NewUserPage; diff --git a/src/controllers/dashboard/users/usernew.html b/src/controllers/dashboard/users/usernew.html index 67f1f61ebc..c3f77c5e49 100644 --- a/src/controllers/dashboard/users/usernew.html +++ b/src/controllers/dashboard/users/usernew.html @@ -1,62 +1,3 @@
-
-
-
-
-
-

${ButtonAddUser}

- ${Help} -
-
- -
- -
- -
-
- -
-

${HeaderLibraryAccess}

-
- -
${LibraryAccessHelp}
-
-
-
-
-
-
- - - -
- - - -
-
-
-
diff --git a/src/controllers/dashboard/users/usernew.js b/src/controllers/dashboard/users/usernew.js deleted file mode 100644 index 406bf55c86..0000000000 --- a/src/controllers/dashboard/users/usernew.js +++ /dev/null @@ -1,130 +0,0 @@ -import 'jquery'; -import loading from '../../../components/loading/loading'; -import globalize from '../../../scripts/globalize'; -import '../../../elements/emby-checkbox/emby-checkbox'; -import Dashboard from '../../../scripts/clientUtils'; -import toast from '../../../components/toast/toast'; - -/* eslint-disable indent */ - - function loadMediaFolders(page, mediaFolders) { - let html = ''; - html += '

' + globalize.translate('HeaderLibraries') + '

'; - html += '
'; - - for (let i = 0; i < mediaFolders.length; i++) { - const folder = mediaFolders[i]; - html += ''; - } - - html += '
'; - $('.folderAccess', page).html(html).trigger('create'); - $('#chkEnableAllFolders', page).prop('checked', false); - } - - function loadChannels(page, channels) { - let html = ''; - html += '

' + globalize.translate('Channels') + '

'; - html += '
'; - - for (let i = 0; i < channels.length; i++) { - const folder = channels[i]; - html += ''; - } - - html += '
'; - $('.channelAccess', page).show().html(html).trigger('create'); - - if (channels.length) { - $('.channelAccessContainer', page).show(); - } else { - $('.channelAccessContainer', page).hide(); - } - - $('#chkEnableAllChannels', page).prop('checked', false); - } - - function loadUser(page) { - $('#txtUsername', page).val(''); - $('#txtPassword', page).val(''); - loading.show(); - const promiseFolders = ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders', { - IsHidden: false - })); - const promiseChannels = ApiClient.getJSON(ApiClient.getUrl('Channels')); - Promise.all([promiseFolders, promiseChannels]).then(function (responses) { - loadMediaFolders(page, responses[0].Items); - loadChannels(page, responses[1].Items); - loading.hide(); - }); - } - - function saveUser(page) { - const user = {}; - user.Name = $('#txtUsername', page).val(); - user.Password = $('#txtPassword', page).val(); - ApiClient.createUser(user).then(function (user) { - user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).is(':checked'); - user.Policy.EnabledFolders = []; - - if (!user.Policy.EnableAllFolders) { - user.Policy.EnabledFolders = $('.chkFolder', page).get().filter(function (i) { - return i.checked; - }).map(function (i) { - return i.getAttribute('data-id'); - }); - } - - user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).is(':checked'); - user.Policy.EnabledChannels = []; - - if (!user.Policy.EnableAllChannels) { - user.Policy.EnabledChannels = $('.chkChannel', page).get().filter(function (i) { - return i.checked; - }).map(function (i) { - return i.getAttribute('data-id'); - }); - } - - ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () { - Dashboard.navigate('useredit.html?userId=' + user.Id); - }); - }, function () { - toast(globalize.translate('ErrorDefault')); - loading.hide(); - }); - } - - function onSubmit() { - const page = $(this).parents('.page')[0]; - loading.show(); - saveUser(page); - return false; - } - - function loadData(page) { - loadUser(page); - } - - $(document).on('pageinit', '#newUserPage', function () { - const page = this; - $('#chkEnableAllChannels', page).on('change', function () { - if (this.checked) { - $('.channelAccessListContainer', page).hide(); - } else { - $('.channelAccessListContainer', page).show(); - } - }); - $('#chkEnableAllFolders', page).on('change', function () { - if (this.checked) { - $('.folderAccessListContainer', page).hide(); - } else { - $('.folderAccessListContainer', page).show(); - } - }); - $('.newUserProfileForm').off('submit', onSubmit).on('submit', onSubmit); - }).on('pageshow', '#newUserPage', function () { - loadData(this); - }); - -/* eslint-enable indent */ diff --git a/src/scripts/routes.js b/src/scripts/routes.js index ed0b40b97d..68b47cc75c 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -456,7 +456,7 @@ import { appRouter } from '../components/appRouter'; path: 'dashboard/users/usernew.html', autoFocus: false, roles: 'admin', - controller: 'dashboard/users/usernew' + pageComponent: 'NewUserPage' }); defineRoute({ From 84e8bbceb181fc69086b82fbf1c98425d34e28cc Mon Sep 17 00:00:00 2001 From: grafixeyehero <32230989+grafixeyehero@users.noreply.github.com> Date: Sat, 2 Oct 2021 20:17:32 +0300 Subject: [PATCH 2/8] use folder.Id for FolderAccess & ChannelAccess key instead of index --- src/components/pages/NewUserPage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/pages/NewUserPage.tsx b/src/components/pages/NewUserPage.tsx index f27f73391a..dc857ed272 100644 --- a/src/components/pages/NewUserPage.tsx +++ b/src/components/pages/NewUserPage.tsx @@ -179,9 +179,9 @@ const NewUserPage: FunctionComponent = () => { {globalize.translate('HeaderLibraries')}
- {mediaFoldersResult.map((folder, index: number)=> ( + {mediaFoldersResult.map(folder => ( @@ -206,9 +206,9 @@ const NewUserPage: FunctionComponent = () => { {globalize.translate('Channels')}
- {channelsResult.map((folder, index: number)=> ( + {channelsResult.map(folder => ( From 9db5773cb9680404431efc5b24ac5a5cd4ac48a4 Mon Sep 17 00:00:00 2001 From: grafixeyehero <32230989+grafixeyehero@users.noreply.github.com> Date: Tue, 19 Oct 2021 19:19:28 +0300 Subject: [PATCH 3/8] rebase --- .../SectionTitleLinkElement.tsx | 34 ------------------- .../users}/ButtonElement.tsx | 2 +- .../users}/CheckBoxElement.tsx | 2 +- .../users}/InputElement.tsx | 2 +- .../users/NewUserChannelAccessList.tsx} | 4 +-- .../users/NewUserFolderAccessList.tsx} | 4 +-- .../users/SectionTitleLinkElement.tsx | 2 +- src/components/pages/NewUserPage.tsx | 16 ++++----- 8 files changed, 16 insertions(+), 50 deletions(-) delete mode 100644 src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx rename src/components/{DashboardComponent/users/ElementComponent => dashboard/users}/ButtonElement.tsx (92%) rename src/components/{DashboardComponent/users/ElementComponent => dashboard/users}/CheckBoxElement.tsx (93%) rename src/components/{DashboardComponent/users/ElementComponent => dashboard/users}/InputElement.tsx (93%) rename src/components/{DashboardComponent/users/NewUserPage/ChannelAccess.tsx => dashboard/users/NewUserChannelAccessList.tsx} (80%) rename src/components/{DashboardComponent/users/NewUserPage/FolderAccess.tsx => dashboard/users/NewUserFolderAccessList.tsx} (80%) diff --git a/src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx b/src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx deleted file mode 100644 index 68dc565796..0000000000 --- a/src/components/DashboardComponent/users/ElementComponent/SectionTitleLinkElement.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { FunctionComponent } from 'react'; -import globalize from '../../../../scripts/globalize'; - -const createLinkElement = ({ className, title, href }) => ({ - __html: ` - ${title} - ` -}); - -type IProps = { - title?: string; - className?: string; - url?: string -} - -const SectionTitleLinkElement: FunctionComponent = ({ className, title, url }: IProps) => { - return ( -
- ); -}; - -export default SectionTitleLinkElement; diff --git a/src/components/DashboardComponent/users/ElementComponent/ButtonElement.tsx b/src/components/dashboard/users/ButtonElement.tsx similarity index 92% rename from src/components/DashboardComponent/users/ElementComponent/ButtonElement.tsx rename to src/components/dashboard/users/ButtonElement.tsx index 14a9ecb1e4..dfde8c7399 100644 --- a/src/components/DashboardComponent/users/ElementComponent/ButtonElement.tsx +++ b/src/components/dashboard/users/ButtonElement.tsx @@ -1,5 +1,5 @@ import React, { FunctionComponent } from 'react'; -import globalize from '../../../../scripts/globalize'; +import globalize from '../../../scripts/globalize'; const createButtonElement = ({ type, className, title }) => ({ __html: `