diff --git a/scripts/langindex.json b/scripts/langindex.json
index 7dbfa28cf..2ad44374b 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -1582,6 +1582,7 @@
"core.course.completion_setby:auto:todo": "course",
"core.course.completion_setby:manual:done": "course",
"core.course.completion_setby:manual:markdone": "course",
+ "core.course.completionmenuitem": "completion",
"core.course.completionrequirements": "course",
"core.course.confirmdownload": "local_moodlemobileapp",
"core.course.confirmdownloadunknownsize": "local_moodlemobileapp",
@@ -1620,6 +1621,7 @@
"core.course.relativedatessubmissionduedatebefore": "course",
"core.course.section": "moodle",
"core.course.startdate": "moodle",
+ "core.course.studentsmust": "completion",
"core.course.thisweek": "format_weeks/currentsection",
"core.course.todo": "completion",
"core.course.tour_navigation_course_index_student_content": "tool_usertours",
@@ -1628,6 +1630,7 @@
"core.course.viewcourse": "block_timeline",
"core.course.warningmanualcompletionmodified": "local_moodlemobileapp",
"core.course.warningofflinemanualcompletiondeleted": "local_moodlemobileapp",
+ "core.course.youmust": "completion",
"core.coursedetails": "moodle",
"core.coursenogroups": "local_moodlemobileapp",
"core.courses.addtofavourites": "block_myoverview",
@@ -2313,8 +2316,9 @@
"core.scanqr": "local_moodlemobileapp",
"core.scrollbackward": "local_moodlemobileapp",
"core.scrollforward": "local_moodlemobileapp",
- "core.search.allcourses": "search",
+ "core.search": "moodle",
"core.search.allcategories": "local_moodlemobileapp",
+ "core.search.allcourses": "search",
"core.search.empty": "local_moodlemobileapp",
"core.search.filtercategories": "local_moodlemobileapp",
"core.search.filtercourses": "local_moodlemobileapp",
@@ -2323,7 +2327,6 @@
"core.search.noresults": "local_moodlemobileapp",
"core.search.noresultshelp": "local_moodlemobileapp",
"core.search.resultby": "local_moodlemobileapp",
- "core.search": "moodle",
"core.searching": "local_moodlemobileapp",
"core.searchresults": "moodle",
"core.sec": "moodle",
diff --git a/src/addons/mod/resource/services/handlers/module.ts b/src/addons/mod/resource/services/handlers/module.ts
index 8d112fb72..2e2bfbeb0 100644
--- a/src/addons/mod/resource/services/handlers/module.ts
+++ b/src/addons/mod/resource/services/handlers/module.ts
@@ -220,6 +220,15 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
return extra.join(' ');
}
+ /**
+ * @inheritdoc
+ */
+ async manualCompletionAlwaysShown(module: CoreCourseModuleData): Promise {
+ const hideButton = await this.hideOpenButton(module);
+
+ return !hideButton;
+ }
+
/**
* @inheritdoc
*/
diff --git a/src/core/components/combobox/combobox.scss b/src/core/components/combobox/combobox.scss
index e270c8528..bb19aa559 100644
--- a/src/core/components/combobox/combobox.scss
+++ b/src/core/components/combobox/combobox.scss
@@ -131,25 +131,4 @@ ion-button {
ion-icon {
margin: var(--icon-margin);
}
-
- .select-icon {
- margin: var(--icon-margin);
- width: 19px;
- height: 19px;
- position: relative;
-
- .select-icon-inner {
- left: 5px;
- top: 50%;
- margin-top: -2px;
- position: absolute;
- width: 0px;
- height: 0px;
- color: currentcolor;
- pointer-events: none;
- border-top: 5px solid;
- border-right: 5px solid transparent;
- border-left: 5px solid transparent;
- }
- }
}
diff --git a/src/core/features/course/components/components.module.ts b/src/core/features/course/components/components.module.ts
index f663770b5..598626b03 100644
--- a/src/core/features/course/components/components.module.ts
+++ b/src/core/features/course/components/components.module.ts
@@ -30,6 +30,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module-
import { CoreCourseModuleSummaryComponent } from './module-summary/module-summary';
import { CoreCourseCourseIndexTourComponent } from './course-index-tour/course-index-tour';
import { CoreRemindersComponentsModule } from '@features/reminders/components/components.module';
+import { CoreCourseModuleCompletionDetailsComponent } from './module-completion-details/module-completion-details';
@NgModule({
declarations: [
@@ -46,6 +47,7 @@ import { CoreRemindersComponentsModule } from '@features/reminders/components/co
CoreCourseUnsupportedModuleComponent,
CoreCourseModuleNavigationComponent,
CoreCourseModuleSummaryComponent,
+ CoreCourseModuleCompletionDetailsComponent,
],
imports: [
CoreBlockComponentsModule,
@@ -66,6 +68,7 @@ import { CoreRemindersComponentsModule } from '@features/reminders/components/co
CoreCourseUnsupportedModuleComponent,
CoreCourseModuleNavigationComponent,
CoreCourseModuleSummaryComponent,
+ CoreCourseModuleCompletionDetailsComponent,
],
})
export class CoreCourseComponentsModule {}
diff --git a/src/core/features/course/components/module-completion-details/module-completion-details.html b/src/core/features/course/components/module-completion-details/module-completion-details.html
new file mode 100644
index 000000000..6ef5932eb
--- /dev/null
+++ b/src/core/features/course/components/module-completion-details/module-completion-details.html
@@ -0,0 +1,44 @@
+
+
+
+
{{ 'core.course.youmust' | translate }}
+
{{ 'core.course.studentsmust' | translate }}
+
+
+
+
+
+
+
+
+ {{ 'core.course.completion_automatic:done' | translate }}
+ {{ rule.rulevalue.description }}
+
+
+
+
+ {{ 'core.course.completion_automatic:failed' | translate }}
+ {{ rule.rulevalue.description }}
+
+
+
+
+ {{ 'core.course.completion_automatic:todo' | translate }}
+ {{ rule.rulevalue.description }}
+
+
+
+
+
+
+ {{ rule.rulevalue.description }}
+
+
+
+
+
+
+ {{ 'core.course.completion_manual:markdone' | translate }}
+
+
+
diff --git a/src/core/features/course/components/module-completion-details/module-completion-details.scss b/src/core/features/course/components/module-completion-details/module-completion-details.scss
new file mode 100644
index 000000000..bbd4a147a
--- /dev/null
+++ b/src/core/features/course/components/module-completion-details/module-completion-details.scss
@@ -0,0 +1,22 @@
+:host {
+ h2, div {
+ font-size: 16px;
+ }
+ h2 {
+ margin-top: 0px;
+ margin-bottom: 8px;
+ line-height: 27px;
+ }
+ ion-list {
+ line-height: 32px;
+ }
+
+ ion-icon {
+ width: 24px;
+ vertical-align: middle;
+ }
+
+ ion-icon.completion_dot {
+ font-size: 4px;
+ }
+}
diff --git a/src/core/features/course/components/module-completion-details/module-completion-details.ts b/src/core/features/course/components/module-completion-details/module-completion-details.ts
new file mode 100644
index 000000000..c7d6ffc83
--- /dev/null
+++ b/src/core/features/course/components/module-completion-details/module-completion-details.ts
@@ -0,0 +1,91 @@
+// (C) Copyright 2015 Moodle Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Component, Input, OnInit } from '@angular/core';
+
+import {
+ CoreCourseModuleCompletionStatus,
+ CoreCourseModuleWSRuleDetails,
+} from '@features/course/services/course';
+import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
+import { CoreUser } from '@features/user/services/user';
+import { Translate } from '@singletons';
+
+/**
+ * Component to show automatic completion details dialog.
+ */
+@Component({
+ selector: 'core-course-module-completion-details',
+ templateUrl: 'module-completion-details.html',
+ styleUrls: ['module-completion-details.scss'],
+})
+export class CoreCourseModuleCompletionDetailsComponent implements OnInit {
+
+ @Input() completion?: CoreCourseModuleCompletionData; // The completion status.
+
+ isTrackedUser = false;
+ isManual = false;
+ completionDetails: CompletionRule[] = [];
+
+ /**
+ * @inheritdoc
+ */
+ async ngOnInit(): Promise {
+ if (!this.completion) {
+ return;
+ }
+
+ this.isManual = !this.completion.isautomatic;
+ this.isTrackedUser = !!this.completion.istrackeduser;
+
+ if (!this.completion?.details) {
+ return;
+ }
+
+ const details = this.completion.details;
+
+ // Format rules.
+ this.completionDetails = await Promise.all(details.map(async (rule: CompletionRule) => {
+ rule.statusComplete = rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
+ rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS;
+ rule.statusCompleteFail = rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
+ rule.statusIncomplete = rule.rulevalue.status === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE;
+ rule.accessibleDescription = null;
+
+ if (this.completion?.overrideby) {
+ const fullName = await CoreUser.getUserFullNameWithDefault(this.completion.overrideby, this.completion.courseId);
+
+ const setByData = {
+ $a: {
+ condition: rule.rulevalue.description,
+ setby: fullName,
+ },
+ };
+ const overrideStatus = rule.statusComplete ? 'done' : 'todo';
+
+ rule.accessibleDescription = Translate.instant('core.course.completion_setby:auto:' + overrideStatus, setByData);
+ }
+
+ return rule;
+ }));
+ }
+
+}
+
+type CompletionRule = CoreCourseModuleWSRuleDetails & {
+ statusComplete?: boolean;
+ statusCompleteFail?: boolean;
+ statusIncomplete?: boolean;
+ accessibleDescription?: string | null;
+};
diff --git a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.scss b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.scss
index 0eedd0acf..08d29868d 100644
--- a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.scss
+++ b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.scss
@@ -1,14 +1,13 @@
:host {
- min-width: var(--a11y-min-target-size);
- min-height: var(--a11y-min-target-size);
+ display: contents;
--size: 30px;
img {
- padding: 5px;
+ padding: 2px;
width: var(--size);
vertical-align: middle;
max-width: none;
- margin: 7px;
+ margin: 4px;
}
ion-button {
@@ -16,5 +15,11 @@
--padding-start: 0px;
--padding-end: 0px;
--padding-bottom: 0px;
+ margin: 0;
+ --a11y-target-min-size: 32px;
+ width: var(--a11y-target-min-size);
+ height: var(--a11y-target-min-size);
+ min-width: var(--a11y-target-min-size);
+ min-height: var(--a11y-target-min-size);;
}
}
diff --git a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts
index 5ba425701..aa2c210da 100644
--- a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts
+++ b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts
@@ -147,7 +147,10 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC
return;
}
- await CoreCourseHelper.changeManualCompletion(this.completion, event);
+ event.stopPropagation();
+ event.preventDefault();
+
+ await CoreCourseHelper.changeManualCompletion(this.completion);
CoreEvents.trigger(CoreEvents.MANUAL_COMPLETION_CHANGED, { completion: this.completion });
}
diff --git a/src/core/features/course/components/module-completion/core-course-module-completion.html b/src/core/features/course/components/module-completion/core-course-module-completion.html
index dbc62caca..e6487d401 100644
--- a/src/core/features/course/components/module-completion/core-course-module-completion.html
+++ b/src/core/features/course/components/module-completion/core-course-module-completion.html
@@ -1,70 +1,45 @@
-
-
-
+
+
+
+
+ {{ 'core.course.todo' | translate }}
+
+
-
-
-
-
-
- {{ 'core.course.completion_automatic:done' | translate }}
- {{ rule.rulevalue.description }}
-
-
+
+
+ {{'core.course.done' | translate }}
+
+
+
-
-
-
- {{ 'core.course.completion_automatic:failed' | translate }}
- {{ rule.rulevalue.description }}
-
-
-
-
-
-
- {{ 'core.course.completion_automatic:todo' | translate }}
- {{ rule.rulevalue.description }}
-
-
-
-
-
-
-
-
-
- {{ 'core.course.completion_automatic:todo' | translate }}
- {{ rule.rulevalue.description }}
-
-
-
-
-
-
-
-
-
- {{ 'core.course.todo' | translate }}
-
-
-
-
- {{'core.course.done' | translate }}
-
-
-
- {{'core.course.failed' | translate }}
-
+
+
+
+ {{ 'core.course.completion_manual:done' | translate }}
+
+
+
+ {{ 'core.course.completion_manual:markdone' | translate }}
+
+
-
-
-
+
+ {{ 'core.course.completionmenuitem' | translate }}
+
+
diff --git a/src/core/features/course/components/module-completion/module-completion.scss b/src/core/features/course/components/module-completion/module-completion.scss
new file mode 100644
index 000000000..27c7e8302
--- /dev/null
+++ b/src/core/features/course/components/module-completion/module-completion.scss
@@ -0,0 +1,17 @@
+:host {
+ display: block;
+ margin: var(--margin, 4px);
+
+ ion-button {
+ margin: 0px;
+ }
+
+ ion-button.button-solid.ion-color-success::part(native){
+ background: var(--ion-color-tint);
+ color: var(--ion-color-shade);
+ }
+
+ ion-button.button-outline::part(native){
+ border-color: var(--gray-400);
+ }
+}
diff --git a/src/core/features/course/components/module-completion/module-completion.ts b/src/core/features/course/components/module-completion/module-completion.ts
index 5a6d5b986..c0c3d3dcb 100644
--- a/src/core/features/course/components/module-completion/module-completion.ts
+++ b/src/core/features/course/components/module-completion/module-completion.ts
@@ -12,17 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChange } from '@angular/core';
import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classes/module-completion';
import {
- CoreCourseCompletionMode,
CoreCourseModuleCompletionStatus,
CoreCourseModuleCompletionTracking,
- CoreCourseModuleWSRuleDetails,
} from '@features/course/services/course';
+import { CoreDomUtils } from '@services/utils/dom';
+import { CoreCourseModuleCompletionDetailsComponent } from '../module-completion-details/module-completion-details';
+import { CoreCourseHelper } from '@features/course/services/course-helper';
import { CoreUser } from '@features/user/services/user';
import { Translate } from '@singletons';
+import { CoreEventObserver, CoreEvents } from '@singletons/events';
/**
* Component to handle activity completion. It shows a checkbox with the current status, and allows manually changing
@@ -36,61 +38,132 @@ import { Translate } from '@singletons';
@Component({
selector: 'core-course-module-completion',
templateUrl: 'core-course-module-completion.html',
+ styleUrls: ['module-completion.scss'],
})
-export class CoreCourseModuleCompletionComponent extends CoreCourseModuleCompletionBaseComponent {
+export class CoreCourseModuleCompletionComponent
+ extends CoreCourseModuleCompletionBaseComponent
+ implements OnInit, OnChanges, OnDestroy {
@Input() showCompletionConditions = false; // Whether to show activity completion conditions.
@Input() showManualCompletion = false; // Whether to show manual completion.
- @Input() mode: CoreCourseCompletionMode = CoreCourseCompletionMode.FULL; // Show full completion status or a basic mode.
- details?: CompletionRule[];
+ completed = false;
accessibleDescription: string | null = null;
- completionStatus?: CoreCourseModuleCompletionStatus;
+ showCompletionInfo = false;
+ protected completionObserver?: CoreEventObserver;
+
+ /**
+ * @inheritdoc
+ */
+ ngOnInit(): void {
+ if (!this.completion) {
+ return;
+ }
+
+ const hasConditions = !this.completion.isautomatic || (this.completion.details?.length || 0) > 0;
+ this.showCompletionInfo = hasConditions && (this.showCompletionConditions || this.showManualCompletion);
+ if (!this.showCompletionInfo) {
+ return;
+ }
+
+ if (!this.completion.isautomatic && this.completion.istrackeduser) {
+ this.completionObserver = CoreEvents.on(CoreEvents.MANUAL_COMPLETION_CHANGED, (data) => {
+ if (!this.completion || this.completion.cmid != data.completion.cmid) {
+ return;
+ }
+
+ this.completion = data.completion;
+ this.calculateData();
+ this.completionChanged.emit(this.completion);
+ });
+ }
+ }
/**
* @inheritdoc
*/
protected async calculateData(): Promise {
- if (!this.completion?.details) {
+ if (!this.completion || !this.completion.istrackeduser) {
return;
}
- this.completionStatus = !this.completion?.istrackeduser ||
- this.completion.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE
+ const completionStatus = this.completion.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE
? undefined
: this.completion.state;
- // Format rules.
- this.details = await Promise.all(this.completion.details.map(async (rule: CompletionRule) => {
- rule.statuscomplete = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
- rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS;
- rule.statuscompletefail = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
- rule.statusincomplete = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE;
- rule.accessibleDescription = null;
+ this.completed = completionStatus !== CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE &&
+ completionStatus !== CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
- if (this.completion?.overrideby) {
+ if (!this.completion.isautomatic) {
+ // Set an accessible description for manual completions with overridden completion state.
+ if (this.completion.overrideby) {
const fullName = await CoreUser.getUserFullNameWithDefault(this.completion.overrideby, this.completion.courseId);
const setByData = {
$a: {
- condition: rule.rulevalue.description,
+ activityname: this.moduleName,
setby: fullName,
},
};
- const overrideStatus = rule.statuscomplete ? 'done' : 'todo';
+ const setByLangKey = this.completion.state ? 'completion_setby:manual:done' : 'completion_setby:manual:markdone';
+ this.accessibleDescription = Translate.instant('core.course.' + setByLangKey, setByData);
+ } else {
+ const langKey = this.completion.state ? 'completion_manual:aria:done' : 'completion_manual:aria:markdone';
+ this.accessibleDescription = Translate.instant('core.course.' + langKey, { $a: this.moduleName });
+ }
+ }
+ }
- rule.accessibleDescription = Translate.instant('core.course.completion_setby:auto:' + overrideStatus, setByData);
+ /**
+ * Completion clicked.
+ *
+ * @param event The click event.
+ */
+ async completionClicked(event: Event): Promise {
+ if (!this.completion || !this.showCompletionInfo) {
+ return;
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+
+ if (this.completion.isautomatic || !this.completion.istrackeduser) {
+ // Fake clicked element to correct position of the popover.
+ let target: HTMLElement | null = event.target as HTMLElement;
+ if (target && target.tagName !== 'ION-BUTTON') {
+ target = target.parentElement;
}
- return rule;
- }));
+ CoreDomUtils.openPopover({
+ component: CoreCourseModuleCompletionDetailsComponent,
+ componentProps: {
+ completion: this.completion,
+ },
+ showBackdrop: true,
+ event: { target } as Event,
+ });
+ } else {
+ await CoreCourseHelper.changeManualCompletion(this.completion);
+
+ CoreEvents.trigger(CoreEvents.MANUAL_COMPLETION_CHANGED, { completion: this.completion });
+
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ ngOnChanges(changes: { [name: string]: SimpleChange }): void {
+ if (changes.completion && this.completion && this.completion.istrackeduser) {
+ this.calculateData();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ ngOnDestroy(): void {
+ this.completionObserver?.off();
}
}
-
-type CompletionRule = CoreCourseModuleWSRuleDetails & {
- statuscomplete?: boolean;
- statuscompletefail?: boolean;
- statusincomplete?: boolean;
- accessibleDescription?: string | null;
-};
diff --git a/src/core/features/course/components/module-info/course-module-info.scss b/src/core/features/course/components/module-info/course-module-info.scss
index e9f9146ff..a95a6898e 100644
--- a/src/core/features/course/components/module-info/course-module-info.scss
+++ b/src/core/features/course/components/module-info/course-module-info.scss
@@ -60,30 +60,8 @@
}
}
- core-course-module-completion ::ng-deep ion-button {
- min-height: 28px;
- margin: 0;
- font-size: 12px;
- text-transform: none;
- font-weight: normal;
-
- ion-icon {
- font-size: 16px;
- min-width: 16px;
- @include margin(0, 8px, 0, 0);
-
- &[slot=start] {
- @include margin(null, 8px, null, 0);
- }
-
- &[slot=end] {
- @include margin(null, 0, null, 8px);
- }
- }
-
- ion-label {
- white-space: normal !important;
- }
+ core-course-module-completion {
+ --margin: 0px;
}
}
diff --git a/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html b/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html
index abe56f66d..837781567 100644
--- a/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html
+++ b/src/core/features/course/components/module-manual-completion/core-course-module-manual-completion.html
@@ -1,14 +1,14 @@
+ class="ion-text-wrap chip">
{{ 'core.course.completion_manual:done' | translate }}
+ class="ion-text-wrap chip">
{{ 'core.course.completion_manual:markdone' | translate }}
@@ -16,7 +16,7 @@
-
+
{{ 'core.course.completion_manual:markdone' | translate }}
diff --git a/src/core/features/course/components/module-manual-completion/module-manual-completion.ts b/src/core/features/course/components/module-manual-completion/module-manual-completion.ts
index 5d3c51f4f..c59624f9a 100644
--- a/src/core/features/course/components/module-manual-completion/module-manual-completion.ts
+++ b/src/core/features/course/components/module-manual-completion/module-manual-completion.ts
@@ -13,7 +13,6 @@
// limitations under the License.
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChange } from '@angular/core';
-import { CoreCourseCompletionMode } from '@features/course/services/course';
import { CoreCourseHelper, CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
import { CoreUser } from '@features/user/services/user';
import { Translate } from '@singletons';
@@ -21,6 +20,8 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events';
/**
* Component to display a button for manual completion.
+ *
+ * @deprecated since 4.3. Not used anymore.
*/
@Component({
selector: 'core-course-module-manual-completion',
@@ -30,7 +31,6 @@ export class CoreCourseModuleManualCompletionComponent implements OnInit, OnChan
@Input() completion?: CoreCourseModuleCompletionData; // The completion status.
@Input() moduleName?: string; // The name of the module this completion affects.
- @Input() mode: CoreCourseCompletionMode = CoreCourseCompletionMode.FULL; // Show full completion status or a basic mode.
@Output() completionChanged = new EventEmitter(); // Notify when completion changes.
accessibleDescription: string | null = null;
@@ -100,7 +100,7 @@ export class CoreCourseModuleManualCompletionComponent implements OnInit, OnChan
event.stopPropagation();
event.preventDefault();
- await CoreCourseHelper.changeManualCompletion(this.completion, event);
+ await CoreCourseHelper.changeManualCompletion(this.completion);
CoreEvents.trigger(CoreEvents.MANUAL_COMPLETION_CHANGED, { completion: this.completion });
}
diff --git a/src/core/features/course/components/module/core-course-module.html b/src/core/features/course/components/module/core-course-module.html
index e76e3df4a..0f4185250 100644
--- a/src/core/features/course/components/module/core-course-module.html
+++ b/src/core/features/course/components/module/core-course-module.html
@@ -23,13 +23,6 @@
-
-
-
-
@@ -72,10 +65,10 @@
contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
-
-
+
+
(''); // Module prefetch status icon.
prefetchStatusText$ = new BehaviorSubject(''); // Module prefetch status text.
- autoCompletionTodo = false;
moduleHasView = true;
protected prefetchHandler?: CoreCourseModulePrefetchHandler;
@@ -78,7 +78,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
this.showLegacyCompletion = this.showLegacyCompletion ??
CoreConstants.CONFIG.uselegacycompletion ??
!site.isVersionGreaterEqualThan('3.11');
- this.checkShowManualCompletion();
+ this.checkShowCompletion();
if (!this.module.handlerData) {
return;
@@ -87,18 +87,10 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
this.module.handlerData.a11yTitle = this.module.handlerData.a11yTitle ?? this.module.handlerData.title;
this.moduleHasView = CoreCourse.moduleHasView(this.module);
- const completionStatus = this.showCompletionConditions && this.module.completiondata?.isautomatic &&
- this.module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC
- ? this.module.completiondata.state
- : undefined;
-
- this.autoCompletionTodo = completionStatus == CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE ||
- completionStatus == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
-
this.hasInfo = !!(
this.module.description ||
(this.showActivityDates && this.module.dates && this.module.dates.length) ||
- (this.autoCompletionTodo && !this.showLegacyCompletion) ||
+ (this.hasCompletion && !this.showLegacyCompletion) ||
(this.module.availabilityinfo)
);
@@ -160,9 +152,14 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
/**
* Check whether manual completion should be shown.
*/
- protected async checkShowManualCompletion(): Promise {
+ protected async checkShowCompletion(): Promise {
this.showManualCompletion = this.showCompletionConditions ||
await CoreCourseModuleDelegate.manualCompletionAlwaysShown(this.module);
+
+ this.hasCompletion = !!this.module.completiondata && this.module.uservisible &&
+ (!this.module.completiondata.isautomatic || (this.module.completiondata.details?.length || 0) > 0) &&
+ (this.showCompletionConditions || this.showManualCompletion);
+
}
/**
diff --git a/src/core/features/course/lang.json b/src/core/features/course/lang.json
index d1501f66e..22ab99028 100644
--- a/src/core/features/course/lang.json
+++ b/src/core/features/course/lang.json
@@ -12,6 +12,7 @@
"completion_manual:aria:markdone": "Mark {{$a}} as done",
"completion_manual:done": "Done",
"completion_manual:markdone": "Mark as done",
+ "completionmenuitem": "Completion",
"completion_setby:auto:done": "Done: {{$a.condition}} (set by {{$a.setby}})",
"completion_setby:auto:todo": "To do: {{$a.condition}} (set by {{$a.setby}})",
"completion_setby:manual:done": "{{$a.activityname}} is marked by {{$a.setby}} as done. Press to undo.",
@@ -54,6 +55,7 @@
"relativedatessubmissionduedatebefore": "{{$a.datediffstr}} before course start",
"section": "Section",
"startdate": "Course start date",
+ "studentsmust": "Students must",
"thisweek": "This week",
"todo": "To do",
"tour_navigation_course_index_student_content": "Browse through activities and track your progress.",
@@ -61,5 +63,6 @@
"useactivityonbrowser": "You can still use it using your device's web browser.",
"viewcourse": "View course",
"warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.",
+ "youmust": "You must",
"warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}"
}
diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts
index 283d3e6d2..7cefea8cc 100644
--- a/src/core/features/course/services/course-helper.ts
+++ b/src/core/features/course/services/course-helper.ts
@@ -2012,12 +2012,10 @@ export class CoreCourseHelperProvider {
* Completion clicked.
*
* @param completion The completion.
- * @param event The click event.
* @returns Promise resolved with the result.
*/
async changeManualCompletion(
completion: CoreCourseModuleCompletionData,
- event?: Event,
): Promise {
if (!completion) {
return;
@@ -2028,9 +2026,6 @@ export class CoreCourseHelperProvider {
return;
}
- event?.preventDefault();
- event?.stopPropagation();
-
const modal = await CoreDomUtils.showModalLoading();
completion.state = completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE
? CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE
diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts
index 13217003d..c536c25d7 100644
--- a/src/core/features/course/services/course.ts
+++ b/src/core/features/course/services/course.ts
@@ -81,6 +81,9 @@ export enum CoreCourseModuleCompletionStatus {
COMPLETION_COMPLETE_FAIL = 3,
}
+/**
+ * @deprecated since 4.3 Not used anymore.
+ */
export enum CoreCourseCompletionMode {
FULL = 'full',
BASIC = 'basic',
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index 8d55ea74f..792077422 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -389,6 +389,27 @@ ion-button.button.button-outline {
--border-radius: var(--core-input-radius);
}
+ion-button .select-icon {
+ margin: var(--icon-margin);
+ width: 19px;
+ height: 19px;
+ position: relative;
+
+ .select-icon-inner {
+ left: 5px;
+ top: 50%;
+ margin-top: -2px;
+ position: absolute;
+ width: 0px;
+ height: 0px;
+ color: currentcolor;
+ pointer-events: none;
+ border-top: 5px solid;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+ }
+}
+
[role="button"],
.clickable {
cursor: pointer;
@@ -463,10 +484,9 @@ div.core-iframe-network-error {
left: -15%;
}
-ion-alert.core-nohead {
- .alert-head {
- padding-bottom: 0;
- }
+ion-alert.core-nohead .alert-head,
+ion-alert .alert-head:empty {
+ padding-bottom: 0;
}
@keyframes scaleFrom0 {
@@ -1130,7 +1150,7 @@ ion-badge {
}
ion-chip,
-ion-button.chip {
+ion-button.button.chip {
line-height: 1.1;
font-size: 12px;
min-height: 24px;
@@ -1149,7 +1169,11 @@ ion-button.chip {
}
}
-ion-button.chip {
+ion-button.button.chip {
+ --border-radius: var(--radius-md);
+ min-height: 32px;
+ font-size: 14px;
+
ion-icon[slot=start] {
@include margin(0, 8px, 0, 0);
}
@@ -1840,13 +1864,34 @@ video::-webkit-media-text-track-display {
white-space: normal !important;
}
-ion-modal.core-modal-no-background {
- --background: transparent;
- --box-shadow: none !important;
- pointer-events: none;
+ion-modal {
+ .modal-wrapper {
+ --border-radius: var(--modal-radius);
+ }
- ion-backdrop {
- display: none;
+ &.core-modal-lateral,
+ &.core-modal-fullscreen {
+ --modal-radius: 0px;
+ }
+
+ &.core-modal-no-background {
+ --background: transparent;
+ --box-shadow: none !important;
+ pointer-events: none;
+
+ ion-backdrop {
+ display: none;
+ }
+ }
+}
+
+ion-popover {
+ .popover-wrapper .popover-content {
+ border-radius: var(--modal-radius);
+ }
+ &.md {
+ margin-top: 2px;
+ margin-bottom: 2px;
}
}
diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss
index 067c14541..ee7d4df9d 100644
--- a/src/theme/theme.light.scss
+++ b/src/theme/theme.light.scss
@@ -72,6 +72,7 @@ html {
--list-item-max-width: 768px;
+ --modal-radius: var(--radius-md);
--modal-lateral-max-width: 320px;
--modal-lateral-margin: 56px;