aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DevHive.Angular/src/app/app-constants.module.ts8
-rw-r--r--src/DevHive.Angular/src/app/app-routing.module.ts6
-rw-r--r--src/DevHive.Angular/src/app/app.module.ts6
-rw-r--r--src/DevHive.Angular/src/app/components/feed/feed.component.css56
-rw-r--r--src/DevHive.Angular/src/app/components/feed/feed.component.html15
-rw-r--r--src/DevHive.Angular/src/app/components/feed/feed.component.ts34
-rw-r--r--src/DevHive.Angular/src/app/components/login/login.component.ts34
-rw-r--r--src/DevHive.Angular/src/app/components/post/post.component.css53
-rw-r--r--src/DevHive.Angular/src/app/components/post/post.component.html2
-rw-r--r--src/DevHive.Angular/src/app/components/post/post.component.ts34
-rw-r--r--src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css0
-rw-r--r--src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html1
-rw-r--r--src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts15
-rw-r--r--src/DevHive.Angular/src/app/components/profile/profile.component.css88
-rw-r--r--src/DevHive.Angular/src/app/components/profile/profile.component.html45
-rw-r--r--src/DevHive.Angular/src/app/components/profile/profile.component.ts60
-rw-r--r--src/DevHive.Angular/src/app/components/register/register.component.ts48
-rw-r--r--src/DevHive.Angular/src/app/services/feed.service.spec.ts16
-rw-r--r--src/DevHive.Angular/src/app/services/feed.service.ts9
-rw-r--r--src/DevHive.Angular/src/app/services/login.service.spec.ts16
-rw-r--r--src/DevHive.Angular/src/app/services/login.service.ts9
-rw-r--r--src/DevHive.Angular/src/app/services/register.service.spec.ts16
-rw-r--r--src/DevHive.Angular/src/app/services/register.service.ts11
-rw-r--r--src/DevHive.Angular/src/app/services/user.service.ts80
-rw-r--r--src/DevHive.Angular/src/models/identity/register-user.ts37
-rw-r--r--src/DevHive.Angular/src/models/identity/user.ts94
-rw-r--r--src/DevHive.Angular/src/styles.css3
-rw-r--r--src/DevHive.Data/Repositories/TechnologyRepository.cs11
-rw-r--r--src/DevHive.Tests/DevHive.Web.Tests/TechnologyController.Tests.cs137
29 files changed, 650 insertions, 294 deletions
diff --git a/src/DevHive.Angular/src/app/app-constants.module.ts b/src/DevHive.Angular/src/app/app-constants.module.ts
new file mode 100644
index 0000000..df676da
--- /dev/null
+++ b/src/DevHive.Angular/src/app/app-constants.module.ts
@@ -0,0 +1,8 @@
+export class AppConstants {
+ public static BASE_API_URL = 'http://localhost:5000/api';
+ public static API_USER_URL = AppConstants.BASE_API_URL + '/User';
+ public static API_USER_LOGIN_URL = AppConstants.API_USER_URL + '/login';
+ public static API_USER_REGISTER_URL = AppConstants.API_USER_URL + '/register';
+
+ public static FETCH_TIMEOUT = 500;
+}
diff --git a/src/DevHive.Angular/src/app/app-routing.module.ts b/src/DevHive.Angular/src/app/app-routing.module.ts
index b733655..7817dcb 100644
--- a/src/DevHive.Angular/src/app/app-routing.module.ts
+++ b/src/DevHive.Angular/src/app/app-routing.module.ts
@@ -3,11 +3,15 @@ import { Routes, RouterModule } from '@angular/router';
import { FeedComponent } from './components/feed/feed.component';
import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/register/register.component';
+import { ProfileComponent } from './components/profile/profile.component';
+import {ProfileSettingsComponent} from './components/profile-settings/profile-settings.component';
const routes: Routes = [
{ path: '', component: FeedComponent },
{ path: 'login', component: LoginComponent },
- { path: 'register', component: RegisterComponent }
+ { path: 'register', component: RegisterComponent },
+ { path: 'profile/:username', component: ProfileComponent },
+ { path: 'profile/:username/settings', component: ProfileSettingsComponent }
];
@NgModule({
diff --git a/src/DevHive.Angular/src/app/app.module.ts b/src/DevHive.Angular/src/app/app.module.ts
index 6270561..7158941 100644
--- a/src/DevHive.Angular/src/app/app.module.ts
+++ b/src/DevHive.Angular/src/app/app.module.ts
@@ -12,6 +12,8 @@ import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/register/register.component';
import { FeedComponent } from './components/feed/feed.component';
import { PostComponent } from './components/post/post.component';
+import { ProfileComponent } from './components/profile/profile.component';
+import { ProfileSettingsComponent } from './components/profile-settings/profile-settings.component';
@NgModule({
declarations: [
@@ -19,7 +21,9 @@ import { PostComponent } from './components/post/post.component';
LoginComponent,
RegisterComponent,
FeedComponent,
- PostComponent
+ PostComponent,
+ ProfileComponent,
+ ProfileSettingsComponent
],
imports: [
BrowserModule,
diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.css b/src/DevHive.Angular/src/app/components/feed/feed.component.css
index 07667c2..aa3c392 100644
--- a/src/DevHive.Angular/src/app/components/feed/feed.component.css
+++ b/src/DevHive.Angular/src/app/components/feed/feed.component.css
@@ -1,7 +1,7 @@
#feed-page {
max-width: 40em;
margin: 0 auto;
- border: .5em solid white;
+ border: .5em solid var(--bg-color);
border-bottom: 0;
box-sizing: border-box;
display: flex;
@@ -31,10 +31,21 @@
}
#posts {
- max-height: calc(100vh - 3em);
+ max-height: calc(100vh - 3.5em);
overflow-y: auto;
}
+ /* Hide scrollbar for Chrome, Safari and Opera */
+#posts::-webkit-scrollbar {
+ display: none;
+}
+
+ /* Hide scrollbar for IE, Edge and Firefox */
+#posts {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+}
+
/* Profile bar */
#profile-bar {
@@ -43,9 +54,14 @@
display: flex;
flex-direction: column;
align-items: center;
+
box-sizing: border-box;
+ background-color: var(--bg-color);
+
margin-right: .5em;
padding-bottom: 1em;
+ padding: .5em;
+ border-radius: .6em;
}
#profile-bar-profile-pic {
@@ -63,12 +79,11 @@
#top-bar {
display: flex;
+ width: 95%;
+ margin: 0 auto;
+ margin-bottom: .5em;
}
-#top-bar img {
- height: 1.6em;
- width: 1.6em;
-}
#top-bar-profile-pic {
height: 2.5em;
@@ -84,9 +99,38 @@
#top-bar-create-post {
flex: 1;
font-size: inherit;
+ width: 100%;
+ margin: 0 auto;
+ box-sizing: border-box;
+ border: 2px solid var(--bg-color);
+ padding: .5em;
+ border-radius: .6em;
+ box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
}
#top-bar-open-chat {
/* Until implemented */
display: none;
}
+
+#profile-bar,
+#top-bar > * {
+ box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
+}
+
+/* Elements, that act as buttons */
+
+#profile-bar:hover,
+#top-bar-profile-pic:hover {
+ cursor: pointer;
+}
+
+/* Can't copy text from */
+
+#profile-bar,
+.vote {
+ -webkit-user-select: none; /* Safari */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* IE10+/Edge */
+ user-select: none; /* Standard */
+}
diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.html b/src/DevHive.Angular/src/app/components/feed/feed.component.html
index ffbad14..5d7c86a 100644
--- a/src/DevHive.Angular/src/app/components/feed/feed.component.html
+++ b/src/DevHive.Angular/src/app/components/feed/feed.component.html
@@ -1,5 +1,10 @@
-<div id="feed-page">
- <nav id="profile-bar" class="rounded-border">
+<!-- TODO: replace with loading component -->
+<div id="content" *ngIf="!dataArrived">
+ Loading...
+</div>
+
+<div id="feed-page" *ngIf="dataArrived">
+ <nav id="profile-bar" (click)="goToProfile()">
<img id="profile-bar-profile-pic" class="round-image" src="assets/images/feed/profile-pic.png" alt=""/>
<div id="profile-bar-name">
{{ user.firstName }} {{ user.lastName }}
@@ -10,10 +15,8 @@
</nav>
<div id="feed-content">
<nav id="top-bar">
- <a id="top-bar-profile-pic" class="round-image" href="">
- <img class="round-image" src="assets/images/feed/profile-pic.png" alt=""/>
- </a>
- <input id="top-bar-create-post" class="rounded-border" type="text" placeholder="What's on your mind?"/>
+ <img id="top-bar-profile-pic" class="round-image" src="assets/images/feed/profile-pic.png" alt="" (click)="goToProfile()">
+ <input id="top-bar-create-post" type="text" placeholder="What's on your mind?"/>
<a id="top-bar-open-chat" href="">
<img src="assets/images/feed/chat-pic.png" alt=""/>
</a>
diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.ts b/src/DevHive.Angular/src/app/components/feed/feed.component.ts
index 2889e7f..7d37c9a 100644
--- a/src/DevHive.Angular/src/app/components/feed/feed.component.ts
+++ b/src/DevHive.Angular/src/app/components/feed/feed.component.ts
@@ -1,13 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Router } from '@angular/router';
-import { Guid } from 'guid-typescript';
-import jwt_decode from 'jwt-decode';
-import { FeedService } from 'src/app/services/feed.service';
import { User } from 'src/models/identity/user';
-import { IJWTPayload } from 'src/interfaces/jwt-payload';
-import { IUserCredentials } from 'src/interfaces/user-credentials';
import { PostComponent } from '../post/post.component';
+import { UserService } from '../../services/user.service';
+import { AppConstants } from 'src/app/app-constants.module';
@Component({
selector: 'app-feed',
@@ -16,17 +13,15 @@ import { PostComponent } from '../post/post.component';
})
export class FeedComponent implements OnInit {
private _title = 'Feed';
+ public dataArrived = false;
public user: User;
public posts: PostComponent[];
- constructor(private titleService: Title, private service: FeedService, private router: Router) {
- this.titleService.setTitle(this._title);
+ constructor(private _titleService: Title, private _router: Router, private _userService: UserService) {
+ this._titleService.setTitle(this._title);
}
ngOnInit(): void {
- // Default values, so it doesn't give an error while initializing
- this.user = new User(Guid.createEmpty(), '', '', '', '');
-
this.posts = [
new PostComponent(),
new PostComponent(),
@@ -35,21 +30,16 @@ export class FeedComponent implements OnInit {
];
if (sessionStorage.getItem('UserCred')) {
- const jwt: IJWTPayload = JSON.parse(sessionStorage.getItem('UserCred') ?? '');
- const userCred = jwt_decode<IUserCredentials>(jwt.token);
- this.saveUserData(userCred.ID, jwt.token);
+ // Workaround for waiting the fetch response
+ // TODO: properly wait for it, before loading the page contents
+ setTimeout(() => { this.user = this._userService.fetchUserFromSessionStorage(); }, AppConstants.FETCH_TIMEOUT);
+ setTimeout(() => { this.dataArrived = true; }, AppConstants.FETCH_TIMEOUT + 100);
} else {
- this.router.navigate(['/login']);
+ this._router.navigate(['/login']);
}
}
- saveUserData(userId: Guid, authToken: string): void {
- fetch(`http://localhost:5000/api/User?Id=${userId}`, {
- method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer ' + authToken
- }
- }).then(response => response.json()).then(data => Object.assign(this.user, data));
+ goToProfile(): void {
+ this._router.navigate(['/profile/' + this.user.userName]);
}
}
diff --git a/src/DevHive.Angular/src/app/components/login/login.component.ts b/src/DevHive.Angular/src/app/components/login/login.component.ts
index b3698e2..a9206f2 100644
--- a/src/DevHive.Angular/src/app/components/login/login.component.ts
+++ b/src/DevHive.Angular/src/app/components/login/login.component.ts
@@ -1,7 +1,8 @@
import { Component, OnInit } from '@angular/core';
-import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
+import { FormGroup, FormBuilder, Validators, FormControl, AbstractControl } from '@angular/forms';
import { Router } from '@angular/router';
import { Title } from '@angular/platform-browser';
+import { UserService } from 'src/app/services/user.service';
@Component({
selector: 'app-login',
@@ -9,16 +10,15 @@ import { Title } from '@angular/platform-browser';
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
- loginUserFormGroup: FormGroup;
-
private _title = 'Login';
+ loginUserFormGroup: FormGroup;
- constructor(private titleService: Title, private fb: FormBuilder, private router: Router) {
- titleService.setTitle(this._title);
+ constructor(private _titleService: Title, private _fb: FormBuilder, private _router: Router, private _userService: UserService) {
+ this._titleService.setTitle(this._title);
}
ngOnInit(): void {
- this.loginUserFormGroup = this.fb.group({
+ this.loginUserFormGroup = this._fb.group({
username: new FormControl('', [
Validators.required
]),
@@ -29,31 +29,19 @@ export class LoginComponent implements OnInit {
}
async onSubmit(): Promise<void> {
- const response = await fetch('http://localhost:5000/api/User/login', {
- method: 'POST',
- body: JSON.stringify({
- UserName: this.loginUserFormGroup.get('username')?.value,
- Password: this.loginUserFormGroup.get('password')?.value
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- });
- const userCred: string = await response.json();
-
- sessionStorage.setItem('UserCred', JSON.stringify(userCred));
- this.router.navigate(['/']);
+ this._userService.loginUser(this.loginUserFormGroup);
+ this._router.navigate(['/']);
}
onRedirectRegister(): void {
- this.router.navigate(['/register']);
+ this._router.navigate(['/register']);
}
- get username() {
+ get username(): AbstractControl | null {
return this.loginUserFormGroup.get('username');
}
- get password() {
+ get password(): AbstractControl | null {
return this.loginUserFormGroup.get('password');
}
}
diff --git a/src/DevHive.Angular/src/app/components/post/post.component.css b/src/DevHive.Angular/src/app/components/post/post.component.css
index e53eb2c..4769191 100644
--- a/src/DevHive.Angular/src/app/components/post/post.component.css
+++ b/src/DevHive.Angular/src/app/components/post/post.component.css
@@ -1,7 +1,26 @@
.post {
display: flex;
- width: inherit;
- margin: .5em 0;
+ width: 95%;
+
+ margin: .5em auto;
+ box-sizing: border-box;
+ border: 2px solid var(--card-bg);
+ padding: .5em;
+ background-color: var(--card-bg);
+ border-radius: .6em;
+box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
+}
+
+ /* So the bottom shadow of the last element
+ * doesn't get cut off
+ */
+.post:nth-last-child(1) {
+ margin-bottom: 1.1em;
+}
+
+hr {
+ border: 1px solid black;
+ width: 90%;
}
/* Author */
@@ -43,22 +62,32 @@
display: flex;
flex-direction: column;
align-items: center;
- margin-right: -.2em;
+ margin-right: -.1em;
+}
+
+.score {
+ flex: 1;
+ display: flex;
+ align-items: center;
}
+
.vote {
+ display: flex;
+ align-items: center;
flex: 1;
- background: white;
+
+ background: var(--card-bg);
font-size: 1em;
+
+ border: 1px solid var(--card-bg);
box-sizing: border-box;
- border: 2px solid white;
- border-radius: .3em;
-}
+ border-radius: .2em;
-.vote:hover {
- background: lightgray;
-}
+ }
-.vote:active {
- border-color: black;
+.vote:hover {
+ border: 1px solid var(--focus-color);
+ color: var(--focus-color);
+ cursor: pointer;
}
diff --git a/src/DevHive.Angular/src/app/components/post/post.component.html b/src/DevHive.Angular/src/app/components/post/post.component.html
index b6fab55..041158f 100644
--- a/src/DevHive.Angular/src/app/components/post/post.component.html
+++ b/src/DevHive.Angular/src/app/components/post/post.component.html
@@ -1,4 +1,4 @@
-<div class="post rounded-border">
+<div class="post">
<div class="content">
<div class="author">
<img class="round-image" src="assets/images/feed/profile-pic.png">
diff --git a/src/DevHive.Angular/src/app/components/post/post.component.ts b/src/DevHive.Angular/src/app/components/post/post.component.ts
index 72c7aec..b4bb2f0 100644
--- a/src/DevHive.Angular/src/app/components/post/post.component.ts
+++ b/src/DevHive.Angular/src/app/components/post/post.component.ts
@@ -3,26 +3,26 @@ import { Guid } from 'guid-typescript';
import { User } from 'src/models/identity/user';
@Component({
- selector: 'app-post',
- templateUrl: './post.component.html',
- styleUrls: ['./post.component.css'],
+ selector: 'app-post',
+ templateUrl: './post.component.html',
+ styleUrls: ['./post.component.css'],
})
export class PostComponent implements OnInit {
- public user: User;
- public votesNumber: number;
+ public user: User;
+ public votesNumber: number;
- constructor() {}
+ constructor() {}
- ngOnInit(): void {
- //Fetch data in post service
- this.user = new User(
- Guid.create(),
- 'gosho_trapov',
- 'Gosho',
- 'Trapov',
- 'assets/images/feed/profile-pic.png'
- );
+ ngOnInit(): void {
+ // Fetch data in post service
+ this.user = new User(
+ Guid.create(),
+ 'gosho_trapov',
+ 'Gosho',
+ 'Trapov',
+ 'assets/images/feed/profile-pic.png'
+ );
- this.votesNumber = 23;
- }
+ this.votesNumber = 23;
+ }
}
diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css
diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html
new file mode 100644
index 0000000..0cc443e
--- /dev/null
+++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html
@@ -0,0 +1 @@
+<p>profile-settings works!</p>
diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts
new file mode 100644
index 0000000..9226aae
--- /dev/null
+++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-profile-settings',
+ templateUrl: './profile-settings.component.html',
+ styleUrls: ['./profile-settings.component.css']
+})
+export class ProfileSettingsComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.css b/src/DevHive.Angular/src/app/components/profile/profile.component.css
new file mode 100644
index 0000000..6954b22
--- /dev/null
+++ b/src/DevHive.Angular/src/app/components/profile/profile.component.css
@@ -0,0 +1,88 @@
+* {
+ box-sizing: border-box;
+}
+
+#content {
+ max-width: 22em;
+ justify-content: start;
+}
+
+hr {
+ width: calc(100% - 1em);
+ color: black;
+ border: 1px solid black;
+}
+
+/* Navigation bar (for loggedin user) */
+
+#navigation {
+ display: flex;
+ width: 100%;
+}
+
+.submit-btn {
+ flex: 1;
+ margin-top: 0;
+ margin-left: .5em;
+ font-size: inherit;
+}
+
+#navigation > .submit-btn:nth-of-type(1) {
+ margin-left: 0;
+}
+
+/* Top card */
+
+#main-info {
+ display: flex;
+ width: 100%;
+ margin-bottom: .25em
+}
+
+#main-info > img {
+ width: 5em;
+ height: 5em;
+}
+
+#other-main-info {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+#other-main-info > * {
+ font-size: 1.4em;
+}
+
+#other-main-info *:nth-child(1) {
+ margin-top: auto;
+}
+
+#other-main-info *:nth-last-child(1) {
+ margin-bottom: auto;
+}
+
+/* Languages and technologies */
+
+.secondary-info {
+ margin-top: .25em;
+ margin-bottom: .25em;
+ width: 100%;
+ display: flex;
+ align-items: center;
+}
+
+.user-language, .user-technology {
+ border-radius: .4em;
+ background-color: lightgrey;
+ padding: .2em;
+ margin: 0 .2em;
+}
+
+/* Posts */
+
+#posts {
+ width: 100%;
+}
+
diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.html b/src/DevHive.Angular/src/app/components/profile/profile.component.html
new file mode 100644
index 0000000..5303db3
--- /dev/null
+++ b/src/DevHive.Angular/src/app/components/profile/profile.component.html
@@ -0,0 +1,45 @@
+<!-- TODO: replace with loading component -->
+<div id="content" style="justify-content: center;" *ngIf="!dataArrived">
+ Loading...
+</div>
+
+<div id="content" *ngIf="dataArrived">
+ <nav id="navigation">
+ <button class="submit-btn" type="submit" (click)="goBack()">ᐊ Back</button>
+ <button class="submit-btn" type="submit" (click)="navigateToSettings()" *ngIf="loggedInUser">Settings</button>
+ </nav>
+ <hr>
+ <div id="main-info" class="rounded-border">
+ <img class="round-image" src="assets/images/feed/profile-pic.png" alt=""/>
+ <div id="other-main-info">
+ <div id="name">
+ {{ user.firstName }} {{ user.lastName }}
+ </div>
+ <div id="username">
+ @{{ user.userName }}
+ </div>
+ </div>
+ </div>
+ <div class="secondary-info rounded-border">
+ Languages:
+ <div class="user-language">
+ C#
+ </div>
+ <div class="user-language">
+ TypeScript
+ </div>
+ </div>
+ <div class="secondary-info rounded-border">
+ Technologies:
+ <div class="user-technology">
+ .NET 5
+ </div>
+ <div class="user-technology">
+ Angular
+ </div>
+ </div>
+ <div id="posts">
+ <hr>
+ Posts go here
+ </div>
+</div>
diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.ts b/src/DevHive.Angular/src/app/components/profile/profile.component.ts
new file mode 100644
index 0000000..16bb053
--- /dev/null
+++ b/src/DevHive.Angular/src/app/components/profile/profile.component.ts
@@ -0,0 +1,60 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { UserService } from 'src/app/services/user.service';
+import { User } from 'src/models/identity/user';
+import { AppConstants } from 'src/app/app-constants.module';
+
+@Component({
+ selector: 'app-profile',
+ templateUrl: './profile.component.html',
+ styleUrls: ['./profile.component.css']
+})
+export class ProfileComponent implements OnInit {
+ public loggedInUser = false;
+ public dataArrived = false;
+ public user: User;
+
+ constructor(private _router: Router, private _userService: UserService)
+ { }
+
+ private setDefaultUser(): void {
+ this.user = this._userService.getDefaultUser();
+ }
+
+ ngOnInit(): void {
+ const username = this._router.url.substring(9);
+ console.log(username);
+
+ if (sessionStorage.getItem('UserCred')) {
+ // Workaround for waiting the fetch response
+ // TODO: properly wait for it, before loading the page contents
+ setTimeout(() => {
+ this.user = this._userService.fetchUserFromSessionStorage();
+ }, AppConstants.FETCH_TIMEOUT);
+
+ // After getting the user, check if we're on the profile page of the logged in user
+ setTimeout(() => {
+ if (this.user.userName !== username) {
+ this.setDefaultUser();
+ } else {
+ this.loggedInUser = true;
+ }
+ }, AppConstants.FETCH_TIMEOUT + 50);
+ }
+ else {
+ this.setDefaultUser();
+ }
+
+ setTimeout(() => {
+ this.dataArrived = true;
+ }, AppConstants.FETCH_TIMEOUT + 100);
+ }
+
+ goBack(): void {
+ this._router.navigate(['/']);
+ }
+
+ navigateToSettings(): void {
+ this._router.navigate([this._router.url + '/settings']);
+ }
+}
diff --git a/src/DevHive.Angular/src/app/components/register/register.component.ts b/src/DevHive.Angular/src/app/components/register/register.component.ts
index 7304589..3fdfbcd 100644
--- a/src/DevHive.Angular/src/app/components/register/register.component.ts
+++ b/src/DevHive.Angular/src/app/components/register/register.component.ts
@@ -1,7 +1,8 @@
import { Component, OnInit } from '@angular/core';
-import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
+import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { Title } from '@angular/platform-browser';
import { Router } from '@angular/router';
+import { UserService } from 'src/app/services/user.service';
@Component({
selector: 'app-register',
@@ -9,16 +10,15 @@ import { Router } from '@angular/router';
styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
- public registerUserFormGroup: FormGroup;
-
private _title = 'Register';
+ public registerUserFormGroup: FormGroup;
- constructor(private titleService: Title, private fb: FormBuilder, private router: Router) {
- titleService.setTitle(this._title);
+ constructor(private _titleService: Title, private _fb: FormBuilder, private _router: Router, private _userService: UserService) {
+ this._titleService.setTitle(this._title);
}
ngOnInit(): void {
- this.registerUserFormGroup = this.fb.group({
+ this.registerUserFormGroup = this._fb.group({
firstName: new FormControl('', [
Validators.required,
Validators.minLength(3)
@@ -45,50 +45,32 @@ export class RegisterComponent implements OnInit {
this.registerUserFormGroup.valueChanges.subscribe(console.log);
}
- async onSubmit(): Promise<void> {
- // TODO: add a check for form data validity
-
- const response = await fetch('http://localhost:5000/api/User/register', {
- method: 'POST',
- body: JSON.stringify({
- UserName: this.registerUserFormGroup.get('username')?.value,
- Email: this.registerUserFormGroup.get('email')?.value,
- FirstName: this.registerUserFormGroup.get('firstName')?.value,
- LastName: this.registerUserFormGroup.get('lastName')?.value,
- Password: this.registerUserFormGroup.get('password')?.value
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- });
- const userCred: string = await response.json();
-
- // TODO: don't redirect if there is an error response
- sessionStorage.setItem('UserCred', JSON.stringify(userCred));
- this.router.navigate(['/']);
+ onSubmit(): void {
+ this._userService.registerUser(this.registerUserFormGroup);
+ this._router.navigate(['/']);
}
onRedirectRegister(): void {
- this.router.navigate(['/login']);
+ this._router.navigate(['/login']);
}
- get firstName() {
+ get firstName(): AbstractControl | null {
return this.registerUserFormGroup.get('firstName');
}
- get lastName() {
+ get lastName(): AbstractControl | null {
return this.registerUserFormGroup.get('lastName');
}
- get username() {
+ get username(): AbstractControl | null {
return this.registerUserFormGroup.get('username');
}
- get email() {
+ get email(): AbstractControl | null {
return this.registerUserFormGroup.get('email');
}
- get password() {
+ get password(): AbstractControl | null {
return this.registerUserFormGroup.get('password');
}
}
diff --git a/src/DevHive.Angular/src/app/services/feed.service.spec.ts b/src/DevHive.Angular/src/app/services/feed.service.spec.ts
deleted file mode 100644
index 656b960..0000000
--- a/src/DevHive.Angular/src/app/services/feed.service.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { FeedService } from './feed.service';
-
-describe('FeedService', () => {
- let service: FeedService;
-
- beforeEach(() => {
- TestBed.configureTestingModule({});
- service = TestBed.inject(FeedService);
- });
-
- it('should be created', () => {
- expect(service).toBeTruthy();
- });
-});
diff --git a/src/DevHive.Angular/src/app/services/feed.service.ts b/src/DevHive.Angular/src/app/services/feed.service.ts
deleted file mode 100644
index 23284b9..0000000
--- a/src/DevHive.Angular/src/app/services/feed.service.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable({
- providedIn: 'root'
-})
-export class FeedService {
-
- constructor() { }
-}
diff --git a/src/DevHive.Angular/src/app/services/login.service.spec.ts b/src/DevHive.Angular/src/app/services/login.service.spec.ts
deleted file mode 100644
index 299b0d5..0000000
--- a/src/DevHive.Angular/src/app/services/login.service.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { LoginService } from './login.service';
-
-describe('LoginService', () => {
- let service: LoginService;
-
- beforeEach(() => {
- TestBed.configureTestingModule({});
- service = TestBed.inject(LoginService);
- });
-
- it('should be created', () => {
- expect(service).toBeTruthy();
- });
-});
diff --git a/src/DevHive.Angular/src/app/services/login.service.ts b/src/DevHive.Angular/src/app/services/login.service.ts
deleted file mode 100644
index 92c777e..0000000
--- a/src/DevHive.Angular/src/app/services/login.service.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable({
- providedIn: 'root'
-})
-export class LoginService {
-
- constructor() { }
-}
diff --git a/src/DevHive.Angular/src/app/services/register.service.spec.ts b/src/DevHive.Angular/src/app/services/register.service.spec.ts
deleted file mode 100644
index 2ba3960..0000000
--- a/src/DevHive.Angular/src/app/services/register.service.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { RegisterService } from './register.service';
-
-describe('RegisterService', () => {
- let service: RegisterService;
-
- beforeEach(() => {
- TestBed.configureTestingModule({});
- service = TestBed.inject(RegisterService);
- });
-
- it('should be created', () => {
- expect(service).toBeTruthy();
- });
-});
diff --git a/src/DevHive.Angular/src/app/services/register.service.ts b/src/DevHive.Angular/src/app/services/register.service.ts
deleted file mode 100644
index 52cb38e..0000000
--- a/src/DevHive.Angular/src/app/services/register.service.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable({
- providedIn: 'root'
-})
-export class RegisterService {
-
- constructor() { }
-
-
-}
diff --git a/src/DevHive.Angular/src/app/services/user.service.ts b/src/DevHive.Angular/src/app/services/user.service.ts
new file mode 100644
index 0000000..8c679d7
--- /dev/null
+++ b/src/DevHive.Angular/src/app/services/user.service.ts
@@ -0,0 +1,80 @@
+import { Injectable } from '@angular/core';
+import {Guid} from 'guid-typescript';
+import { User } from '../../models/identity/user';
+import jwt_decode from 'jwt-decode';
+import { IJWTPayload } from '../../interfaces/jwt-payload';
+import { IUserCredentials } from '../../interfaces/user-credentials';
+import { FormGroup } from '@angular/forms';
+import { AppConstants } from 'src/app/app-constants.module';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UserService {
+ constructor() { }
+
+ getDefaultUser(): User {
+ return new User(Guid.createEmpty(), 'gosho_trapov', 'Gosho', 'Trapov', '');
+ }
+
+ fetchUserFromSessionStorage(): User {
+ // Get the token and userid from session storage
+ const jwt: IJWTPayload = JSON.parse(sessionStorage.getItem('UserCred') ?? '');
+ const userCred = jwt_decode<IUserCredentials>(jwt.token);
+
+ return this.fetchUser(userCred.ID, jwt.token);
+ }
+
+ fetchUser(userId: Guid, authToken: string): User {
+ const fetchedUser: User = new User(Guid.createEmpty(), '', '', '', '');
+
+ // Fetch the data and assign it to fetchedUser
+ fetch(AppConstants.API_USER_URL + '?Id=' + userId, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: 'Bearer ' + authToken
+ }
+ }).then(response => response.json())
+ .then(data => Object.assign(fetchedUser, data));
+
+ return fetchedUser;
+ }
+
+ // TODO: make return bool when the response is an error
+ loginUser(loginUserFormGroup: FormGroup): void {
+ // Save the fetch reponse in the sessionStorage
+ fetch(AppConstants.API_USER_LOGIN_URL, {
+ method: 'POST',
+ body: JSON.stringify({
+ UserName: loginUserFormGroup.get('username')?.value,
+ Password: loginUserFormGroup.get('password')?.value
+ }),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }).then(response => response.json())
+ .then(data => sessionStorage.setItem('UserCred', JSON.stringify(data)));
+ }
+
+ // TODO: make return bool when the response is an error
+ registerUser(registerUserFormGroup: FormGroup): void {
+ // TODO: add a check for form data validity
+
+ // Like in login, save the fetch reponse in the sessionStorage
+ fetch(AppConstants.API_USER_REGISTER_URL, {
+ method: 'POST',
+ body: JSON.stringify({
+ UserName: registerUserFormGroup.get('username')?.value,
+ Email: registerUserFormGroup.get('email')?.value,
+ FirstName: registerUserFormGroup.get('firstName')?.value,
+ LastName: registerUserFormGroup.get('lastName')?.value,
+ Password: registerUserFormGroup.get('password')?.value
+ }),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }).then(response => response.json())
+ .then(data => sessionStorage.setItem('UserCred', JSON.stringify(data)));
+ }
+}
diff --git a/src/DevHive.Angular/src/models/identity/register-user.ts b/src/DevHive.Angular/src/models/identity/register-user.ts
deleted file mode 100644
index f96b8bb..0000000
--- a/src/DevHive.Angular/src/models/identity/register-user.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-export class RegisterUser
-{
- private _userName: string;
- private _password: string;
-
- // constructor(userName: string, password: string)
- // {
- // this.userName = userName;
- // this.password = password;
- // }
-
- public get userName()
- {
- return this._userName;
- }
-
- public set userName(userName: string)
- {
- if (userName.length <= 3)
- throw new Error('Username cannot be less than 3 characters long!');
-
- this._userName = userName;
- }
-
- public get password()
- {
- return this._password;
- }
-
- public set password(pass: string)
- {
- if (pass.length <= 5)
- throw Error("Password too short!");
-
- this._password = pass;
- }
-}
diff --git a/src/DevHive.Angular/src/models/identity/user.ts b/src/DevHive.Angular/src/models/identity/user.ts
index e16c908..2c5c57d 100644
--- a/src/DevHive.Angular/src/models/identity/user.ts
+++ b/src/DevHive.Angular/src/models/identity/user.ts
@@ -1,52 +1,52 @@
-import { Guid } from "guid-typescript";
+import { Guid } from 'guid-typescript';
export class User {
- private _id : Guid;
- private _lastName : string;
- private _firstName : string;
- private _userName : string;
- private _imageUrl : string;
+ private _id : Guid;
+ private _lastName : string;
+ private _firstName : string;
+ private _userName : string;
+ private _imageUrl : string;
- constructor(id: Guid, userName: string, firstName: string, lastName: string, imageUrl: string) {
- this.id = id;
- this.userName = userName;
- this.firstName = firstName;
- this.lastName = lastName;
- }
+ constructor(id: Guid, userName: string, firstName: string, lastName: string, imageUrl: string) {
+ this.id = id;
+ this.userName = userName;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.imageUrl = imageUrl;
+ }
-
- public get id() : Guid {
- return this._id;
- }
- public set id(v : Guid) {
- this._id = v;
- }
-
- public get userName() : string {
- return this._userName;
- }
- public set userName(v : string) {
- this._userName = v;
- }
-
- public get firstName() : string {
- return this._firstName;
- }
- public set firstName(v : string) {
- this._firstName = v;
- }
-
- public get lastName() : string {
- return this._lastName;
- }
- public set lastName(v: string) {
- this._lastName = v;
- }
-
- public get imageUrl() : string {
- return this._imageUrl;
- }
- public set imageUrl(v : string) {
- this._imageUrl = v;
- }
+ public get id(): Guid {
+ return this._id;
+ }
+ public set id(v: Guid) {
+ this._id = v;
+ }
+
+ public get userName(): string {
+ return this._userName;
+ }
+ public set userName(v: string) {
+ this._userName = v;
+ }
+
+ public get firstName(): string {
+ return this._firstName;
+ }
+ public set firstName(v: string) {
+ this._firstName = v;
+ }
+
+ public get lastName(): string {
+ return this._lastName;
+ }
+ public set lastName(v: string) {
+ this._lastName = v;
+ }
+
+ public get imageUrl(): string {
+ return this._imageUrl;
+ }
+ public set imageUrl(v: string) {
+ this._imageUrl = v;
+ }
}
diff --git a/src/DevHive.Angular/src/styles.css b/src/DevHive.Angular/src/styles.css
index 3d6d2f4..95592a1 100644
--- a/src/DevHive.Angular/src/styles.css
+++ b/src/DevHive.Angular/src/styles.css
@@ -4,6 +4,7 @@
:root {
--bg-color: white;
--focus-color: forestgreen;
+ --card-bg: white;
}
html, body {
@@ -12,6 +13,7 @@ html, body {
}
body {
font: 21px sans-serif !important;
+ background-color: var(--bg-color);
}
input:focus, button:focus {
@@ -68,6 +70,7 @@ input:focus, button:focus {
.input-field {
width: 100%;
+ background-color: var(--bg-color);
border: 0;
border-bottom: 1px solid grey;
diff --git a/src/DevHive.Data/Repositories/TechnologyRepository.cs b/src/DevHive.Data/Repositories/TechnologyRepository.cs
index 1631972..73827a7 100644
--- a/src/DevHive.Data/Repositories/TechnologyRepository.cs
+++ b/src/DevHive.Data/Repositories/TechnologyRepository.cs
@@ -27,12 +27,6 @@ namespace DevHive.Data.Repositories
return await RepositoryMethods.SaveChangesAsync(this._context);
}
-
- public async Task<Technology> GetByNameAsync(string technologyName)
- {
- return await this._context.Technologies
- .FirstOrDefaultAsync(x => x.Name == technologyName);
- }
#endregion
#region Read
@@ -43,6 +37,11 @@ namespace DevHive.Data.Repositories
.Set<Technology>()
.FindAsync(id);
}
+ public async Task<Technology> GetByNameAsync(string technologyName)
+ {
+ return await this._context.Technologies
+ .FirstOrDefaultAsync(x => x.Name == technologyName);
+ }
#endregion
#region Edit
diff --git a/src/DevHive.Tests/DevHive.Web.Tests/TechnologyController.Tests.cs b/src/DevHive.Tests/DevHive.Web.Tests/TechnologyController.Tests.cs
index 711f66f..2709c9a 100644
--- a/src/DevHive.Tests/DevHive.Web.Tests/TechnologyController.Tests.cs
+++ b/src/DevHive.Tests/DevHive.Web.Tests/TechnologyController.Tests.cs
@@ -6,6 +6,7 @@ using DevHive.Web.Models.Technology;
using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;
+using System;
using System.Threading.Tasks;
namespace DevHive.Web.Tests
@@ -13,6 +14,7 @@ namespace DevHive.Web.Tests
[TestFixture]
public class TechnologyControllerTests
{
+ const string NAME = "Gosho Trapov";
private Mock<ITechnologyService> TechnologyServiceMock { get; set; }
private Mock<IMapper> MapperMock { get; set; }
private TechnologyController TechnologyController { get; set; }
@@ -28,16 +30,14 @@ namespace DevHive.Web.Tests
#region Create
[Test]
public void Create_ReturnsOkObjectResult_WhenTechnologyIsSuccessfullyCreated()
- {
- string name = "Gosho Trapov";
-
+ {
CreateTechnologyWebModel createTechnologyWebModel = new CreateTechnologyWebModel
{
- Name = name
+ Name = NAME
};
CreateTechnologyServiceModel createTechnologyServiceModel = new CreateTechnologyServiceModel
{
- Name = name
+ Name = NAME
};
this.MapperMock.Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())).Returns(createTechnologyServiceModel);
@@ -48,8 +48,135 @@ namespace DevHive.Web.Tests
Assert.IsInstanceOf<OkResult>(result);
}
+ [Test]
+ public void Create_ReturnsBadRequestObjectResult_WhenTechnologyIsNotCreatedSuccessfully()
+ {
+ CreateTechnologyWebModel createTechnologyWebModel = new CreateTechnologyWebModel
+ {
+ Name = NAME
+ };
+ CreateTechnologyServiceModel createTechnologyServiceModel = new CreateTechnologyServiceModel
+ {
+ Name = NAME
+ };
+
+ this.MapperMock.Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())).Returns(createTechnologyServiceModel);
+ this.TechnologyServiceMock.Setup(p => p.Create(It.IsAny<CreateTechnologyServiceModel>())).Returns(Task.FromResult(false));
+
+ IActionResult result = this.TechnologyController.Create(createTechnologyWebModel).Result;
+
+ Assert.IsInstanceOf<BadRequestObjectResult>(result);
+ }
+ #endregion
+
+ #region Read
+ [Test]
+ public void GetById_ReturnsTheThecnology_WhenItExists()
+ {
+ Guid id = new Guid();
+
+ CreateTechnologyServiceModel createTechnologyServiceModel = new CreateTechnologyServiceModel
+ {
+ Name = NAME
+ };
+ CreateTechnologyWebModel createTechnologyWebModel = new CreateTechnologyWebModel
+ {
+ Name = NAME
+ };
+
+ this.TechnologyServiceMock.Setup(p => p.GetTechnologyById(It.IsAny<Guid>())).Returns(Task.FromResult(createTechnologyServiceModel));
+ this.MapperMock.Setup(p => p.Map<CreateTechnologyWebModel>(It.IsAny<CreateTechnologyServiceModel>())).Returns(createTechnologyWebModel);
+
+ IActionResult result = this.TechnologyController.GetById(id).Result;
+
+ Assert.IsInstanceOf<OkObjectResult>(result);
+
+ OkObjectResult okObjectResult = result as OkObjectResult;
+ CreateTechnologyWebModel resultModel = okObjectResult.Value as Models.Technology.CreateTechnologyWebModel;
+
+ Assert.AreEqual(NAME, resultModel.Name);
+ }
+ #endregion
+
+ #region Update
+ [Test]
+ public void Update_ShouldReturnOkResult_WhenTechnologyIsUpdatedSuccessfully()
+ {
+ Guid id = new Guid();
+ UpdateTechnologyWebModel updateTechnologyWebModel = new UpdateTechnologyWebModel
+ {
+ Name = NAME
+ };
+ UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel
+ {
+ Name = NAME
+ };
+
+ this.TechnologyServiceMock.Setup(p => p.UpdateTechnology(It.IsAny<Guid>(), It.IsAny<UpdateTechnologyServiceModel>())).Returns(Task.FromResult(true));
+ this.MapperMock.Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())).Returns(updateTechnologyServiceModel);
+
+ IActionResult result = this.TechnologyController.Update(id, updateTechnologyWebModel).Result;
+
+ Assert.IsInstanceOf<OkResult>(result);
+ }
+
+ [Test]
+ public void Update_ShouldReturnOkResult_WhenTechnologyIsNotUpdatedSuccessfully ()
+ {
+ Guid id = new Guid();
+ string message = "Could not update Technology";
+ UpdateTechnologyWebModel updateTechnologyWebModel = new UpdateTechnologyWebModel
+ {
+ Name = NAME
+ };
+ UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel
+ {
+ Name = NAME
+ };
+
+ this.TechnologyServiceMock.Setup(p => p.UpdateTechnology(It.IsAny<Guid>(), It.IsAny<UpdateTechnologyServiceModel>())).Returns(Task.FromResult(false));
+ this.MapperMock.Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())).Returns(updateTechnologyServiceModel);
+
+ IActionResult result = this.TechnologyController.Update(id, updateTechnologyWebModel).Result;
+ Assert.IsInstanceOf<BadRequestObjectResult>(result);
+
+ BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult;
+ string resultModel = badRequestObjectResult.Value.ToString();
+ Assert.AreEqual(message, resultModel);
+ }
#endregion
+ #region Delete
+ [Test]
+ public void Delete_ReturnsOkResult_When_TechnologyIsDeletedSuccessfully()
+ {
+ Guid id = new Guid();
+
+ this.TechnologyServiceMock.Setup(p => p.DeleteTechnology(It.IsAny<Guid>())).Returns(Task.FromResult(true));
+
+ IActionResult result = this.TechnologyController.Delete(id).Result;
+
+ Assert.IsInstanceOf<OkResult>(result);
+ }
+
+ [Test]
+ public void Delet_ReturnsBadRequestObjectResult_WhenTechnologyIsNotDeletedSuccessfully()
+ {
+ string message = "Could not delete Technology";
+ Guid id = new Guid();
+
+ this.TechnologyServiceMock.Setup(p => p.DeleteTechnology(It.IsAny<Guid>())).Returns(Task.FromResult(false));
+
+ IActionResult result = this.TechnologyController.Delete(id).Result;
+
+ Assert.IsInstanceOf<BadRequestObjectResult>(result);
+
+ BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult;
+ string resultModel = badRequestObjectResult.Value.ToString();
+
+ Assert.AreEqual(message, resultModel);
+ }
+ #endregion
}
}