diff --git a/angular.json b/angular.json
index 3525308..97ae561 100644
--- a/angular.json
+++ b/angular.json
@@ -1,5 +1,8 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "cli": {
+ "analytics": "78615b9f-c26a-483b-b3eb-bd26aa21274b"
+ },
"version": 1,
"newProjectRoot": "projects",
"projects": {
diff --git a/package-lock.json b/package-lock.json
index 1db443e..0688929 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1693,6 +1693,35 @@
}
}
},
+ "@fortawesome/angular-fontawesome": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.10.1.tgz",
+ "integrity": "sha512-4nVRm+NcLcdaNrNFhThb/7/tb5CDm0vQwJFyljR3XMCQyEr94hMX5SiUYbvYm9YJVImMYfdpR0lO0B8sh12Vbw==",
+ "requires": {
+ "tslib": "^2.3.1"
+ }
+ },
+ "@fortawesome/fontawesome-common-types": {
+ "version": "0.2.36",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+ "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg=="
+ },
+ "@fortawesome/fontawesome-svg-core": {
+ "version": "1.2.36",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz",
+ "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.36"
+ }
+ },
+ "@fortawesome/free-solid-svg-icons": {
+ "version": "5.15.4",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz",
+ "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.36"
+ }
+ },
"@istanbuljs/schema": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
@@ -1979,6 +2008,12 @@
"jsonc-parser": "3.0.0"
}
},
+ "@socket.io/base64-arraybuffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+ "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
+ "dev": true
+ },
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@@ -2846,12 +2881,6 @@
}
}
},
- "base64-arraybuffer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz",
- "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==",
- "dev": true
- },
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -4133,9 +4162,9 @@
}
},
"date-format": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
- "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz",
+ "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==",
"dev": true
},
"debug": {
@@ -4531,9 +4560,9 @@
}
},
"engine.io": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.0.tgz",
- "integrity": "sha512-ErhZOVu2xweCjEfYcTdkCnEYUiZgkAcBBAhW4jbIvNG8SLU3orAqoJCiytZjYF7eTpVmmCrLDjLIEaPlUAs1uw==",
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz",
+ "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==",
"dev": true,
"requires": {
"@types/cookie": "^0.4.1",
@@ -4563,12 +4592,12 @@
}
},
"engine.io-parser": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.2.tgz",
- "integrity": "sha512-wuiO7qO/OEkPJSFueuATIXtrxF7/6GTbAO9QLv7nnbjwZ5tYhLm9zxvLwxstRs0dcT0KUlWTjtIOs1T86jt12g==",
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
+ "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"dev": true,
"requires": {
- "base64-arraybuffer": "~1.0.1"
+ "@socket.io/base64-arraybuffer": "~1.0.2"
}
},
"enhanced-resolve": {
@@ -5379,9 +5408,9 @@
}
},
"flatted": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
- "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz",
+ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==",
"dev": true
},
"flatten": {
@@ -5391,9 +5420,9 @@
"dev": true
},
"follow-redirects": {
- "version": "1.14.6",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
- "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
+ "version": "1.14.7",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
+ "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"dev": true
},
"for-in": {
@@ -5441,14 +5470,14 @@
"dev": true
},
"fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
+ "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
}
},
"fs-minipass": {
@@ -6803,12 +6832,13 @@
"dev": true
},
"jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"requires": {
- "graceful-fs": "^4.1.6"
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
}
},
"jsonparse": {
@@ -7231,16 +7261,16 @@
}
},
"log4js": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
- "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.0.tgz",
+ "integrity": "sha512-ysc/XUecZJuN8NoKOssk3V0cQ29xY4fra6fnigZa5VwxFsCsvdqsdnEuAxNN89LlHpbE4KUD3zGcn+kFqonSVQ==",
"dev": true,
"requires": {
- "date-format": "^3.0.0",
- "debug": "^4.1.1",
- "flatted": "^2.0.1",
- "rfdc": "^1.1.4",
- "streamroller": "^2.2.4"
+ "date-format": "^4.0.3",
+ "debug": "^4.3.3",
+ "flatted": "^3.2.4",
+ "rfdc": "^1.3.0",
+ "streamroller": "^3.0.2"
}
},
"loglevel": {
@@ -7680,9 +7710,9 @@
"dev": true
},
"nanoid": {
- "version": "3.1.30",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
- "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
+ "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
"dev": true
},
"nanomatch": {
@@ -11462,22 +11492,14 @@
"dev": true
},
"streamroller": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
- "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz",
+ "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==",
"dev": true,
"requires": {
- "date-format": "^2.1.0",
+ "date-format": "^4.0.3",
"debug": "^4.1.1",
- "fs-extra": "^8.1.0"
- },
- "dependencies": {
- "date-format": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
- "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
- "dev": true
- }
+ "fs-extra": "^10.0.0"
}
},
"string-width": {
@@ -12013,9 +12035,9 @@
}
},
"universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"dev": true
},
"unpipe": {
diff --git a/package.json b/package.json
index 72f9faa..c0e05e2 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,9 @@
"@angular/platform-browser": "~12.0.4",
"@angular/platform-browser-dynamic": "~12.0.4",
"@angular/router": "~12.0.4",
+ "@fortawesome/angular-fontawesome": "^0.10.1",
+ "@fortawesome/fontawesome-svg-core": "^1.2.36",
+ "@fortawesome/free-solid-svg-icons": "^5.15.4",
"rxjs": "~6.6.0",
"tslib": "^2.1.0",
"zone.js": "~0.11.4"
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 9126010..3b1c730 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,833 +1,2 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ title }} app is running!
-
-
-
-
-
-
Resources
-
Here are some links to help you get started:
-
-
-
-
-
Next Steps
-
What do you want to do next with your app?
-
-
-
-
-
-
-
-
New Component
-
-
-
-
-
-
Angular Material
-
-
-
-
-
-
Add PWA Support
-
-
-
-
-
-
Add Dependency
-
-
-
-
-
-
Run and Watch Tests
-
-
-
-
-
-
Build for Production
-
-
-
-
-
-
ng generate component xyz
-
ng add @angular/material
-
ng add @angular/pwa
-
ng add _____
-
ng test
-
ng build
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8cd9343..02e2e53 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,11 +1,40 @@
import { NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule, Routes } from '@angular/router';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
+import { CoursesComponent } from './features/courses/courses.component';
+import { CoursesModule } from './features/courses/courses.module';
+import { EditCourseComponent } from './features/courses/edit-course/edit-course.component';
+import { LoginComponent } from './features/login/login.component';
+import { RegistrationComponent } from './features/registration/registration.component';
+import { SharedModule } from './shared/shared.module';
+
+const appRoutes: Routes = [
+ { path: '', component: CoursesComponent },
+ { path: 'login', component: LoginComponent },
+ { path: 'registration', component: RegistrationComponent },
+ { path: 'newCourse', component: EditCourseComponent },
+];
@NgModule({
- declarations: [AppComponent],
- imports: [BrowserModule, AppRoutingModule],
+ declarations: [
+ AppComponent,
+ CoursesComponent,
+ LoginComponent,
+ RegistrationComponent,
+ EditCourseComponent,
+ ],
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ CoursesModule,
+ RouterModule.forRoot(appRoutes),
+ FormsModule,
+ SharedModule,
+ ReactiveFormsModule,
+ ],
providers: [],
bootstrap: [AppComponent],
})
diff --git a/src/app/features/course/course.component.ts b/src/app/features/course/course.component.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/features/course/course.module.ts b/src/app/features/course/course.module.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/features/courses/courseCard.component.css b/src/app/features/courses/courseCard.component.css
new file mode 100644
index 0000000..1e7638c
--- /dev/null
+++ b/src/app/features/courses/courseCard.component.css
@@ -0,0 +1,13 @@
+:host {
+ border: 2px solid #3FF804;
+ margin-top: 1.5em;
+ padding: 1em;
+ display: block;
+}
+
+.information-list {
+ list-style-type: none;
+ display: initial;
+ position: absolute;
+ margin-top: 3em;
+}
diff --git a/src/app/features/courses/courseCard.component.html b/src/app/features/courses/courseCard.component.html
new file mode 100644
index 0000000..3303cbe
--- /dev/null
+++ b/src/app/features/courses/courseCard.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/src/app/features/courses/courseCard.component.ts b/src/app/features/courses/courseCard.component.ts
new file mode 100644
index 0000000..1ff0d89
--- /dev/null
+++ b/src/app/features/courses/courseCard.component.ts
@@ -0,0 +1,26 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+
+@Component({
+ selector: 'card',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: './courseCard.component.html',
+ styleUrls: ['./courseCard.component.css'],
+})
+export class CourseCardComponent {
+ editableTemp: string = '';
+ isEditable: Boolean = false;
+ @Input() title: string = '';
+ @Input() text?: string;
+ @Input() creationDate?: string;
+ @Input() authors?: string;
+ @Input() duration: string = '';
+
+ @Input()
+ set editable(editable: string) {
+ this.editableTemp = editable;
+ this.isEditable = this.editableTemp == 'true';
+ }
+ get editable() {
+ return this.editableTemp;
+ }
+}
diff --git a/src/app/features/courses/courses.component.ts b/src/app/features/courses/courses.component.ts
new file mode 100644
index 0000000..a63e7e4
--- /dev/null
+++ b/src/app/features/courses/courses.component.ts
@@ -0,0 +1,15 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { mockedCourseList } from 'src/assets/courseList';
+
+@Component({
+ selector: 'app-courses',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: `./courses.html`,
+})
+export class CoursesComponent {
+ listOfCourses: Array = mockedCourseList;
+
+ identify(index: number, item: any) {
+ return item.title;
+ }
+}
diff --git a/src/app/features/courses/courses.html b/src/app/features/courses/courses.html
new file mode 100644
index 0000000..b921f00
--- /dev/null
+++ b/src/app/features/courses/courses.html
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/app/features/courses/courses.module.ts b/src/app/features/courses/courses.module.ts
new file mode 100644
index 0000000..c97022b
--- /dev/null
+++ b/src/app/features/courses/courses.module.ts
@@ -0,0 +1,10 @@
+import { NgModule } from '@angular/core';
+import { SharedModule } from 'src/app/shared/shared.module';
+import { CourseCardComponent } from './courseCard.component';
+
+@NgModule({
+ imports: [SharedModule],
+ declarations: [CourseCardComponent],
+ exports: [CourseCardComponent],
+})
+export class CoursesModule {}
diff --git a/src/app/features/courses/edit-course/edit-course.component.css b/src/app/features/courses/edit-course/edit-course.component.css
new file mode 100644
index 0000000..12c012a
--- /dev/null
+++ b/src/app/features/courses/edit-course/edit-course.component.css
@@ -0,0 +1,53 @@
+.edit-form{
+ padding: 2em;
+ border: blue 1px solid;
+}
+.description-area {
+ border: 2px solid #F2F81D;
+ margin-right: 24px;
+ height: 300px;
+ width: 99%;
+ }
+
+ .additional-information {
+ border: 2px solid;
+ width: 99%;
+ margin-top: 33px;
+ padding-left: 12px;
+ height: 427px;
+ }
+ .add-authors-header {
+ display: inline;
+ margin-left: 100px;
+ }
+ .authors-header {
+ display: inline;
+ margin-left: 874px;
+ }
+ .authors-list {
+ position: absolute;
+ list-style-type: none;
+ margin-left: 1026px;
+ }
+ .course-authors-header {
+ display: inline;
+ position: absolute;
+ margin-left: -95px;
+ margin-top: 261px;
+ }
+ .added-authors-list {
+ position: absolute;
+ list-style-type: none;
+ margin-left: 1026px;
+ margin-top: 276px;
+ }
+ .ng-touched.ng-invalid {
+ border: 3px solid red;
+ }
+input{
+ border: 3px solid yellow;
+}
+
+.alert{
+ color: red;
+}
diff --git a/src/app/features/courses/edit-course/edit-course.component.html b/src/app/features/courses/edit-course/edit-course.component.html
new file mode 100644
index 0000000..76a5168
--- /dev/null
+++ b/src/app/features/courses/edit-course/edit-course.component.html
@@ -0,0 +1,78 @@
+
diff --git a/src/app/features/courses/edit-course/edit-course.component.ts b/src/app/features/courses/edit-course/edit-course.component.ts
new file mode 100644
index 0000000..6c60373
--- /dev/null
+++ b/src/app/features/courses/edit-course/edit-course.component.ts
@@ -0,0 +1,20 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { authorValidator } from 'src/app/shared/validators/author-validator';
+
+@Component({
+ selector: 'edit-course',
+ styleUrls: ['./edit-course.component.css'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: './edit-course.component.html',
+})
+export class EditCourseComponent {
+ editCourseForm = new FormGroup({
+ title: new FormControl('', [Validators.required]),
+ description: new FormControl('', [Validators.required]),
+ newAuthor: new FormGroup({
+ authorName: new FormControl('', authorValidator()),
+ }),
+ duration: new FormControl('', [Validators.required, Validators.min(0)]),
+ });
+}
diff --git a/src/app/features/login/login.component.css b/src/app/features/login/login.component.css
new file mode 100644
index 0000000..ba70cb2
--- /dev/null
+++ b/src/app/features/login/login.component.css
@@ -0,0 +1,19 @@
+.login-form{
+ margin-top: 1em;
+ padding-left: 45%;
+ padding-top: 7em;
+ padding-bottom: 7em;
+ border: blue 1px solid;
+ }
+
+ .ng-touched.ng-invalid {
+ border: 3px solid red;
+ }
+input{
+ border: 3px solid yellow;
+}
+
+.alert{
+ color: red;
+}
+
diff --git a/src/app/features/login/login.component.html b/src/app/features/login/login.component.html
new file mode 100644
index 0000000..d99e858
--- /dev/null
+++ b/src/app/features/login/login.component.html
@@ -0,0 +1,44 @@
+
diff --git a/src/app/features/login/login.component.ts b/src/app/features/login/login.component.ts
new file mode 100644
index 0000000..71b6c25
--- /dev/null
+++ b/src/app/features/login/login.component.ts
@@ -0,0 +1,12 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { User } from 'src/app/models/user.medel';
+
+@Component({
+ selector: 'login',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ styleUrls: ['./login.component.css'],
+ templateUrl: './login.component.html',
+})
+export class LoginComponent {
+ @Input() user = new User('', '');
+}
diff --git a/src/app/features/registration/registration.component.css b/src/app/features/registration/registration.component.css
new file mode 100644
index 0000000..6ac1dd9
--- /dev/null
+++ b/src/app/features/registration/registration.component.css
@@ -0,0 +1,19 @@
+.registration-form{
+ margin-top: 1em;
+ padding-left: 45%;
+ padding-top: 7em;
+ padding-bottom: 7em;
+ border: blue 1px solid;
+ }
+
+ .ng-touched.ng-invalid {
+ border: 3px solid red;
+ }
+input{
+ border: 3px solid yellow;
+}
+
+.alert{
+ color: red;
+}
+
diff --git a/src/app/features/registration/registration.component.html b/src/app/features/registration/registration.component.html
new file mode 100644
index 0000000..25bf980
--- /dev/null
+++ b/src/app/features/registration/registration.component.html
@@ -0,0 +1,61 @@
+
diff --git a/src/app/features/registration/registration.component.ts b/src/app/features/registration/registration.component.ts
new file mode 100644
index 0000000..de7550d
--- /dev/null
+++ b/src/app/features/registration/registration.component.ts
@@ -0,0 +1,17 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { emailValidator } from 'src/app/shared/validators/email-validator';
+
+@Component({
+ selector: 'registration',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ styleUrls: ['./registration.component.css'],
+ templateUrl: './registration.component.html',
+})
+export class RegistrationComponent {
+ registrationForm = new FormGroup({
+ name: new FormControl('', [Validators.required, Validators.minLength(6)]),
+ email: new FormControl('', [Validators.required, emailValidator()]),
+ password: new FormControl('', Validators.required),
+ });
+}
diff --git a/src/app/models/user.medel.ts b/src/app/models/user.medel.ts
new file mode 100644
index 0000000..bd122dc
--- /dev/null
+++ b/src/app/models/user.medel.ts
@@ -0,0 +1,8 @@
+export class User {
+ email: string;
+ password: string;
+ constructor(email: string, password: string) {
+ this.email = email;
+ this.password = password;
+ }
+}
diff --git a/src/app/shared/components/button/button.component.css b/src/app/shared/components/button/button.component.css
new file mode 100644
index 0000000..4eac515
--- /dev/null
+++ b/src/app/shared/components/button/button.component.css
@@ -0,0 +1,21 @@
+:host {
+ display: inline;
+ border: 2px solid #DB1DF8;
+ margin-left: 1em;
+ margin-top: 2.2em;
+ background-color: white;
+ vertical-align: middle;
+}
+
+:host:hover {
+ font-weight: bold;
+ font-size: 1em;
+}
+
+:host:active {
+ background-color: gray;
+}
+
+:host:focus {
+ border: 2px solid red;
+}
diff --git a/src/app/shared/components/button/button.component.html b/src/app/shared/components/button/button.component.html
new file mode 100644
index 0000000..4e125fb
--- /dev/null
+++ b/src/app/shared/components/button/button.component.html
@@ -0,0 +1,5 @@
+ 0">
+
+
+
+ 0">{{ buttonText }}
diff --git a/src/app/shared/components/button/button.component.ts b/src/app/shared/components/button/button.component.ts
new file mode 100644
index 0000000..413fe0a
--- /dev/null
+++ b/src/app/shared/components/button/button.component.ts
@@ -0,0 +1,13 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+
+@Component({
+ selector: 'button[common-button]',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ styleUrls: ['./button.component.css'],
+ templateUrl: './button.component.html',
+ host: { class: 'course-buttons' },
+})
+export class ButtonComponent {
+ @Input() buttonText: string = '';
+ @Input() buttonTextIconName: string = '';
+}
diff --git a/src/app/shared/components/header/header.component.css b/src/app/shared/components/header/header.component.css
new file mode 100644
index 0000000..eb89eea
--- /dev/null
+++ b/src/app/shared/components/header/header.component.css
@@ -0,0 +1,20 @@
+:host {
+ border: 2px solid red;
+}
+
+.user-menu {
+ display: inline;
+ margin-left: 82%;
+ position: absolute;
+ margin-top: 2%;
+}
+
+.user-name {
+ margin-right: 22px;
+ display: inline;
+}
+
+.headerImage {
+ margin: -0.6em;
+ width: 6%;
+}
diff --git a/src/app/shared/components/header/header.component.html b/src/app/shared/components/header/header.component.html
new file mode 100644
index 0000000..695fcd3
--- /dev/null
+++ b/src/app/shared/components/header/header.component.html
@@ -0,0 +1,7 @@
+
+
diff --git a/src/app/shared/components/header/header.component.ts b/src/app/shared/components/header/header.component.ts
new file mode 100644
index 0000000..de06a9c
--- /dev/null
+++ b/src/app/shared/components/header/header.component.ts
@@ -0,0 +1,13 @@
+import { Component } from "@angular/core";
+
+
+@Component({
+ selector: 'header[common-header]',
+ templateUrl: './header.component.html',
+ styleUrls: ['./header.component.css']
+})
+
+export class HeaderComponent {
+ imageSrc = `assets/Icon.png`;
+ userName = `Dave`;
+}
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/shared/components/info/info.component.html b/src/app/shared/components/info/info.component.html
new file mode 100644
index 0000000..c687e10
--- /dev/null
+++ b/src/app/shared/components/info/info.component.html
@@ -0,0 +1,3 @@
+
+{{title}}
+{{text}}
diff --git a/src/app/shared/components/info/info.component.ts b/src/app/shared/components/info/info.component.ts
new file mode 100644
index 0000000..79971c3
--- /dev/null
+++ b/src/app/shared/components/info/info.component.ts
@@ -0,0 +1,16 @@
+import { Component, Input } from "@angular/core";
+
+
+@Component({
+ selector: 'info',
+ templateUrl: './info.component.html',
+ styles: [`:host{
+ display: inline-block;
+ width: 78%;
+ }`]
+})
+
+export class InfoComponent {
+ @Input() title: string = '';
+ @Input() text?: string;
+}
diff --git a/src/app/shared/components/search/search.component.css b/src/app/shared/components/search/search.component.css
new file mode 100644
index 0000000..c66ae74
--- /dev/null
+++ b/src/app/shared/components/search/search.component.css
@@ -0,0 +1,3 @@
+.search-input{
+ width: 89%;
+}
diff --git a/src/app/shared/components/search/search.component.html b/src/app/shared/components/search/search.component.html
new file mode 100644
index 0000000..55c3000
--- /dev/null
+++ b/src/app/shared/components/search/search.component.html
@@ -0,0 +1,7 @@
+
diff --git a/src/app/shared/components/search/search.component.ts b/src/app/shared/components/search/search.component.ts
new file mode 100644
index 0000000..8a8274d
--- /dev/null
+++ b/src/app/shared/components/search/search.component.ts
@@ -0,0 +1,16 @@
+import { ChangeDetectionStrategy, Component, Input, Output } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+
+@Component({
+ selector: 'search',
+ styleUrls: ['./search.component.css'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: './search.component.html',
+})
+export class SearchComponent {
+ @Input() searchValue: string = '';
+ @Output() event: any;
+ searchForm = new FormGroup({
+ value: new FormControl(''),
+ });
+}
diff --git a/src/app/shared/directives/email-validator.directive.ts b/src/app/shared/directives/email-validator.directive.ts
new file mode 100644
index 0000000..6922a91
--- /dev/null
+++ b/src/app/shared/directives/email-validator.directive.ts
@@ -0,0 +1,20 @@
+import { Directive } from '@angular/core';
+import {
+ AbstractControl,
+ NG_VALIDATORS,
+ ValidationErrors,
+ Validator,
+} from '@angular/forms';
+import { emailValidator } from '../validators/email-validator';
+
+@Directive({
+ selector: '[email]',
+ providers: [
+ { provide: NG_VALIDATORS, useExisting: EmailValidatorDirective, multi: true },
+ ],
+})
+export class EmailValidatorDirective implements Validator {
+ validate(formControl: AbstractControl): ValidationErrors | null {
+ return emailValidator();
+ }
+}
diff --git a/src/app/shared/icon.module.ts b/src/app/shared/icon.module.ts
new file mode 100644
index 0000000..1ea97d5
--- /dev/null
+++ b/src/app/shared/icon.module.ts
@@ -0,0 +1,16 @@
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FaIconLibrary, FontAwesomeModule } from '@fortawesome/angular-fontawesome';
+import { faPen, faTrashRestoreAlt } from '@fortawesome/free-solid-svg-icons';
+
+@NgModule({
+ imports: [CommonModule, FontAwesomeModule],
+ declarations: [],
+ exports: [],
+})
+export class IconModule {
+ constructor(library: FaIconLibrary) {
+ // Add an icon to the library for convenient access in other components
+ library.addIcons(faPen, faTrashRestoreAlt);
+ }
+}
diff --git a/src/app/shared/pipes/creation-date.pipe.ts b/src/app/shared/pipes/creation-date.pipe.ts
new file mode 100644
index 0000000..e3115da
--- /dev/null
+++ b/src/app/shared/pipes/creation-date.pipe.ts
@@ -0,0 +1,14 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'creation-date',
+})
+export class CreationDatePipe implements PipeTransform {
+ transform(value: Date): string {
+ var dd = String(value.getDate()).padStart(2, '0');
+ var mm = String(value.getMonth() + 1).padStart(2, '0'); //January is 0!
+ var yyyy = value.getFullYear();
+
+ return `${dd}.${mm}.${yyyy}`;
+ }
+}
diff --git a/src/app/shared/pipes/formatdate.pipe.ts b/src/app/shared/pipes/formatdate.pipe.ts
new file mode 100644
index 0000000..ca77190
--- /dev/null
+++ b/src/app/shared/pipes/formatdate.pipe.ts
@@ -0,0 +1,11 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'formatdate'
+})
+export class FormatdatePipe implements PipeTransform {
+ transform(value: string, args?: any): string {
+
+ return `${Math.trunc(Number(value) / 60)}:${Number(value) % 60} hours`;
+ }
+}
diff --git a/src/app/shared/pipes/string-joiner.pipe.ts b/src/app/shared/pipes/string-joiner.pipe.ts
new file mode 100644
index 0000000..48ef1b8
--- /dev/null
+++ b/src/app/shared/pipes/string-joiner.pipe.ts
@@ -0,0 +1,8 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'string-joiner' })
+export class StringJoinerPipe implements PipeTransform {
+ transform(array: string[], separator: string) {
+ return array.join(separator);
+ }
+}
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
new file mode 100644
index 0000000..b527c9d
--- /dev/null
+++ b/src/app/shared/shared.module.ts
@@ -0,0 +1,40 @@
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
+import { ButtonComponent } from './components/button/button.component';
+import { HeaderComponent } from './components/header/header.component';
+import { InfoComponent } from './components/info/info.component';
+import { SearchComponent } from './components/search/search.component';
+import { EmailValidatorDirective } from './directives/email-validator.directive';
+import { IconModule } from './icon.module';
+import { FormatdatePipe } from './pipes/formatdate.pipe';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FontAwesomeModule,
+ IconModule,
+ FormsModule,
+ ReactiveFormsModule,
+ ],
+ declarations: [
+ HeaderComponent,
+ InfoComponent,
+ ButtonComponent,
+ FormatdatePipe,
+ EmailValidatorDirective,
+ SearchComponent,
+ ],
+ exports: [
+ HeaderComponent,
+ InfoComponent,
+ ButtonComponent,
+ FormatdatePipe,
+ CommonModule,
+ IconModule,
+ EmailValidatorDirective,
+ SearchComponent,
+ ],
+})
+export class SharedModule {}
diff --git a/src/app/shared/validators/author-validator.ts b/src/app/shared/validators/author-validator.ts
new file mode 100644
index 0000000..8881ec2
--- /dev/null
+++ b/src/app/shared/validators/author-validator.ts
@@ -0,0 +1,10 @@
+import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
+
+const validateRegExp = new RegExp(`^[A-Za-z0-9]+$`);
+
+export function authorValidator(): ValidatorFn {
+ return (control: AbstractControl): ValidationErrors | null => {
+ const forbidden = validateRegExp.test(control.value);
+ return forbidden ? { InvalidEmail: { value: control.value } } : null;
+ };
+}
diff --git a/src/app/shared/validators/email-validator.ts b/src/app/shared/validators/email-validator.ts
new file mode 100644
index 0000000..acaefdc
--- /dev/null
+++ b/src/app/shared/validators/email-validator.ts
@@ -0,0 +1,10 @@
+import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
+
+const validateRegExp = new RegExp(`^[^@\s]+@[^@\s\.]+\.[^@\.\s]+$`);
+
+export function emailValidator(): ValidatorFn {
+ return (control: AbstractControl): ValidationErrors | null => {
+ const forbidden = validateRegExp.test(control.value);
+ return forbidden ? { InvalidEmail: { value: control.value } } : null;
+ };
+}
diff --git a/src/assets/Icon.png b/src/assets/Icon.png
new file mode 100644
index 0000000..3a5cc1f
Binary files /dev/null and b/src/assets/Icon.png differ
diff --git a/src/assets/courseList.ts b/src/assets/courseList.ts
new file mode 100644
index 0000000..691c523
--- /dev/null
+++ b/src/assets/courseList.ts
@@ -0,0 +1,31 @@
+export const mockedCourseList = [
+ {
+ title: 'JavaScript',
+ description: `Lorem Ipsum is simply dummy text of the printing and
+ typesetting industry. Lorem Ipsum
+ has been the industry's standard dummy text ever since
+ the 1500s, when an unknown
+ printer took a galley of type and scrambled it to make
+ a type specimen book. It has survived
+ not only five centuries, but also the leap into
+ electronic typesetting, remaining essentially u
+ nchanged.`,
+ creationDate: '8/3/2021',
+ duration: 160,
+ authors: ['Vasiliy Dobkin', 'Valentina Larina'],
+ editable: true,
+ },
+ {
+ title: 'Angular',
+ description: `Lorem Ipsum is simply dummy text of the printing and
+ typesetting industry. Lorem Ipsum
+ has been the industry's standard dummy text ever since
+ the 1500s, when an unknown
+ printer took a galley of type and scrambled it to make
+ a type specimen book.`,
+ creationDate: '10/11/2020',
+ duration: 210,
+ authors: ['Anna Sidorenko', 'Nicolas Kim'],
+ editable: false,
+ },
+];
diff --git a/tsconfig.json b/tsconfig.json
index 6df8283..ccc2536 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -26,5 +26,6 @@
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
- }
+ },
+ "strictPropertyInitialization": false
}