Skip to content

Commit

Permalink
chore(course): improve cms and logic
Browse files Browse the repository at this point in the history
  • Loading branch information
danieljancar committed May 15, 2024
1 parent 95c60ca commit f813d0c
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { buildCollection, buildProperty } from 'firecms';
import { buildCollection, buildProperty, getRandomId } from 'firecms';

type ResourcesDocument = {
title: string;
description?: string;
source: string;
createdAt: Date;
updatedAt: Date;
Expand All @@ -27,19 +26,17 @@ export const courseResourcesCollection = buildCollection<ResourcesDocument>({
validation: { required: true },
dataType: 'string',
},
description: {
name: 'Description',
validation: { required: false, min: 3, max: 100000 },
dataType: 'string',
markdown: true,
},
source: buildProperty({
name: 'Source',
validation: { required: true },
dataType: 'string',
storage: {
mediaType: 'file',
storagePath: 'files/courses/resources/',
fileName: (context) => {
const extension = context.file?.name.split('.').pop();
return `${getRandomId()}.${extension}`;
},
},
}),
createdAt: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,62 +1,37 @@
import { buildCollection } from 'firecms';

type TasksDone = {
userId: undefined;
done: boolean;
firstDoneAt: Date;
firstUndoneAt: Date;
lastDoneAt: Date;
lastUndoneAt: Date;
status: boolean;
email: string;
createdAt: Date;
updatedAt: Date;
};

export const courseTasksDoneCollection = buildCollection<TasksDone>({
name: 'Tasks done',
singularName: 'Task done',
path: 'tasks_done',
path: 'done',
icon: 'AssignmentTurnedIn',
defaultSize: 'm',
selectionEnabled: true,
initialSort: ['lastDoneAt', 'desc'],
customId: true,
initialSort: ['updatedAt', 'desc'],
permissions: () => ({
read: true,
edit: true,
create: true,
edit: false,
create: false,
delete: true,
}),
properties: {
userId: {
name: 'User',
validation: { required: true },
dataType: 'reference',
path: 'users',
previewProperties: ['username'],
},
done: {
status: {
name: 'Done',
validation: { required: true },
dataType: 'boolean',
},
firstDoneAt: {
name: 'First done at',
dataType: 'date',
readOnly: true,
},
firstUndoneAt: {
name: 'First undone at',
dataType: 'date',
readOnly: true,
},
lastDoneAt: {
name: 'Last done at',
dataType: 'date',
readOnly: true,
},
lastUndoneAt: {
name: 'Last undone at',
dataType: 'date',
readOnly: true,
email: {
name: 'Email',
validation: { required: true },
dataType: 'string',
},
createdAt: {
name: 'Created at',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ type Task = {
title: string;
description: string;
order: number;
fileInput: boolean;
relatedDocuments?: relatedDocument[];
workPhase: WorkPhase;
createdAt: Date;
updatedAt: Date;
};

type WorkPhase = 'individual' | 'pair' | 'group' | 'plenum';
type relatedDocument = {
title: string;
url: string;
};

export const courseTaskCollection = buildCollection<Task>({
name: 'Tasks',
Expand All @@ -30,7 +36,7 @@ export const courseTaskCollection = buildCollection<Task>({
properties: {
title: {
name: 'Title',
validation: { required: true },
validation: { required: true, min: 3, max: 100 },
dataType: 'string',
},
description: {
Expand All @@ -55,6 +61,36 @@ export const courseTaskCollection = buildCollection<Task>({
plenum: 'Plenum',
},
},
fileInput: {
name: 'File input',
validation: { required: true },
defaultValue: false,
dataType: 'boolean',
disabled: true,
},
relatedDocuments: {
disabled: true,
name: 'Related documents',
validation: { required: false },
dataType: 'array',
of: {
dataType: 'map',
properties: {
title: {
name: 'Title',
validation: { required: true },
dataType: 'string',
},
url: {
name: 'URL',
validation: { required: true },
dataType: 'reference',
path: '',
previewProperties: ['title'],
},
},
},
},
createdAt: {
name: 'Created at',
dataType: 'date',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Timestamp } from 'firebase/firestore';
export type CourseResource = {
id: string;
title: string;
description?: string;
source: string;
createdAt: Timestamp;
updatedAt: Timestamp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type relatedDocument = {
export type CourseTasksDone = {
id?: string;
status: boolean;
email: string;
fileInput?: string;
createdAt?: Timestamp;
updatedAt: Timestamp;
Expand Down
1 change: 1 addition & 0 deletions apps/course/projects/types/src/lib/user.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type User = {
email: string;
username: string;
password?: string;
role: 'user' | 'admin' | undefined;
name?: string;
avatar?: string;
bio?: string;
Expand Down
9 changes: 7 additions & 2 deletions apps/course/src/app/core/data/course.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,13 @@ export class CourseService {
);
}

markTaskAsCompleted(courseId: string, taskId: string, userId: string): void {
markTaskAsCompleted(
courseId: string,
taskId: string,
user: { id: string; email: string },
): void {
const taskDoneRef = this.afs.doc<CourseTasksDone>(
`${this.COURSES_COLLECTION_COLLECTION}/${courseId}/${this.COURSE_TASKS_COLLECTION}/${taskId}/${this.COURSE_TASKS_DONE_COLLECTION}/${userId}`,
`${this.COURSES_COLLECTION_COLLECTION}/${courseId}/${this.COURSE_TASKS_COLLECTION}/${taskId}/${this.COURSE_TASKS_DONE_COLLECTION}/${user.id}`,
);

const currentTime = Timestamp.now();
Expand All @@ -119,6 +123,7 @@ export class CourseService {
if (error.code === 'not-found') {
taskDoneRef.set({
status: true,
email: user.email,
updatedAt: currentTime,
createdAt: currentTime,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export class CourseTasksComponent implements OnInit, OnDestroy {
public isLoading: boolean = true;
public tasks: CourseTask[] | undefined;
public buttonTimer: boolean = false;
private userId: string | undefined;
private user = {
id: '',
email: '',
};
private subscription: Subscription = new Subscription();
private courseStateService = inject(CourseStateService);
private titleService = inject(Title);
Expand All @@ -44,7 +47,8 @@ export class CourseTasksComponent implements OnInit, OnDestroy {
this.subscription.add(
this.userService.user$.subscribe((user) => {
if (user) {
this.userId = user.id;
this.user.id = user.id;
this.user.email = user.email;
this.subscription.add(
this.courseService
.getCourseTasks(course.id, user.id)
Expand All @@ -65,10 +69,10 @@ export class CourseTasksComponent implements OnInit, OnDestroy {
this.buttonTimer = true;
if (!this.tasks) return;
const courseId = this.course?.id;
const userId = this.userId;
const user = this.user;

if (courseId && userId) {
this.courseService.markTaskAsCompleted(courseId, taskId, userId);
if (courseId && user) {
this.courseService.markTaskAsCompleted(courseId, taskId, user);
setTimeout(() => {
this.buttonTimer = false;
}, 1000);
Expand All @@ -85,7 +89,7 @@ export class CourseTasksComponent implements OnInit, OnDestroy {
this.buttonTimer = true;
if (!this.tasks) return;
const courseId = this.course?.id;
const userId = this.userId;
const userId = this.user.id;

if (courseId && userId) {
const task = this.tasks.find((task) => task.id === taskId);
Expand Down
49 changes: 39 additions & 10 deletions apps/course/src/app/guards/auth/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,62 @@
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AngularFireAuth } from '@angular/fire/compat/auth';
import { Observable } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';
import { AngularFirestore } from '@angular/fire/compat/firestore';
import { Observable, of } from 'rxjs';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { ToastService } from '../../core/feedback/toast.service';
import { ToastType } from '../../types/feedback/toast.types';
import { User } from '../../../../projects/types/src/lib/user.types';

@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(
private afa: AngularFireAuth,
private afs: AngularFirestore,
private router: Router,
private toastService: ToastService,
) {}

canActivate(): Observable<boolean> | Promise<boolean> | boolean {
return this.afa.authState.pipe(
take(1),
map((user) => !!user),
tap((loggedIn) => {
if (!loggedIn) {
this.router.navigate(['/a/login']).then(() => {
this.toastService.showToast(
'You must be logged in to access this page.',
ToastType.Info,
switchMap((user) => {
if (user) {
return this.afs
.collection('users')
.doc(user.uid)
.valueChanges()
.pipe(
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'unknown'.
take(1),
map((userData: User) => {
if (
userData &&
(userData.role === 'user' || userData.role === 'admin')
) {
return true;
} else {
this.toastService.showToast(
'You must be a verified user to access this page.',
ToastType.Info,
);
return false;
}
}),
);
});
} else {
this.toastService.showToast(
'You must be logged in to access this page.',
ToastType.Info,
);
return of(false);
}
}),
tap((allowed) => {
if (!allowed) {
this.router.navigate(['/a/login']);
}
}),
);
Expand Down
5 changes: 1 addition & 4 deletions apps/course/src/app/guards/auth/login.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ export class LoginGuard implements CanActivate {
tap((notLoggedIn) => {
if (!notLoggedIn) {
this.router.navigate(['/']).then(() => {
this.toastService.showToast(
'You are already logged in.',
ToastType.Info,
);
this.toastService.showToast('Not yet verified.', ToastType.Info);
});
}
}),
Expand Down

0 comments on commit f813d0c

Please sign in to comment.