diff --git a/kolibri/core/package.json b/kolibri/core/package.json index 6a20aea0e41..67d36977590 100644 --- a/kolibri/core/package.json +++ b/kolibri/core/package.json @@ -21,7 +21,7 @@ "js-cookie": "^3.0.1", "knuth-shuffle-seeded": "^1.0.6", "kolibri-constants": "0.1.42", - "kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#4b8f272d37446ff0e65c4048c0339e279976103b", + "kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#a671f6f898eef07a3167dc74648ef329f8e3af44", "lockr": "0.8.5", "lodash": "^4.17.21", "loglevel": "^1.8.1", diff --git a/kolibri/plugins/coach/assets/src/composables/useCoachTabs.js b/kolibri/plugins/coach/assets/src/composables/useCoachTabs.js new file mode 100644 index 00000000000..53671ee29eb --- /dev/null +++ b/kolibri/plugins/coach/assets/src/composables/useCoachTabs.js @@ -0,0 +1,83 @@ +/** + * Provides a place to store last tabs interaction + * and helper methods to save and retrieve it. + * This information is typically used to find out + * if a tab was clicked recently so that we can decide + * on whether we should programatically re-focus it after + * navigating to a page. + * + * Motivation: Our routing architecture where parts of a page + * containing tabs get reloaded on route change would result + * in losing focus from the active tab. Therefore, we need + * to programatically re-focus after components are mounted again. + * However, for that we need to be able to estimate when navigation + * occured as a result of user interaction with tabs because in other + * cases focus shouldn't be manipulated (e.g. when visiting a page for + * the first time before clicking on tabs) . + * + * Usage: When tabs are clicked, save that interaction by calling `saveTabsClick`. + * Then when you need find out if tabs were clicked recently, + * call `wereTabsClickedRecently`. + */ +import { reactive } from 'kolibri.lib.vueCompositionApi'; + +// tabs interaction is considered to be recent +// when it's not older than ... +const RECENT_INTERACTION_LIMIT_IN_MS = 3000; +const TabsEvents = { + CLICK: 'click', +}; + +/** + * Needs to be placed outside of the `useCoachTabs` + * function so that it behaves like global state. + * Why: Due to our routing structure, tab components are re-mounted + * after a tab is clicked. Without placing this state outside, it would + * be lost as components that use this composable are initialized again. + */ +const lastTabsInteraction = reactive({ + tabsInterfaceId: '', + event: '', + timestamp: '', +}); + +export function useCoachTabs() { + /** + * Stores an interaction with tabs + * + * @param {String} tabsInterfaceId ID of a tabbed interface interacted with + * @param {String} event An event kind. Available kinds: 'click' + */ + function saveTabsInteraction(tabsInterfaceId, event) { + lastTabsInteraction.tabsInterfaceId = tabsInterfaceId; + lastTabsInteraction.event = event; + lastTabsInteraction.timestamp = Date.now(); + } + + /** + * Stores a click interaction with tabs + * + * @param {String} tabsInterfaceId ID of a tabbed interface interacted with + */ + function saveTabsClick(tabsInterfaceId) { + saveTabsInteraction(tabsInterfaceId, TabsEvents.CLICK); + } + + /** + * @param {String} tabsInterfaceId ID of a tabbed interface + * @returns {Boolean} `true` when tabs with the provided ID were + * clicked recently + */ + function wereTabsClickedRecently(tabsInterfaceId) { + if (lastTabsInteraction.tabsInterfaceId !== tabsInterfaceId) { + return false; + } + const diff = Math.abs(Date.now() - lastTabsInteraction.timestamp); + return diff < RECENT_INTERACTION_LIMIT_IN_MS; + } + + return { + saveTabsClick, + wereTabsClickedRecently, + }; +} diff --git a/kolibri/plugins/coach/assets/src/constants/tabsConstants.js b/kolibri/plugins/coach/assets/src/constants/tabsConstants.js new file mode 100644 index 00000000000..b29e1ccf3fd --- /dev/null +++ b/kolibri/plugins/coach/assets/src/constants/tabsConstants.js @@ -0,0 +1,21 @@ +export const REPORTS_TABS_ID = 'coachReports'; +export const ReportsTabs = { + LESSONS: 'tabLessons', + QUIZZES: 'tabQuizzes', + GROUPS: 'tabGroups', + LEARNERS: 'tabLearners', +}; + +export const PLAN_TABS_ID = 'coachPlan'; +export const PlanTabs = { + LESSONS: 'tabLessons', + QUIZZES: 'tabQuizzes', + GROUPS: 'tabGroups', +}; + +export const REPORTS_GROUP_TABS_ID = 'coachPlanGroup'; +export const ReportsGroupTabs = { + REPORTS: 'tabReports', + MEMBERS: 'tabMembers', + ACTIVITY: 'tabActivity', +}; diff --git a/kolibri/plugins/coach/assets/src/views/plan/CoachExamsPage/index.vue b/kolibri/plugins/coach/assets/src/views/plan/CoachExamsPage/index.vue index 9900c9f5d09..d08b3cd4e50 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/CoachExamsPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/CoachExamsPage/index.vue @@ -7,153 +7,157 @@ > - - -
-

- {{ $tr('totalQuizSize', { size: calcTotalSizeOfVisibleQuizzes }) }} -

- - - - - - -
- + +
+

+ {{ $tr('totalQuizSize', { size: calcTotalSizeOfVisibleQuizzes }) }} +

+ -
-
- - - + + +
+ +
+
+ + + - + + + + -

- {{ $tr('noExams') }} -

-

- {{ $tr('noStartedExams') }} -

-

- {{ $tr('noExamsNotStarted') }} -

-

- {{ $tr('noEndedExams') }} -

+

+ {{ $tr('noExams') }} +

+

+ {{ $tr('noStartedExams') }} +

+

+ {{ $tr('noExamsNotStarted') }} +

+

+ {{ $tr('noEndedExams') }} +

- - -

{{ coachString('openQuizModalDetail') }}

-

{{ coachString('lodQuizDetail') }}

-

{{ coachString('fileSizeToDownload', { size: quizSize(activeQuizId) }) }}

-
- -
{{ coachString('closeQuizModalDetail') }}
-
+ + +

{{ coachString('openQuizModalDetail') }}

+

{{ coachString('lodQuizDetail') }}

+

{{ coachString('fileSizeToDownload', { size: quizSize(activeQuizId) }) }}

+
+ +
{{ coachString('closeQuizModalDetail') }}
+
+
@@ -169,6 +173,7 @@ import plugin_data from 'plugin_data'; import bytesForHumans from 'kolibri.utils.bytesForHumans'; import { PageNames } from '../../../constants'; + import { PLAN_TABS_ID, PlanTabs } from '../../../constants/tabsConstants'; import commonCoach from '../../common'; import CoachAppBarPage from '../../CoachAppBarPage'; import PlanHeader from '../../plan/PlanHeader'; @@ -183,6 +188,8 @@ mixins: [commonCoach, commonCoreStrings], data() { return { + PLAN_TABS_ID, + PlanTabs, statusSelected: { label: this.coachString('filterQuizAll'), value: this.coachString('filterQuizAll'), diff --git a/kolibri/plugins/coach/assets/src/views/plan/GroupsPage/index.vue b/kolibri/plugins/coach/assets/src/views/plan/GroupsPage/index.vue index f25ac4371ad..daa81efcba6 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/GroupsPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/GroupsPage/index.vue @@ -6,61 +6,65 @@ :showSubNav="true" > - -
- + +
+ +
+ + + + + + +

+ {{ $tr('noGroups') }} +

+ + -
- - - - - - -

- {{ $tr('noGroups') }} -

- - - - - - + + + +
@@ -78,6 +82,7 @@ import CoachAppBarPage from '../../CoachAppBarPage'; import PlanHeader from '../../plan/PlanHeader'; import { GroupModals } from '../../../constants'; + import { PLAN_TABS_ID, PlanTabs } from '../../../constants/tabsConstants'; import CreateGroupModal from './CreateGroupModal'; import GroupRowTr from './GroupRow'; import RenameGroupModal from './RenameGroupModal'; @@ -102,6 +107,8 @@ }); return { + PLAN_TABS_ID, + PlanTabs, selectedGroup, setSelectedGroup(name, id) { selectedGroup.value = { name, id }; diff --git a/kolibri/plugins/coach/assets/src/views/plan/LessonSummaryPage/index.vue b/kolibri/plugins/coach/assets/src/views/plan/LessonSummaryPage/index.vue index 0f1fa0c20a2..0b879a1be70 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/LessonSummaryPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/LessonSummaryPage/index.vue @@ -22,6 +22,9 @@ +

+ {{ coachString('generalInformationLabel') }} +

- -

- {{ coachString('totalLessonsSize', { size: calcTotalSizeOfVisibleLessons }) }} -

-
- - -
+ + +

+ {{ coachString('totalLessonsSize', { size: calcTotalSizeOfVisibleLessons }) }} +

+
+ + +
- - - - + + + + -

- {{ $tr('noLessons') }} -

-

- {{ $tr('noVisibleLessons') }} -

- -

{{ coachString('makeLessonVisibleText') }}

-

{{ $tr('fileSizeToDownload', { size: lessonSize(activeLesson.id) }) }}

- -
+

+ {{ $tr('noLessons') }} +

+

+ {{ $tr('noVisibleLessons') }} +

+ +

{{ coachString('makeLessonVisibleText') }}

+

{{ $tr('fileSizeToDownload', { size: lessonSize(activeLesson.id) }) }}

+ +
- -

{{ coachString('makeLessonNotVisibleText') }}

-

{{ $tr('fileSizeToRemove', { size: lessonSize(activeLesson.id) }) }}

- -
+ +

{{ coachString('makeLessonNotVisibleText') }}

+

{{ $tr('fileSizeToRemove', { size: lessonSize(activeLesson.id) }) }}

+ +
- - - + @submit="$refs.detailsModal.submitData()" + > + + +
@@ -156,6 +161,7 @@ import bytesForHumans from 'kolibri.utils.bytesForHumans'; import CoachAppBarPage from '../../CoachAppBarPage'; import { LessonsPageNames } from '../../../constants/lessonsConstants'; + import { PLAN_TABS_ID, PlanTabs } from '../../../constants/tabsConstants'; import commonCoach from '../../common'; import PlanHeader from '../../plan/PlanHeader'; import AssignmentDetailsModal from '../../plan/assignments/AssignmentDetailsModal'; @@ -172,6 +178,8 @@ mixins: [commonCoach, commonCoreStrings], data() { return { + PLAN_TABS_ID, + PlanTabs, showModal: false, showLessonIsVisibleModal: false, showLessonIsNotVisibleModal: false, @@ -261,7 +269,6 @@ const snackbarMessage = newActiveState ? this.coachString('lessonVisibleToLearnersLabel') : this.coachString('lessonNotVisibleToLearnersLabel'); - const promise = LessonResource.saveModel({ id: lesson.id, data: { @@ -269,9 +276,7 @@ }, exists: true, }); - this.manageModalVisibilityAndPreferences(); - return promise.then(() => { this.$store.dispatch('lessonsRoot/refreshClassLessons', this.$route.params.classId); this.$store.dispatch('createSnackbar', snackbarMessage); diff --git a/kolibri/plugins/coach/assets/src/views/plan/PlanHeader.vue b/kolibri/plugins/coach/assets/src/views/plan/PlanHeader.vue index b137442caff..c44aaef5ed8 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/PlanHeader.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/PlanHeader.vue @@ -10,18 +10,15 @@

{{ $tr('planYourClassLabel') }}

{{ $tr('planYourClassDescription') }}

- - - - + @@ -34,16 +31,68 @@ import { mapGetters } from 'vuex'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; import commonCoach from '../common'; + import { PageNames } from '../../constants'; import { LessonsPageNames } from '../../constants/lessonsConstants'; + import { PLAN_TABS_ID, PlanTabs } from '../../constants/tabsConstants'; + import { useCoachTabs } from '../../composables/useCoachTabs'; export default { name: 'PlanHeader', mixins: [commonCoach, commonCoreStrings], + setup() { + const { saveTabsClick, wereTabsClickedRecently } = useCoachTabs(); + return { + saveTabsClick, + wereTabsClickedRecently, + }; + }, + props: { + activeTabId: { + type: String, + required: true, + }, + }, + data() { + return { + PLAN_TABS_ID, + }; + }, computed: { ...mapGetters(['classListPageEnabled']), LessonsPageNames() { return LessonsPageNames; }, + tabs() { + return [ + { + id: PlanTabs.LESSONS, + label: this.coreString('lessonsLabel'), + to: this.classRoute(this.LessonsPageNames.PLAN_LESSONS_ROOT), + }, + { + id: PlanTabs.QUIZZES, + label: this.coreString('quizzesLabel'), + to: this.classRoute(PageNames.EXAMS), + }, + { + id: PlanTabs.GROUPS, + label: this.coachString('groupsLabel'), + to: this.classRoute('GroupsPage'), + }, + ]; + }, + }, + mounted() { + // focus the active tab but only when it's likely + // that this header was re-mounted as a result + // of navigation after clicking a tab (focus shouldn't + // be manipulated programatically in other cases, e.g. + // when visiting the Plan page for the first time) + if (this.wereTabsClickedRecently(this.PLAN_TABS_ID)) { + this.$nextTick(() => { + this.$refs.tabsList.focusActiveTab(); + }); + } }, $trs: { planYourClassLabel: { diff --git a/kolibri/plugins/coach/assets/src/views/plan/PlanLearnerListPage.vue b/kolibri/plugins/coach/assets/src/views/plan/PlanLearnerListPage.vue deleted file mode 100644 index 42bb7a98648..00000000000 --- a/kolibri/plugins/coach/assets/src/views/plan/PlanLearnerListPage.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - diff --git a/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue b/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue index b5ea260651b..f3ee7a54f93 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue @@ -22,6 +22,9 @@
+

+ {{ coachString('generalInformationLabel') }} +

- - - + + + @@ -24,6 +31,7 @@ diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupHeader.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupHeader.vue index 80a3af6bed0..8f6da8cdba7 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupHeader.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupHeader.vue @@ -11,18 +11,18 @@ - - - - + @@ -34,21 +34,70 @@ import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; import commonCoach from '../common'; + import { REPORTS_GROUP_TABS_ID, ReportsGroupTabs } from '../../constants/tabsConstants'; + import { useCoachTabs } from '../../composables/useCoachTabs'; export default { name: 'ReportsGroupHeader', mixins: [commonCoach, commonCoreStrings], + setup() { + const { saveTabsClick, wereTabsClickedRecently } = useCoachTabs(); + return { + saveTabsClick, + wereTabsClickedRecently, + }; + }, props: { + activeTabId: { + type: String, + required: true, + }, enablePrint: { type: Boolean, required: false, default: false, }, }, + data() { + return { + REPORTS_GROUP_TABS_ID, + }; + }, computed: { group() { return this.groupMap[this.$route.params.groupId]; }, + tabs() { + return [ + { + id: ReportsGroupTabs.REPORTS, + label: this.coachString('reportsLabel'), + to: this.classRoute('ReportsGroupReportPage', {}), + }, + { + id: ReportsGroupTabs.MEMBERS, + label: this.coachString('membersLabel'), + to: this.classRoute('ReportsGroupLearnerListPage', {}), + }, + { + id: ReportsGroupTabs.ACTIVITY, + label: this.coachString('activityLabel'), + to: this.classRoute('ReportsGroupActivityPage', {}), + }, + ]; + }, + }, + mounted() { + // focus the active tab but only when it's likely + // that this header was re-mounted as a result + // of navigation after clicking a tab (focus shouldn't + // be manipulated programatically in other cases, e.g. + // when visiting the Group page for the first time) + if (this.wereTabsClickedRecently(this.REPORTS_GROUP_TABS_ID)) { + this.$nextTick(() => { + this.$refs.tabsList.focusActiveTab(); + }); + } }, $trs: { back: { diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupLearnerListPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupLearnerListPage.vue index 6d86ba1144e..93c1325a340 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupLearnerListPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupLearnerListPage.vue @@ -7,44 +7,52 @@ > - - - - - + + @@ -55,6 +63,7 @@ import sortBy from 'lodash/sortBy'; import commonCoach from '../common'; + import { REPORTS_GROUP_TABS_ID, ReportsGroupTabs } from '../../constants/tabsConstants'; import CoachAppBarPage from '../CoachAppBarPage'; import CSVExporter from '../../csv/exporter'; import * as csvFields from '../../csv/fields'; @@ -69,6 +78,12 @@ ReportsControls, }, mixins: [commonCoach], + data() { + return { + REPORTS_GROUP_TABS_ID, + ReportsGroupTabs, + }; + }, computed: { group() { return this.groupMap[this.$route.params.groupId]; diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupListPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupListPage.vue index b81e2874f2d..11c13f49bd3 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupListPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupListPage.vue @@ -7,52 +7,60 @@ > - - - - - + + @@ -64,6 +72,7 @@ import ElapsedTime from 'kolibri.coreVue.components.ElapsedTime'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; import sortBy from 'lodash/sortBy'; + import { REPORTS_TABS_ID, ReportsTabs } from '../../constants/tabsConstants'; import commonCoach from '../common'; import CoachAppBarPage from '../CoachAppBarPage'; import CSVExporter from '../../csv/exporter'; @@ -80,6 +89,12 @@ ElapsedTime, }, mixins: [commonCoach, commonCoreStrings], + data() { + return { + REPORTS_TABS_ID, + ReportsTabs, + }; + }, computed: { table() { const sorted = sortBy(this.groups, ['name']); diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupReportPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupReportPage.vue index 04dcc1cb04f..517b22f2782 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupReportPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsGroupReportPage.vue @@ -8,47 +8,52 @@ - - - - -

{{ coachString('lessonsAssignedLabel') }}

-
    -
  • - -
  • -
-

- {{ coachString('lessonListEmptyState') }} -

-
- -

{{ coachString('quizzesAssignedLabel') }}

-
    -
  • - -
  • -
-

- {{ coachString('quizListEmptyState') }} -

-
-
- + + + + +

{{ coachString('lessonsAssignedLabel') }}

+
    +
  • + +
  • +
+

+ {{ coachString('lessonListEmptyState') }} +

+
+ +

{{ coachString('quizzesAssignedLabel') }}

+
    +
  • + +
  • +
+

+ {{ coachString('quizListEmptyState') }} +

+
+
+
@@ -59,6 +64,7 @@ import commonCoach from '../common'; import CoachAppBarPage from '../CoachAppBarPage'; + import { REPORTS_GROUP_TABS_ID, ReportsGroupTabs } from '../../constants/tabsConstants'; import ReportsGroupHeader from './ReportsGroupHeader'; export default { @@ -68,6 +74,12 @@ ReportsGroupHeader, }, mixins: [commonCoach], + data() { + return { + REPORTS_GROUP_TABS_ID, + ReportsGroupTabs, + }; + }, computed: { lessonsList() { const filtered = this.lessons.filter(lesson => this.isAssigned(lesson.groups)); diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsHeader.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsHeader.vue index a32bdf452b9..a245b52d165 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsHeader.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsHeader.vue @@ -12,23 +12,15 @@

{{ $tr('description') }}

- - - - - - + @@ -41,21 +33,75 @@ import { mapGetters } from 'vuex'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; import commonCoach from '../common'; + import { REPORTS_TABS_ID, ReportsTabs } from '../../constants/tabsConstants'; + import { useCoachTabs } from '../../composables/useCoachTabs'; export default { name: 'ReportsHeader', mixins: [commonCoach, commonCoreStrings], + setup() { + const { saveTabsClick, wereTabsClickedRecently } = useCoachTabs(); + return { + saveTabsClick, + wereTabsClickedRecently, + }; + }, props: { title: { type: String, default: null, }, + activeTabId: { + type: String, + required: true, + }, + }, + data() { + return { + REPORTS_TABS_ID, + }; }, computed: { ...mapGetters(['classListPageEnabled']), reportTitle() { return this.title || this.coachString('reportsLabel'); }, + tabs() { + return [ + { + id: ReportsTabs.LESSONS, + label: this.coreString('lessonsLabel'), + to: this.classRoute('ReportsLessonListPage'), + }, + { + id: ReportsTabs.QUIZZES, + label: this.coreString('quizzesLabel'), + to: this.classRoute('ReportsQuizListPage'), + }, + { + id: ReportsTabs.GROUPS, + label: this.coachString('groupsLabel'), + to: this.classRoute('ReportsGroupListPage'), + }, + { + id: ReportsTabs.LEARNERS, + label: this.coreString('learnersLabel'), + to: this.classRoute('ReportsLearnerListPage'), + }, + ]; + }, + }, + mounted() { + // focus the active tab but only when it's likely + // that this header was re-mounted as a result + // of navigation after clicking a tab (focus shouldn't + // be manipulated programatically in other cases, e.g. + // when visiting the Plan page for the first time) + if (this.wereTabsClickedRecently(this.REPORTS_TABS_ID)) { + this.$nextTick(() => { + this.$refs.tabsList.focusActiveTab(); + }); + } }, $trs: { description: { diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsLearnerListPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsLearnerListPage.vue index fa29dd49841..11df96898b5 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsLearnerListPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsLearnerListPage.vue @@ -7,48 +7,56 @@ > - - - - - + + @@ -60,6 +68,7 @@ import sortBy from 'lodash/sortBy'; import ElapsedTime from 'kolibri.coreVue.components.ElapsedTime'; import commonCoach from '../common'; + import { REPORTS_TABS_ID, ReportsTabs } from '../../constants/tabsConstants'; import CoachAppBarPage from '../CoachAppBarPage'; import CSVExporter from '../../csv/exporter'; import * as csvFields from '../../csv/fields'; @@ -75,6 +84,12 @@ ElapsedTime, }, mixins: [commonCoach], + data() { + return { + REPORTS_TABS_ID, + ReportsTabs, + }; + }, computed: { table() { const sorted = sortBy(this.learners, ['name']); diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonBase.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonBase.vue index 9ecbb05378f..1be0dc1f2bd 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonBase.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonBase.vue @@ -24,6 +24,9 @@
+

+ {{ coachString('generalInformationLabel') }} +

+

+ {{ coachString('detailsLabel') }} +

diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonListPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonListPage.vue index e0fc37cafb3..211ac5324a5 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonListPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsLessonListPage.vue @@ -7,107 +7,115 @@ > - +

{{ coachString('totalLessonsSize', { size: calcTotalSizeOfVisibleLessons }) }}

- - + + + - - - - + + +

{{ coachString('makeLessonVisibleText') }}

+

{{ coachString('fileSizeToDownload', { size: lessonSize(activeLesson.id) }) }}

+ +
- -

{{ coachString('makeLessonNotVisibleText') }}

-

{{ coachString('fileSizeToRemove', { size: lessonSize(activeLesson.id) }) }}

- -
+ +

{{ coachString('makeLessonNotVisibleText') }}

+

{{ coachString('fileSizeToRemove', { size: lessonSize(activeLesson.id) }) }}

+ +
+
@@ -123,6 +131,7 @@ import Lockr from 'lockr'; import bytesForHumans from 'kolibri.utils.bytesForHumans'; import commonCoach from '../common'; + import { REPORTS_TABS_ID, ReportsTabs } from '../../constants/tabsConstants'; import CoachAppBarPage from '../CoachAppBarPage'; import CSVExporter from '../../csv/exporter'; import * as csvFields from '../../csv/fields'; @@ -139,6 +148,8 @@ mixins: [commonCoach, commonCoreStrings], data() { return { + REPORTS_TABS_ID, + ReportsTabs, filter: 'allLessons', showLessonIsVisibleModal: false, showLessonIsNotVisibleModal: false, @@ -158,7 +169,6 @@ if (this.filter.value === 'lessonsNotVisible') { return this.$tr('lessonsNotVisible'); } - return ''; }, userHasDismissedModal() { @@ -228,7 +238,6 @@ const snackbarMessage = newActiveState ? this.coachString('lessonVisibleToLearnersLabel') : this.coachString('lessonNotVisibleToLearnersLabel'); - const promise = LessonResource.saveModel({ id: lesson.id, data: { @@ -236,9 +245,7 @@ }, exists: true, }); - this.manageModalVisibilityAndPreferences(); - return promise.then(() => { this.$store.dispatch('classSummary/refreshClassSummary'); this.$store.dispatch('createSnackbar', snackbarMessage); @@ -250,7 +257,6 @@ ...csvFields.recipients(this.className), ...csvFields.tally(), ]; - const fileName = this.$tr('printLabel', { className: this.className }); new CSVExporter(columns, fileName).export(this.table); }, diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizBaseListPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizBaseListPage.vue index 0c9541b0a32..61046ce8a5a 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizBaseListPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizBaseListPage.vue @@ -24,6 +24,9 @@
+

+ {{ coachString('generalInformationLabel') }} +

+

+ {{ coachString('detailsLabel') }} +

diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizListPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizListPage.vue index c5445ac9cc1..5adc6dcd34f 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizListPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizListPage.vue @@ -7,125 +7,133 @@ > - - -

- {{ $tr('totalQuizSize', { size: calcTotalSizeOfVisibleQuizzes }) }} -

- + + + +

+ {{ $tr('totalQuizSize', { size: calcTotalSizeOfVisibleQuizzes }) }} +

+ -
- - - + + + +

{{ coachString('openQuizModalDetail') }}

+

{{ coachString('lodQuizDetail') }}

+

{{ coachString('fileSizeToDownload', { size: quizSize(modalQuizId) }) }}

+
+ +
{{ coachString('closeQuizModalDetail') }}
+
+
@@ -138,6 +146,7 @@ import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; import { ExamResource } from 'kolibri.resources'; import bytesForHumans from 'kolibri.utils.bytesForHumans'; + import { REPORTS_TABS_ID, ReportsTabs } from '../../constants/tabsConstants'; import commonCoach from '../common'; import CoachAppBarPage from '../CoachAppBarPage'; import CSVExporter from '../../csv/exporter'; @@ -155,6 +164,8 @@ mixins: [commonCoach, commonCoreStrings], data() { return { + REPORTS_TABS_ID, + ReportsTabs, filter: 'allQuizzes', showOpenConfirmationModal: false, showCloseConfirmationModal: false, diff --git a/packages/kolibri-core-for-export/package.json b/packages/kolibri-core-for-export/package.json index c88ff21c35d..deb5869be74 100644 --- a/packages/kolibri-core-for-export/package.json +++ b/packages/kolibri-core-for-export/package.json @@ -24,7 +24,7 @@ "intl": "^1.2.4", "knuth-shuffle-seeded": "^1.0.6", "kolibri-constants": "0.1.42", - "kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#4b8f272d37446ff0e65c4048c0339e279976103b", + "kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#a671f6f898eef07a3167dc74648ef329f8e3af44", "lockr": "0.8.5", "lodash": "^4.17.21", "loglevel": "^1.8.1", diff --git a/yarn.lock b/yarn.lock index 872bab3242b..b9afdbb534b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7386,9 +7386,9 @@ kolibri-constants@0.1.42: resolved "https://registry.yarnpkg.com/kolibri-constants/-/kolibri-constants-0.1.42.tgz#2f62a8d8b8894e5cfbd47ee6564b31018818c93f" integrity sha512-2hUxYnzUEfhLFJO9egVSwYW8/PKob9wLeDYfB74mtIzgQ4zy6huRj3574WetK9gREi+W1Jcm7HGPsfZIFzFvrA== -"kolibri-design-system@https://github.com/learningequality/kolibri-design-system#4b8f272d37446ff0e65c4048c0339e279976103b": +"kolibri-design-system@https://github.com/learningequality/kolibri-design-system#a671f6f898eef07a3167dc74648ef329f8e3af44": version "1.3.0" - resolved "https://github.com/learningequality/kolibri-design-system#4b8f272d37446ff0e65c4048c0339e279976103b" + resolved "https://github.com/learningequality/kolibri-design-system#a671f6f898eef07a3167dc74648ef329f8e3af44" dependencies: aphrodite "https://github.com/learningequality/aphrodite/" autosize "^3.0.21"