From a9ccb1f9288abc4d052159f6f68d5df67d213ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kruli=C5=A1?= Date: Thu, 12 Feb 2026 19:31:52 +0100 Subject: [PATCH 1/2] Handle error state when initial authentication against ReCodEx fails. --- .../CoursesGroupsList/CoursesGroupsList.js | 2 +- src/locales/cs.json | 5 +++-- src/locales/en.json | 7 ++++--- src/pages/Home/Home.js | 18 ++++++++++++++---- src/redux/selectors/auth.js | 2 +- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/components/Groups/CoursesGroupsList/CoursesGroupsList.js b/src/components/Groups/CoursesGroupsList/CoursesGroupsList.js index 4670999..084fae3 100644 --- a/src/components/Groups/CoursesGroupsList/CoursesGroupsList.js +++ b/src/components/Groups/CoursesGroupsList/CoursesGroupsList.js @@ -158,7 +158,7 @@ const CoursesGroupsList = ({ )} diff --git a/src/locales/cs.json b/src/locales/cs.json index 278ea8b..980506f 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -47,7 +47,7 @@ "app.box.highlighterExplanation": "Tento panel je zvýrazněný. Klikněte pro vrácení do normálního stavu.", "app.confirm.no": "Ne", "app.confirm.yes": "Ano", - "app.coursesGroupsList.bind": "Svázat existující skupinu", + "app.coursesGroupsList.bind": "Svázat s existující skupinou", "app.coursesGroupsList.create": "Vytvořit novou skupinu", "app.coursesGroupsList.firstWeekEven": "sudé týdny", "app.coursesGroupsList.firstWeekOdd": "liché týdny", @@ -142,13 +142,14 @@ "app.homepage.groupsStudentPage": "Připojte se ke skupinám, které odpovídají vašim zapsaným předmětům v SISu.", "app.homepage.groupsSuperadminPage": "Správa skupin a jejich atributů na nejvyšší úrovni (dostupné pouze hlavním administrátorům).", "app.homepage.groupsTeacherPage": "Vytvořte nebo přiřaďte skupiny pro vaše předměty ze SISu.", + "app.homepage.loginFailed": "Ověření uživatele selhalo", "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...", "app.homepage.termsPage": "Správa semestrů a jejich souvisejících dat (kdy jsou aktivní pro studenty a učitele).", "app.homepage.title": "Rozšíření SIS-CodEx", "app.homepage.userPage": "Stránka s osobními údaji umožnujě synchronizovat uživatelský profil (jméno, tituly, email) s daty ze SISu.", - "app.homepage.userSessionExpired": "Vaše uživatelská relace vypršela", + "app.homepage.userSessionExpired": "Vaše uživatelská relace vypršela nebo jste se odhlásili", "app.homepage.userSessionExpiredInfo": "Musíte inicializovat novou relaci opětovným vstupem do této aplikace z ReCodExu.", "app.localizedTexts.validation.noLocalizedText": "Prosíme povolte alespoň jednu záložku s lokalizovanými texty.", "app.notifications.hideAll": "Pouze nové notifikace", diff --git a/src/locales/en.json b/src/locales/en.json index cc94971..4bc5e23 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -47,7 +47,7 @@ "app.box.highlighterExplanation": "This box is highlighted. Click to restore.", "app.confirm.no": "No", "app.confirm.yes": "Yes", - "app.coursesGroupsList.bind": "Bind Existing Group", + "app.coursesGroupsList.bind": "Bind with Existing Group", "app.coursesGroupsList.create": "Create New Group", "app.coursesGroupsList.firstWeekEven": "even weeks", "app.coursesGroupsList.firstWeekOdd": "odd weeks", @@ -142,13 +142,14 @@ "app.homepage.groupsStudentPage": "Join groups that correspond to your enrolled courses in SIS.", "app.homepage.groupsSuperadminPage": "Management of groups and their attributes at highest level (available to superadmins only).", "app.homepage.groupsTeacherPage": "Create or bind groups for your courses in SIS.", + "app.homepage.loginFailed": "Authentication process failed", "app.homepage.processingToken": "Processing authentication token...", "app.homepage.processingTokenFailed": "Authentication process failed.", "app.homepage.returnToReCodEx": "Return to ReCodEx...", "app.homepage.termsPage": "Management of terms and their related dates (when they are active for students and teachers).", "app.homepage.title": "SiS-CodEx Extension", "app.homepage.userPage": "The personal data integration page allows updating ReCodEx user profile (name, titles, email) using data from SIS.", - "app.homepage.userSessionExpired": "Your user session has expired", + "app.homepage.userSessionExpired": "Your user session has expired or you have logged out", "app.homepage.userSessionExpiredInfo": "You need to initialize a new session by re-entering this application from ReCodEx.", "app.localizedTexts.validation.noLocalizedText": "Please enable at least one tab of localized texts.", "app.notifications.hideAll": "Only new notifications", @@ -267,4 +268,4 @@ "generic.operationFailed": "The operation has failed", "generic.reset": "Reset", "generic.save": "Save" -} +} \ No newline at end of file diff --git a/src/pages/Home/Home.js b/src/pages/Home/Home.js index 97fab4b..9df0d11 100644 --- a/src/pages/Home/Home.js +++ b/src/pages/Home/Home.js @@ -23,10 +23,10 @@ import Icon, { import Callout from '../../components/widgets/Callout'; import { setLang } from '../../redux/modules/app.js'; -import { login, logout } from '../../redux/modules/auth.js'; +import { login, logout, statusTypes as authStatusTypes } from '../../redux/modules/auth.js'; import { fetchUserIfNeeded } from '../../redux/modules/users.js'; import { loggedInUserSelector } from '../../redux/selectors/users.js'; -import { loggedInUserIdSelector } from '../../redux/selectors/auth.js'; +import { loggedInUserIdSelector, loginStatusSelector } from '../../redux/selectors/auth.js'; import { getReturnUrl, setReturnUrl } from '../../helpers/localStorage.js'; import { knownLocalesNames } from '../../helpers/localizedData.js'; @@ -85,11 +85,12 @@ class Home extends Component { render() { const { loggedInUser, + loginStatus, params: { token = null }, links: { USER_URI, TERMS_URI, GROUPS_STUDENT_URI, GROUPS_TEACHER_URI, GROUPS_SUPERADMIN_URI }, } = this.props; - if (!loggedInUser && !token) { + if (!loggedInUser && (!token || loginStatus === authStatusTypes.LOGIN_FAILED)) { return ( } @@ -97,7 +98,14 @@ class Home extends Component { windowTitle={}>

- + {!token ? ( + + ) : ( + + )}

({ loggedInUserId: loggedInUserIdSelector(state), loggedInUser: loggedInUserSelector(state), + loginStatus: loginStatusSelector(state), }), dispatch => ({ loadAsync: userId => Home.loadAsync({ userId }, dispatch), diff --git a/src/redux/selectors/auth.js b/src/redux/selectors/auth.js index 98340ad..2775503 100644 --- a/src/redux/selectors/auth.js +++ b/src/redux/selectors/auth.js @@ -15,7 +15,7 @@ export const accessTokenExpiration = createSelector(accessTokenSelector, token = ); export const loggedInUserIdSelector = createSelector(getAuth, getLoggedInUserId); -export const loginStatusSelector = createSelector(getAuth, auth => auth.getIn(['status'])); +export const loginStatusSelector = createSelector(getAuth, auth => auth.getIn(['status', 'recodex'])); export const loginErrorSelector = createSelector(getAuth, auth => auth.getIn(['errors', 'recodex'])?.toJS() || null); From 56540505fdba7142b2154d13d0b393641db5bb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kruli=C5=A1?= Date: Thu, 12 Feb 2026 22:47:57 +0100 Subject: [PATCH 2/2] Fixing proper error reporting in GroupsTeacher page for group creation/binding operations. --- src/locales/cs.json | 1 + src/locales/en.json | 1 + src/pages/GroupsTeacher/GroupsTeacher.js | 57 ++++++++++++++++-------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/locales/cs.json b/src/locales/cs.json index 980506f..a50c80e 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -122,6 +122,7 @@ "app.groupsTeacher.createParentGroupLabelExplanation": "Zobrazují se pouze skupiny, které jsou umístěny zároveň pod skupinou předmětu `{course}` a skupinou semestru `{term}`.", "app.groupsTeacher.lastRefreshInfo": "Seznam předmětů, které vyučujete, byl naposledy stažen ze SISu", "app.groupsTeacher.noActiveTerms": "V tuto chvíli nejsou učitelům dostupné žádné semestry.", + "app.groupsTeacher.noGroupsForSelection": "Nejsou dostupné žádné vhodné skupiny pro výběr...", "app.groupsTeacher.notTeacher": "Tato stránka je dostupná pouze učitelům.", "app.groupsTeacher.scheduledAt": "Rozvrženo na", "app.groupsTeacher.selectGroupForBinding": "Vyberte skupinu pro svázání", diff --git a/src/locales/en.json b/src/locales/en.json index 4bc5e23..b55b28b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -122,6 +122,7 @@ "app.groupsTeacher.createParentGroupLabelExplanation": "Only groups that are located in the hierarchy under both `{course}` course group and `{term}` term group are listed.", "app.groupsTeacher.lastRefreshInfo": "The list of courses taught by you was last downloaded from SIS", "app.groupsTeacher.noActiveTerms": "There are currently no terms available for teachers.", + "app.groupsTeacher.noGroupsForSelection": "There are no suitable groups to select from...", "app.groupsTeacher.notTeacher": "This page is available only to teachers.", "app.groupsTeacher.scheduledAt": "Scheduled at", "app.groupsTeacher.selectGroupForBinding": "Select Group for Binding", diff --git a/src/pages/GroupsTeacher/GroupsTeacher.js b/src/pages/GroupsTeacher/GroupsTeacher.js index f04366b..9c4218d 100644 --- a/src/pages/GroupsTeacher/GroupsTeacher.js +++ b/src/pages/GroupsTeacher/GroupsTeacher.js @@ -86,9 +86,15 @@ class GroupsTeacher extends Component { selectedGroupId: '', }; - startBind = (bindEvent, selectGroups) => this.setState({ bindEvent, selectGroups }); + startBind = (bindEvent, selectGroups) => { + const selectedGroupId = selectGroups[0]?.id || ''; + this.setState({ bindEvent, selectGroups, selectedGroupId }); + }; - startCreate = (createEvent, selectGroups) => this.setState({ createEvent, selectGroups }); + startCreate = (createEvent, selectGroups) => { + const selectedGroupId = selectGroups[0]?.id || ''; + this.setState({ createEvent, selectGroups, selectedGroupId }); + }; closeModal = () => this.setState({ @@ -102,25 +108,29 @@ class GroupsTeacher extends Component { handleGroupChange = ev => this.setState({ selectedGroupId: ev.target.value }); - completeModalOperation = () => { + completeModalOperation = async () => { const { loggedInUserId, bind, create, loadAsync } = this.props; - this.setState({ modalPending: true }); - if (this.state.bindEvent !== null) { - bind(this.state.selectedGroupId, this.state.bindEvent).then( - () => loadAsync(loggedInUserId).then(this.closeModal), - error => this.setState({ modalPending: false, modalError: error?.message || 'unknown error' }) - ); - } else if (this.state.createEvent !== null) { - create(this.state.selectedGroupId, this.state.createEvent) - .then( - () => loadAsync(loggedInUserId), - error => this.setState({ modalPending: false, modalError: error?.message || 'unknown error' }) - ) - .then(this.closeModal); - } else { + if (!this.state.bindEvent && !this.state.createEvent) { this.closeModal(); + return; + } + + this.setState({ modalPending: true }); + + try { + if (this.state.bindEvent !== null) { + await bind(this.state.selectedGroupId, this.state.bindEvent); + } else if (this.state.createEvent !== null) { + await create(this.state.selectedGroupId, this.state.createEvent); + } + } catch (error) { + this.setState({ modalPending: false, modalError: error?.message || 'unknown error' }); + return; } + + await loadAsync(loggedInUserId); + this.closeModal(); }; unbindAndReload = (groupId, eventId) => { @@ -376,10 +386,10 @@ class GroupsTeacher extends Component { - {this.state.selectGroups?.map(group => ( ))} + + {!this.state.selectGroups?.length && ( + + )}