From e5f597c530b620cc5dcbbd52a1cd49116eaf5215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kruli=C5=A1?= Date: Tue, 18 Feb 2025 22:34:48 +0100 Subject: [PATCH] Adjusting labels and captions, adding user-sync-cancel callout, adding logout link to home page. --- src/components/layout/Sidebar/Sidebar.js | 2 +- .../widgets/Sidebar/UserPanel/UserPanel.js | 2 +- src/locales/cs.json | 13 ++--- src/locales/en.json | 9 ++-- src/locales/whitelist_cs.json | 4 +- src/pages/Home/Home.js | 47 ++++++++++++++++++- src/pages/User/User.js | 28 +++++++++-- src/redux/modules/users.js | 9 +++- src/redux/selectors/users.js | 5 +- 9 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/components/layout/Sidebar/Sidebar.js b/src/components/layout/Sidebar/Sidebar.js index a554f54..5ad9504 100644 --- a/src/components/layout/Sidebar/Sidebar.js +++ b/src/components/layout/Sidebar/Sidebar.js @@ -74,7 +74,7 @@ const Sidebar = ({ pendingFetchOperations, loggedInUser, currentUrl, links: { HO ) : ( } + title={} icon="person-walking-arrow-loop-left" link={getReturnUrl()} /> diff --git a/src/components/widgets/Sidebar/UserPanel/UserPanel.js b/src/components/widgets/Sidebar/UserPanel/UserPanel.js index 9dd86b8..09fc821 100644 --- a/src/components/widgets/Sidebar/UserPanel/UserPanel.js +++ b/src/components/widgets/Sidebar/UserPanel/UserPanel.js @@ -48,7 +48,7 @@ class UserPanel extends Component { }}> - + diff --git a/src/locales/cs.json b/src/locales/cs.json index c91f876..db8fa8c 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -24,6 +24,7 @@ "app.apiErrorCodes.500-000": "Neočekávaná vnitřní chyba.", "app.apiErrorCodes.501": "Funkce nebyla implementována", "app.apiErrorCodes.unknown": "Neznámá chyba API.", + "app.backToReCodEx": "Zpět do ReCodExu", "app.badge.failedLoading": "Nepodařilo se načíst data", "app.badge.failedLoadingInfo": "Prosim zkontrolujte si své připojení k Internetu.", "app.badge.sessionExpiration": "Expirace sezení:", @@ -53,7 +54,8 @@ "app.groups.removeFromGroup": "Odebrat ze skupiny", "app.header.languageSwitching.translationTitle": "Jazyková verze", "app.headerNotification.copiedToClippboard": "Zkopírováno do schránky.", - "app.homepage.about": "Toto rozšíření ReCodExu mí nastarost datovou integraci mezi ReCodExem a Studijním informačním systémem (SIS) Karlovy Univerzity. Prosíme vyberte si jednu ze stránek níže.", + "app.homepage.about": "Toto rozšíření ReCodExu má na starost datovou integraci mezi ReCodExem a Studijním Informačním Systémem (SIS) Karlovy Univerzity. Prosíme vyberte si jednu ze stránek níže.", + "app.homepage.backToReCodExDescription": "A odlásit z relace SIS-CodExu.", "app.homepage.processingToken": "Probíhá zpracování přihlašovacího tokenu...", "app.homepage.processingTokenFailed": "Autentizační proces selhal.", "app.homepage.returnToReCodEx": "Návrat do ReCodExu...", @@ -66,7 +68,6 @@ "app.localizedTexts.noText": "Pro danou lokalizaci není vyplněn ani text ani externí odkaz na zadaní. Tato úloha ještě není řádně dospecifikována.", "app.localizedTexts.studentHintHeading": "Nápověda", "app.localizedTexts.validation.noLocalizedText": "Prosíme povolte alespoň jednu záložku s lokalizovanými texty.", - "app.logout": "Odhlásit", "app.navigation.dashboard": "Přehled", "app.navigation.edit": "Editovat", "app.navigation.exercise": "Úloha", @@ -103,7 +104,6 @@ "app.roles.supervisorStudents": "Vedoucí-studenti", "app.roles.supervisors": "Vedoucí", "app.roles.supervisorsEmpowered": "Zplnomocnění vedoucí", - "app.sidebar.menu.return": "Zpět do ReCodExu", "app.sidebar.menu.user": "Osobní údaje", "app.submissionStatus.accepted": "Toto řešení bylo označeno jako akceptované vedoucím skupiny.", "app.user.diffBox.email": "Email", @@ -115,12 +115,13 @@ "app.user.diffBox.title": "Uživatelský profil", "app.user.diffBox.titlesAfterName": "Tituly před jménem", "app.user.diffBox.titlesBeforeName": "Tituly za jménem", - "app.user.fetchSisButton": "Občerstvit data ze SISu", + "app.user.fetchSisButton": "Znovu načíst data ze SISu", "app.user.sisUserFailedCallout": "Načítání dat ze SISu selhalo.", "app.user.sisUserLoadedCallout": "Data ze SISu byla úspěšně načtena.", - "app.user.syncButton": "Přepsat ReCodEx daty ze SISu", + "app.user.syncButton": "Aktualizovat profil v ReCodExu daty ze SISu", "app.user.title": "Osobní údaje", - "app.user.userSyncFailedCallout": "Synchronizace uživatele selhala. Prosím, znovu načťete stránku a opakujte akci později.", + "app.user.userSyncCanceledCallout": "Synchronizace uživatelských dat byla přerušena, protože profil z ReCodExu byl zastaralý a bylo nutné jej znovu načíst. Prosíme, zahajte aktualizaci dat znovu, pokud je to stále žádoucí.", + "app.user.userSyncFailedCallout": "Synchronizace uživatele selhala. Prosíme, znovu načťete stránku a opakujte akci později.", "app.user.userUpdatedCallout": "Uživatelská data v ReCodExu byla úspěšně aktualizována daty ze SISu.", "app.userName.externalIds": "Externí identifikátory", "app.userName.externalIdsClickInfo": "klikutím zkopírujete ID do schránky", diff --git a/src/locales/en.json b/src/locales/en.json index 5562bee..48564a4 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -24,6 +24,7 @@ "app.apiErrorCodes.500-000": "Unexpected internal error.", "app.apiErrorCodes.501": "Feature not implemented", "app.apiErrorCodes.unknown": "Unknown API error.", + "app.backToReCodEx": "Back to ReCodEx", "app.badge.failedLoading": "Failed to load the data", "app.badge.failedLoadingInfo": "Please check your Internet connection.", "app.badge.sessionExpiration": "Session expiration:", @@ -54,6 +55,7 @@ "app.header.languageSwitching.translationTitle": "Translation", "app.headerNotification.copiedToClippboard": "Copied to clippboard.", "app.homepage.about": "This ReCodEx extension handles data integration and exchange between ReCodEx and Charles University Student Information System (SIS). Please choose one of the pages below.", + "app.homepage.backToReCodExDescription": "And logout from SIS-CodEx session.", "app.homepage.processingToken": "Processing authentication token...", "app.homepage.processingTokenFailed": "Authentication process failed.", "app.homepage.returnToReCodEx": "Return to ReCodEx...", @@ -66,7 +68,6 @@ "app.localizedTexts.noText": "There is no text nor link for given localization. The exercise is not fully specified yet.", "app.localizedTexts.studentHintHeading": "Hint", "app.localizedTexts.validation.noLocalizedText": "Please enable at least one tab of localized texts.", - "app.logout": "Logout", "app.navigation.dashboard": "Dashboard", "app.navigation.edit": "Edit", "app.navigation.exercise": "Exercise", @@ -103,7 +104,6 @@ "app.roles.supervisorStudents": "Supervisor-students", "app.roles.supervisors": "Supervisors", "app.roles.supervisorsEmpowered": "Empowered Supervisors", - "app.sidebar.menu.return": "Back to ReCodEx", "app.sidebar.menu.user": "Personal Data", "app.submissionStatus.accepted": "This solution was marked by one of the supervisors as accepted.", "app.user.diffBox.email": "Email", @@ -115,11 +115,12 @@ "app.user.diffBox.title": "User's profile", "app.user.diffBox.titlesAfterName": "Titles after", "app.user.diffBox.titlesBeforeName": "Titles before", - "app.user.fetchSisButton": "Refresh SIS Data", + "app.user.fetchSisButton": "Reload SIS data", "app.user.sisUserFailedCallout": "SIS data (re)loading failed.", "app.user.sisUserLoadedCallout": "The SIS user data were successfully (re)loaded.", - "app.user.syncButton": "Overwrite ReCodEx with SIS Data", + "app.user.syncButton": "Update ReCodEx profile with data from SIS", "app.user.title": "Personal Data", + "app.user.userSyncCanceledCallout": "User sync operation was canceled, because the ReCodEx profile data were outdated and needed to be reloaded. Please, re-start the operation if it is still desired.", "app.user.userSyncFailedCallout": "User sync operation failed. Reload the page and try again later.", "app.user.userUpdatedCallout": "The ReCodEx user data were successfully updated by current SIS data.", "app.userName.externalIds": "External identifiers", diff --git a/src/locales/whitelist_cs.json b/src/locales/whitelist_cs.json index cba2f91..8229671 100644 --- a/src/locales/whitelist_cs.json +++ b/src/locales/whitelist_cs.json @@ -1,3 +1,5 @@ [ - "app.roles.student" + "app.roles.student", + "app.user.diffBox.email", + "app.user.diffBox.login" ] \ No newline at end of file diff --git a/src/pages/Home/Home.js b/src/pages/Home/Home.js index f3471cc..39fcbb0 100644 --- a/src/pages/Home/Home.js +++ b/src/pages/Home/Home.js @@ -18,7 +18,7 @@ import Icon, { import Callout from '../../components/widgets/Callout'; import { setLang } from '../../redux/modules/app.js'; -import { login } from '../../redux/modules/auth.js'; +import { login, logout } from '../../redux/modules/auth.js'; import { getReturnUrl, setReturnUrl } from '../../helpers/localStorage.js'; import { knownLocalesNames } from '../../helpers/localizedData.js'; @@ -146,6 +146,50 @@ class Home extends Component {

+ +
+ + + +

+ +

+ + +

+ { + e.preventDefault(); + logout(); + + // let's go back to ReCodEx after the logout... + const url = getReturnUrl(); + if (url && window) { + setReturnUrl(null); + window.location.assign(url); + } + }}> + + + +

+ +

+ +

+ +
)} @@ -171,5 +215,6 @@ export default connect( dispatch => ({ setLang: lang => dispatch(setLang(lang)), login: token => dispatch(login(token)), + logout: () => dispatch(logout()), }) )(injectIntl(withLinks(Home))); diff --git a/src/pages/User/User.js b/src/pages/User/User.js index e4bea05..5aa7a52 100644 --- a/src/pages/User/User.js +++ b/src/pages/User/User.js @@ -13,7 +13,13 @@ import Button, { TheButtonGroup } from '../../components/widgets/TheButton'; import Icon, { LoadingIcon, RefreshIcon, UserProfileIcon, WarningIcon } from '../../components/icons'; import { fetchUserIfNeeded, syncUser, syncUserReset } from '../../redux/modules/users.js'; import { fetchSisUser, fetchSisUserIfNeeded } from '../../redux/modules/sisUsers.js'; -import { loggedInUserSelector, isUserSyncing, isUserUpdated, isUserSyncFailed } from '../../redux/selectors/users.js'; +import { + loggedInUserSelector, + isUserSyncing, + isUserUpdated, + isUserSyncFailed, + isUserSyncCanceled, +} from '../../redux/selectors/users.js'; import { loggedInSisUserSelector } from '../../redux/selectors/sisUsers.js'; import { isLoading, hasFailed, getJsData } from '../../redux/helpers/resourceManager'; @@ -91,6 +97,7 @@ class User extends Component { syncReset, isUserSyncing = false, isUserUpdated = false, + isUserSyncCanceled = false, isUserSyncFailed = false, } = this.props; const sisUserData = getJsData(loggedInSisUser)?.sisUser; @@ -128,6 +135,16 @@ class User extends Component { /> )} + + {isUserSyncCanceled && ( + + + + )} + {isUserSyncFailed && ( syncUser(user.id)}> {isUserSyncing ? : } - + )} @@ -249,6 +269,7 @@ User.propTypes = { loggedInSisUser: ImmutablePropTypes.map, isUserSyncing: PropTypes.bool, isUserUpdated: PropTypes.bool, + isUserSyncCanceled: PropTypes.bool, isUserSyncFailed: PropTypes.bool, loadAsync: PropTypes.func.isRequired, fetchSisUser: PropTypes.func.isRequired, @@ -265,6 +286,7 @@ export default connect( loggedInSisUser: loggedInSisUserSelector(state), isUserSyncing: isUserSyncing(state, loggedInUser && loggedInUser.getIn(['data', 'id'], '')), isUserUpdated: isUserUpdated(state, loggedInUser && loggedInUser.getIn(['data', 'id'], '')), + isUserSyncCanceled: isUserSyncCanceled(state, loggedInUser && loggedInUser.getIn(['data', 'id'], '')), isUserSyncFailed: isUserSyncFailed(state, loggedInUser && loggedInUser.getIn(['data', 'id'], '')), }; }, diff --git a/src/redux/modules/users.js b/src/redux/modules/users.js index 5fc7781..e49e4f4 100644 --- a/src/redux/modules/users.js +++ b/src/redux/modules/users.js @@ -48,13 +48,18 @@ const reducer = handleActions( state .setIn(['resources', id, 'syncing'], true) .removeIn(['resources', id, 'updated']) + .removeIn(['resources', id, 'syncCanceled']) .removeIn(['resources', id, 'syncFailed']), - [additionalActionTypes.SYNC_FULFILLED]: (state, { payload: { user, updated }, meta: { id } }) => + [additionalActionTypes.SYNC_FULFILLED]: ( + state, + { payload: { user, updated = false, canceled = false }, meta: { id } } + ) => state .setIn(['resources', id], createRecord({ state: resourceStatus.FULFILLED, data: user })) .setIn(['resources', id, 'syncing'], false) - .setIn(['resources', id, 'updated'], updated), + .setIn(['resources', id, 'updated'], updated) + .setIn(['resources', id, 'syncCanceled'], canceled), [additionalActionTypes.SYNC_REJECTED]: (state, { meta: { id } }) => state.setIn(['resources', id, 'syncing'], false).setIn(['resources', id, 'syncFailed'], true), diff --git a/src/redux/selectors/users.js b/src/redux/selectors/users.js index 4503aac..873c792 100644 --- a/src/redux/selectors/users.js +++ b/src/redux/selectors/users.js @@ -41,6 +41,9 @@ export const isUserSyncing = createSelector([usersSelector, getParam], (users, i export const isUserUpdated = createSelector([usersSelector, getParam], (users, id) => users.getIn([id, 'updated'], false) ); +export const isUserSyncCanceled = createSelector([usersSelector, getParam], (users, id) => + users.getIn([id, 'syncCanceled'], false) +); export const isUserSyncFailed = createSelector([usersSelector, getParam], (users, id) => - users.getIn([id, 'syncFailed', false]) + users.getIn([id, 'syncFailed'], false) );