Skip to content

Commit 1e5d0f2

Browse files
committed
fix layout of tasks & file attachment, file service handle 401 eror, percent data update, add directive for scaling
1 parent 27514ba commit 1e5d0f2

23 files changed

Lines changed: 334 additions & 60 deletions

projects/social_platform/src/app/core/services/file.service.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,44 @@ export class FileService {
3333
formData.append("file", file);
3434

3535
return new Observable<{ url: string }>(observer => {
36-
fetch(`${environment.apiUrl}${this.FILES_URL}/`, {
37-
method: "POST",
38-
headers: {
39-
Authorization: `Bearer ${this.tokenService.getTokens()?.access}`,
40-
},
41-
body: formData,
42-
})
43-
.then(res => res.json())
36+
const doFetch = (token: string) =>
37+
fetch(`${environment.apiUrl}${this.FILES_URL}/`, {
38+
method: "POST",
39+
headers: { Authorization: `Bearer ${token}` },
40+
body: formData,
41+
});
42+
43+
const token = this.tokenService.getTokens()?.access;
44+
if (!token) {
45+
observer.error(new Error("No access token"));
46+
return;
47+
}
48+
49+
doFetch(token)
50+
.then(res => {
51+
if (res.status === 401) {
52+
this.tokenService.refreshTokens().subscribe({
53+
next: newTokens => {
54+
this.tokenService.memTokens(newTokens);
55+
doFetch(newTokens.access)
56+
.then(r => r.json())
57+
.then(data => {
58+
observer.next(data);
59+
observer.complete();
60+
})
61+
.catch(err => observer.error(err));
62+
},
63+
error: err => observer.error(err),
64+
});
65+
return;
66+
}
67+
return res.json();
68+
})
4469
.then(res => {
45-
observer.next(res);
46-
observer.complete();
70+
if (res) {
71+
observer.next(res);
72+
observer.complete();
73+
}
4774
})
4875
.catch(err => observer.error(err));
4976
});

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

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

3+
import type { Routes } from "@angular/router";
34
import { TrajectoryInfoComponent } from "./info/info.component";
45
import { CourseDetailComponent } from "./course-detail.component";
56
import { CoursesDetailResolver } from "./course-detail.resolver";
67

7-
export const COURSE_DETAIL_ROUTES = [
8+
export const COURSE_DETAIL_ROUTES: Routes = [
89
{
910
path: "",
1011
component: CourseDetailComponent,
12+
runGuardsAndResolvers: "always",
1113
resolve: {
1214
data: CoursesDetailResolver,
1315
},

projects/social_platform/src/app/office/courses/detail/info/info.component.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,21 @@ export class TrajectoryInfoComponent implements OnInit, AfterViewInit {
7777
this.courseStructure.set(courseStructure);
7878
this.courseDetail.set(courseDetail);
7979

80-
if (courseStructure.percent === 100) {
80+
const completedModuleIds = courseStructure.modules
81+
.filter(m => m.progressStatus === "completed")
82+
.map(m => m.id);
83+
84+
const unseenModule = completedModuleIds.find(
85+
id =>
86+
!localStorage.getItem(`course_${courseStructure.courseId}_module_${id}_complete_seen`)
87+
);
88+
89+
if (unseenModule) {
8190
this.isCompleteModule.set(true);
91+
localStorage.setItem(
92+
`course_${courseStructure.courseId}_module_${unseenModule}_complete_seen`,
93+
"true"
94+
);
8295
}
8396
});
8497
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ export class TaskCompleteComponent implements OnInit {
3434

3535
routeToCourses(): void {
3636
const id = this.courseId();
37-
this.router.navigateByUrl(id ? `/office/courses/${id}` : "/office/courses/all");
37+
this.router.navigate(id ? ["/office/courses", id] : ["/office/courses/all"]);
3838
}
3939
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export class LessonComponent implements OnInit {
161161

162162
const body = this.answerBody();
163163
const isTextFile = task.answerType === "text_and_files";
164-
const answerText = task.answerType === "text" ? body : isTextFile ? body?.text : undefined;
164+
const answerText = task.answerType === "text" || isTextFile ? body?.text : undefined;
165165
const optionIds =
166166
task.answerType === "single_choice" || task.answerType === "multiple_choice"
167167
? body
@@ -183,7 +183,7 @@ export class LessonComponent implements OnInit {
183183
} else {
184184
this.hasError.set(true);
185185
this.success.set(false);
186-
this.snackbarService.info("находится на проверке, ожидайте!");
186+
this.snackbarService.error("неверный ответ, попробуйте еще раз!");
187187
setTimeout(() => this.hasError.set(false), 1000);
188188
return;
189189
}
@@ -205,7 +205,7 @@ export class LessonComponent implements OnInit {
205205
error: () => {
206206
this.loader.set(false);
207207
this.hasError.set(true);
208-
this.snackbarService.error("упс, вышла неточность! попробуйте еще раз");
208+
this.snackbarService.error("неверный ответ, попробуйте еще раз!");
209209
},
210210
});
211211
}

projects/social_platform/src/app/office/courses/lesson/shared/exclude-task/exclude-task.component.html

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!-- @format -->
22

33
<div class="exclude" [class.exclude--hasContent]="hasVideoUrl() || !!data.imageUrl">
4-
<div class="exclude__body">
4+
<div class="exclude__body" [class.exclude__body--hasContent]="hasVideoUrl() || !!data.imageUrl">
55
@if (!hasVideoUrl() && !data.imageUrl) {
66
<h2 class="exclude__title text-body-12-bold">{{ data.title | truncate: 110 }}</h2>
77
}
@@ -11,14 +11,17 @@ <h2 class="exclude__title text-body-12-bold">{{ data.title | truncate: 110 }}</h
1111
>
1212
@if (getSafeVideoUrl(); as videoUrl) {
1313
<iframe [src]="videoUrl" frameborder="0" allowfullscreen allowtransparency="true"></iframe>
14-
} @else {
15-
<img alt="exclude-image" [src]="data.imageUrl" class="exclude__image" />
16-
<div [innerHTML]="data.bodyText | truncate: 700"></div>
14+
} @else if (data.imageUrl) {
15+
<img appImagePreview alt="exclude-image" [src]="data.imageUrl" class="exclude__image" />
1716
}
17+
<div [innerHTML]="data.bodyText | truncate: 700"></div>
1818
</div>
1919
</div>
2020

21-
<div class="exclude__content">
21+
<div
22+
class="exclude__content"
23+
[class.exclude__content--hasContent]="hasVideoUrl() || !!data.imageUrl"
24+
>
2225
<p class="exclude__answer text-body-12">ответ</p>
2326
<ul class="exclude__list">
2427
<p class="exclude__text text-body-12">{{ data.title | truncate: 110 }}</p>

projects/social_platform/src/app/office/courses/lesson/shared/exclude-task/exclude-task.component.scss

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@
7777

7878
&__image {
7979
width: 100%;
80-
max-width: 150px;
80+
max-width: 300px;
8181
height: 100%;
82-
max-height: 200px;
83-
margin: calc(75px - 24px) calc(92px - 24px) 26px calc(92px - 24px);
84-
object-fit: contain;
82+
max-height: 150px;
83+
margin: calc(75px - 24px) 0 26px 0;
84+
object-fit: cover;
8585
border-radius: var(--rounded-lg);
8686
}
8787

@@ -95,8 +95,20 @@
9595
border-radius: var(--rounded-lg);
9696
}
9797

98+
&__body {
99+
&--hasContent {
100+
max-width: 333px;
101+
}
102+
}
103+
98104
&__content {
99-
min-width: 333px;
105+
&--hasContent {
106+
min-width: 333px;
107+
}
108+
109+
&:not(&--hasContent) {
110+
max-width: 333px;
111+
}
100112
}
101113

102114
:host ::ng-deep &__hint p {

projects/social_platform/src/app/office/courses/lesson/shared/exclude-task/exclude-task.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { CheckboxComponent } from "@ui/components";
77
import { TruncatePipe } from "projects/core/src/lib/pipes/truncate.pipe";
88
import { Task } from "@office/models/courses.model";
99
import { resolveVideoUrlForIframe } from "@utils/video-url-embed";
10+
import { ImagePreviewDirective } from "../image-preview/image-preview.directive";
1011

1112
/**
1213
* Компо��ент задачи на исключение лишнего
@@ -31,7 +32,7 @@ import { resolveVideoUrlForIframe } from "@utils/video-url-embed";
3132
@Component({
3233
selector: "app-exclude-task",
3334
standalone: true,
34-
imports: [CommonModule, TruncatePipe, CheckboxComponent],
35+
imports: [CommonModule, TruncatePipe, CheckboxComponent, ImagePreviewDirective],
3536
templateUrl: "./exclude-task.component.html",
3637
styleUrl: "./exclude-task.component.scss",
3738
})

projects/social_platform/src/app/office/courses/lesson/shared/file-task/file-task.component.html

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div class="file-task" [class.file-task--hasVideo]="hasVideoUrl()">
44
<div class="file-task__header">
55
<p class="text-body-12 file-task__task">задание {{ data.order }}</p>
6-
@if (!hasVideoUrl() && !data.imageUrl) {
6+
@if ((!data.videoUrl && !data.imageUrl) && data.attachmentUrl) {
77
<p class="file-task__text text-body-12-bold">{{ data.title | truncate: 80 }}</p>
88
}
99
<div
@@ -20,12 +20,35 @@
2020
allowtransparency="true"
2121
></iframe>
2222
} @if (data.imageUrl) {
23-
<img class="file-task__image" [src]="data.imageUrl" />
23+
<img appImagePreview class="file-task__image" [src]="data.imageUrl" />
2424
}
25-
<div class="file-task__image-text">
25+
<div class="file-task__file-text" [class.file-task__image-text]="data.imageUrl">
26+
@if (data.imageUrl) {
2627
<p class="file-task__text text-body-12-bold">{{ data.title | truncate: 80 }}</p>
27-
<div [innerHTML]="data.bodyText | truncateHtml: 700"></div>
28+
}
29+
<div
30+
#descEl
31+
class="file-task__desc text-body-12"
32+
[class.file-task__desc--expanded]="readFullDescription"
33+
[innerHTML]="data.bodyText | truncate: 700"
34+
></div>
35+
36+
@if (descriptionExpandable) {
37+
<div class="read-more text-body-10" (click)="onToggleDescription()">
38+
{{ readFullDescription ? "скрыть" : "подробнее" }}
39+
</div>
40+
}
2841
</div>
42+
43+
@if (data.attachmentUrl) {
44+
<app-file-item
45+
mode="preview"
46+
[type]="'file'"
47+
name="файл с заданием"
48+
[size]="0"
49+
[link]="data.attachmentUrl"
50+
></app-file-item>
51+
}
2952
</div>
3053

3154
@if (hint.length) {
@@ -39,7 +62,7 @@
3962
[class.file-task__content--error]="error"
4063
>
4164
<p class="file-task__label text-body-12">ответ</p>
42-
<p class="file-task__title text-body-12-bold">{{ data.title | truncate: 80 }}</p>
65+
<!-- <p class="file-task__title text-body-12-bold">{{ data.title | truncate: 80 }}</p> -->
4366

4467
<div class="file-task__files">
4568
<app-upload-file [resetAfterUpload]="true" (uploaded)="onFileUploaded($event)">

projects/social_platform/src/app/office/courses/lesson/shared/file-task/file-task.component.scss

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616
&__content {
1717
flex-grow: 1;
1818
min-height: 358px;
19+
max-height: 393px;
1920
padding: 24px;
2021
background: var(--light-white);
2122
border: 0.5px solid var(--medium-grey-for-outline);
2223
border-radius: var(--rounded-lg);
2324
}
2425

2526
&__content {
26-
max-width: 333px;
27+
min-width: 333px;
2728
padding: 12px 24px;
2829

2930
::ng-deep {
@@ -119,7 +120,7 @@
119120

120121
&__image {
121122
width: 100%;
122-
max-width: 110px;
123+
max-width: 300px;
123124
height: 100%;
124125
max-height: 150px;
125126
border-radius: var(--rounded-lg);
@@ -135,6 +136,43 @@
135136
}
136137
}
137138

139+
&__desc {
140+
overflow: hidden;
141+
max-height: 12em;
142+
text-align: justify;
143+
transition: max-height 0.5s ease-in-out;
144+
145+
&:not(&--expanded) {
146+
display: -webkit-box;
147+
-webkit-box-orient: vertical;
148+
-webkit-line-clamp: 8;
149+
}
150+
151+
&--expanded {
152+
overflow-y: auto;
153+
max-height: 210px;
154+
}
155+
}
156+
157+
&__file {
158+
&-text {
159+
display: flex;
160+
flex-direction: column;
161+
margin-bottom: 10px;
162+
}
163+
}
164+
165+
.read-more {
166+
margin-top: 12px;
167+
color: var(--accent);
168+
cursor: pointer;
169+
transition: color 0.2s;
170+
171+
&:hover {
172+
color: var(--accent-dark);
173+
}
174+
}
175+
138176
:host ::ng-deep &__hint p {
139177
margin-top: 20px;
140178

0 commit comments

Comments
 (0)