Skip to content

Commit f0d1657

Browse files
committed
add mobile version of lesson pages
1 parent a788244 commit f0d1657

20 files changed

Lines changed: 315 additions & 486 deletions

projects/social_platform/src/app/office/courses/courses.service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @format */
22

3-
import { Injectable } from "@angular/core";
3+
import { Injectable, signal } from "@angular/core";
44
import { ApiService } from "@corelib";
55
import {
66
CourseCard,
@@ -23,6 +23,9 @@ import { Observable } from "rxjs";
2323
export class CoursesService {
2424
private readonly COURSE_URL = "/courses";
2525

26+
readonly currentLesson = signal<CourseLesson | null>(null);
27+
readonly courseStructure = signal<CourseStructure | null>(null);
28+
2629
constructor(private readonly apiService: ApiService) {}
2730

2831
/**

projects/social_platform/src/app/office/courses/detail/course-detail.component.html

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,57 @@
22

33
<div class="detail">
44
<div class="detail__body">
5-
@if(course()) {
5+
@if (course()) {
66
<div class="info detail__section detail__info">
7+
@if (showCover) {
78
<div class="info__cover">
89
<img class="info__cover" [src]="course()!.headerCoverUrl" alt="cover" />
10+
911
<div class="info__avatar">
1012
<app-avatar
1113
[url]="course()!.avatarUrl"
1214
[size]="106"
1315
(click)="redirectDetailInfo(course()!.id)"
1416
></app-avatar>
15-
@if (!isTaskDetail()) { @if (appWidth > 920) {
16-
<h1 class="info__title text-body-12">{{ course()!.title }}</h1>
17-
} }
17+
18+
@if (!isTaskDetail() && !isMobile) {
19+
<h1 class="info__title text-body-12">
20+
{{ course()!.title }}
21+
</h1>
22+
}
1823
</div>
1924
</div>
25+
}
2026

2127
<div class="info__body">
22-
<div class="info__actions">
23-
@if (appWidth < 920) {
28+
<div class="info__actions" [class.info__actions--task]="showBackOnly">
29+
@if (showBackOnly) {
30+
<app-button size="small" appearance="outline" (click)="redirectDetailInfo(course()!.id)">
31+
назад
32+
</app-button>
33+
34+
<div class="task__meta">
35+
@if (currentLesson()) {
36+
<p class="text-body-14">модуль {{ currentLesson()!.moduleOrder }}</p>
37+
<p class="task__lesson text-body-12">урок {{ lessonOrder }}</p>
38+
}
39+
</div>
40+
2441
<app-button
25-
[disabled]="true"
2642
size="small"
27-
customTypographyClass="text-body-12"
2843
appearance="outline"
44+
(click)="course()!.description ? (isAboutModalOpen = true) : null"
2945
>
30-
аналитика
46+
о курсе
3147
</app-button>
32-
48+
} @else { @if (showAnalyticsButton) {
49+
<app-button [disabled]="true" size="small" appearance="outline">аналитика</app-button>
50+
} @if (showAboutButton) {
3351
<app-button
34-
[disabled]="!course()!.description"
3552
size="small"
36-
class="info__actions--end"
37-
customTypographyClass="text-body-12"
3853
appearance="outline"
54+
class="info__actions--end"
55+
[disabled]="!course()!.description"
3956
(click)="course()!.description ? (isAboutModalOpen = true) : null"
4057
>
4158
о курсе
@@ -44,22 +61,24 @@ <h1 class="info__title text-body-12">{{ course()!.title }}</h1>
4461

4562
<app-button
4663
class="info__actions--full"
47-
(click)="redirectDetailInfo(isTaskDetail() ? course()!.id : undefined)"
48-
[size]="appWidth > 920 ? 'big' : 'big'"
49-
customTypographyClass="text-body-12"
64+
size="big"
5065
appearance="outline"
51-
>{{ isTaskDetail() ? "назад к модулю" : "назад" }}</app-button
66+
(click)="redirectDetailInfo(isTaskDetail() ? course()!.id : undefined)"
5267
>
68+
назад
69+
</app-button>
5370

71+
@if (showProgramButton) {
5472
<app-button
5573
class="info__actions--full"
5674
[disabled]="isDisabled()"
57-
[size]="appWidth > 920 ? 'big' : 'big'"
58-
customTypographyClass="text-body-12"
75+
size="big"
5976
appearance="outline"
6077
(click)="redirectToProgram()"
61-
>вернуться в программу</app-button
6278
>
79+
вернуться в программу
80+
</app-button>
81+
} }
6382
</div>
6483
</div>
6584
</div>

projects/social_platform/src/app/office/courses/detail/course-detail.component.scss

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ $detail-bar-mb: 12px;
77
.detail {
88
display: flex;
99
flex-direction: column;
10-
height: 100%;
1110
max-height: 100%;
1211
padding-top: 20px;
12+
padding-bottom: 20px;
1313
margin: 0 20px;
1414

1515
@include responsive.apply-desktop {
1616
margin: 0;
17+
height: 100%;
18+
padding-bottom: 0px;
1719
}
1820

1921
&__body {
@@ -36,6 +38,7 @@ $detail-bar-mb: 12px;
3638
position: relative;
3739
height: 136px;
3840
border-radius: 15px;
41+
margin-bottom: 24px;
3942

4043
@include responsive.apply-desktop {
4144
border-radius: 15px 15px 0 0;
@@ -62,7 +65,7 @@ $detail-bar-mb: 12px;
6265
position: absolute;
6366
bottom: -10px;
6467
left: 50%;
65-
z-index: 100;
68+
z-index: 10;
6669
display: block;
6770
display: flex;
6871
flex-direction: column;
@@ -114,11 +117,17 @@ $detail-bar-mb: 12px;
114117
grid-template-columns: 1fr 1fr;
115118
gap: 12px;
116119
align-items: center;
117-
padding: 24px 10px 0;
118120

119121
@include responsive.apply-desktop {
120122
gap: 180px;
121-
padding: 24px 0 30px;
123+
padding: 0px 0px 30px;
124+
}
125+
126+
&--task {
127+
display: flex;
128+
flex-direction: row-reverse;
129+
justify-content: space-between;
130+
align-items: center;
122131
}
123132

124133
&--end {
@@ -138,3 +147,15 @@ $detail-bar-mb: 12px;
138147
}
139148
}
140149
}
150+
151+
.task {
152+
&__lesson {
153+
color: var(--dark-grey);
154+
}
155+
156+
&__meta {
157+
display: flex;
158+
flex-direction: column;
159+
align-items: center;
160+
}
161+
}

projects/social_platform/src/app/office/courses/detail/course-detail.component.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { AvatarComponent } from "@ui/components/avatar/avatar.component";
88
import { ButtonComponent } from "@ui/components";
99
import { IconComponent } from "@uilib";
1010
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
11-
import { CourseDetail, CourseStructure } from "@office/models/courses.model";
11+
import { CourseDetail, CourseLesson, CourseStructure } from "@office/models/courses.model";
1212
import { ModalComponent } from "@ui/components/modal/modal.component";
1313
import { CourseAboutComponent } from "@office/courses/shared/course-about/course-about.component";
14+
import { CoursesService } from "@office/courses/courses.service";
1415

1516
/**
1617
* Компонент детального просмотра траектории
@@ -36,6 +37,7 @@ export class CourseDetailComponent implements OnInit {
3637
private readonly route = inject(ActivatedRoute);
3738
private readonly router = inject(Router);
3839
private readonly destroyRef = inject(DestroyRef);
40+
private readonly coursesService = inject(CoursesService);
3941

4042
appWidth = window.innerWidth;
4143

@@ -50,6 +52,44 @@ export class CourseDetailComponent implements OnInit {
5052

5153
protected readonly courseModules = signal<CourseStructure["modules"]>([]);
5254
protected readonly course = signal<CourseDetail | undefined>(undefined);
55+
protected readonly courseStructure = signal<CourseStructure | undefined>(undefined);
56+
protected readonly currentLesson = this.coursesService.currentLesson;
57+
58+
get lessonOrder(): number | null {
59+
const lesson = this.currentLesson();
60+
const structure = this.courseStructure();
61+
if (!lesson || !structure) return null;
62+
63+
for (const mod of structure.modules) {
64+
const found = mod.lessons.find(l => l.id === lesson.id);
65+
if (found) return found.order;
66+
}
67+
return null;
68+
}
69+
70+
get isMobile(): boolean {
71+
return this.appWidth < 920;
72+
}
73+
74+
get showCover(): boolean {
75+
return !this.isTaskDetail() || !this.isMobile;
76+
}
77+
78+
get showAboutButton(): boolean {
79+
return this.isMobile && !this.isTaskDetail();
80+
}
81+
82+
get showBackOnly(): boolean {
83+
return this.isTaskDetail() && this.isMobile;
84+
}
85+
86+
get showAnalyticsButton(): boolean {
87+
return this.isMobile && !this.isTaskDetail();
88+
}
89+
90+
get showProgramButton(): boolean {
91+
return !this.showBackOnly;
92+
}
5393

5494
/**
5595
* Инициализация компонента
@@ -63,8 +103,10 @@ export class CourseDetailComponent implements OnInit {
63103
takeUntilDestroyed(this.destroyRef)
64104
)
65105
.subscribe({
66-
next: ([course, _]: [CourseDetail, CourseStructure]) => {
106+
next: ([course, structure]: [CourseDetail, CourseStructure]) => {
67107
this.course.set(course);
108+
this.courseStructure.set(structure);
109+
this.coursesService.courseStructure.set(structure);
68110

69111
if (!course.partnerProgramId) {
70112
this.isDisabled.set(true);

projects/social_platform/src/app/office/courses/lesson/complete/complete.component.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
@use "styles/typography";
33

44
.complete {
5-
min-height: 358px;
5+
min-height: 543px;
66
padding: 24px;
77
background-color: var(--light-white);
88
border: 0.5px solid var(--medium-grey-for-outline);
99
border-radius: var(--rounded-lg);
10+
11+
@include responsive.apply-desktop {
12+
min-height: 358px;
13+
}
1014

1115
&__block {
1216
display: flex;

projects/social_platform/src/app/office/courses/lesson/lesson.component.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
@if (lessonInfo()) {
44
<div class="task">
5-
<div class="task__wrapper">
5+
<div class="task__wrapper" [class.task__wrapper--mobile]="appWidth < 920">
6+
@if (appWidth >= 920) {
67
<div class="task__tasks">
78
<p class="task__name text-bold-body-14">модуль {{ lessonInfo()?.moduleOrder }}</p>
9+
<p class="task__lesson text-body-12">урок {{ lessonOrder() }}</p>
810
</div>
11+
}
912

1013
<div class="task__progress">
1114
@for (task of tasks(); track task.id) {
@@ -15,7 +18,7 @@
1518
[ngClass]="{
1619
'progress__task--done': isDone(task),
1720
'progress__task--current': isCurrent(task.id),
18-
'progress__task--clickable': isDone(task)
21+
'progress__task--clickable': isClickable(task)
1922
}"
2023
>
2124
<p class="text-body-12">{{ task.order }}</p>

projects/social_platform/src/app/office/courses/lesson/lesson.component.scss

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,21 @@
1010

1111
.task {
1212
display: grid;
13-
grid-template-columns: 68px 1fr;
13+
grid-template-columns: 1fr;
1414
gap: 20px;
1515

16+
@include responsive.apply-desktop {
17+
grid-template-columns: 68px 1fr;
18+
}
19+
1620
&__wrapper {
1721
display: flex;
1822
flex-direction: column;
23+
order: -1;
24+
25+
@include responsive.apply-desktop {
26+
order: 0;
27+
}
1928
}
2029

2130
&__tasks {
@@ -26,15 +35,22 @@
2635
&__skill-name {
2736
color: var(--black);
2837
}
29-
30-
&__name {
31-
color: var(--dark-grey);
38+
39+
&__lesson {
40+
color: var(--grey-for-text);
3241
}
3342

3443
&__progress {
3544
display: flex;
36-
flex-direction: column;
45+
flex-direction: row;
3746
gap: 12px;
47+
overflow-x: auto;
48+
padding-bottom: 0;
49+
50+
@include responsive.apply-desktop {
51+
flex-direction: column;
52+
overflow-x: visible;
53+
}
3854
}
3955

4056
&__main {
@@ -43,6 +59,10 @@
4359
justify-content: center;
4460
}
4561

62+
&__content {
63+
width: 100%;
64+
}
65+
4666
&__complete {
4767
display: block;
4868
}
@@ -62,7 +82,9 @@
6282
.progress {
6383
&__task {
6484
height: 23px;
85+
min-width: 42px;
6586
padding: 4px 0;
87+
flex-shrink: 0;
6688
text-align: center;
6789
background-color: var(--light-white);
6890
border-radius: var(--rounded-xl);
@@ -82,3 +104,15 @@
82104
}
83105
}
84106
}
107+
108+
.action {
109+
&__button {
110+
position: fixed;
111+
bottom: 1%;
112+
left: 7%;
113+
114+
@include responsive.apply-desktop {
115+
position: static;
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)