Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • miburgos/spring2021-sellpoint
  • tdt4140-group-58/spring2021-sellpoint
2 results
Show changes
Commits on Source (84)
Showing
with 2256 additions and 19941 deletions
image: node:14.16.0-alpine
stages:
- install
- build_and_test
variables:
JWT_KEY: "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEA7HvNnIKwIVwmasKlLNOSsIvsc/k+3hHlDVPB0Sq9LKdvwDTKIUHgt8mTM33cpY2B5XPm7VYhhXeC5o4Z/Fdq7fpHmwW1vEYFM18/S7AGncbIOhCFtdM1m0KBZmPHmUVmtRZ6T4fPdwyiJyc37EC7bcJyhXT1+V/RjZKDgUFQZ4Rgv6f90Gh4KEH7G0UgUSyDQNRgej74YUqtx2n5t5pPVqF6IAsDtuUm5KyVqKhjyVYWOV8AV/tiI83W3C2EzROXsUuiFUK3mlyhCErTNwiRSs9Zz304mwj6KAFYuj2XTMNhMbcQEA2OYwGxUMT4/AtEDiQ01b9SDr1M42aHPzw1KVFU6pwTKQch5PqHEydSj1RZc160XOVFIy7uZIejlxAkkT7aSYH6WNW32W74Hzdj2KfV+6asm1K4Z+Gw+YF9esGSL6fKi/EqCzDYNhPofa31fxu5QNMojlaLmcd3YGBTqFWv37jkK0ItPqIoWYpwB994Ia93uvD0YGdZZd1pLlZendsQovbZdZt1rY4gX8RVr2t9QDc0U1UlOfYKXiGbpTb9Up81DQcRN2yQ4r1clzKIaa77iEGe2u1JCtpMvZP/6U6Wc609wMxb+YDTjj2n+PT5Uvfk3wKLY92UKPkvASa5qEcJLgC0HoziytUqCMgj2mCJ9Ufodee/RvhyX8p6hFcCAwEAAQKCAgABX/LhDw1xFDUQ99K4AP7gUd/tCKzp45a+ThdZXPM+mDaZGnV2XgitaNmXOgsXV0xDBjp4dMC7Iue66WaTRfrUKVlTOGRHFO0g0FPw37QaJLazR7dPSEwOfAN+kePuKy4erWPGpJZ7LbKfoaW8lN/CEphR2E05xunT9d8X2EZcY7OgSZy7InBYY5HPJca7UhPtQ47QBx+wiRubhi0yeQ4vBb9NAmNiI9DnEW3JTjlOSVbVvIym9koqxR6TpxwXiR6fpD3r+7hPiGxTPQQfVMhrc+skvqielh3GL8PRSVxg0N32yQap4TpLrw3V119PL82Ue3dnKraSaV1SER2GK5ZvhTH8g0vme8d7qTBC8rVmNOc5Rtg2t+RMYGAVlJsKc4lnGyDwnH6a+pfFAqCD62XefqkvWcUvgp7NN1yvUO9DgVxRoPseuWarevU8GbRTdYY8jVs1c1hVEijkjLliC5PwgzS+8Q4/2STFF/Zbty0cAs/FskK+n/zZPnTMm9uEO21FKHt1DKTcitIKCsmcSs6XwWFlo19Js2fc3qDV8IKjzMAxqYngWAdcS9SIKcjCOg/q9Q4pHXn9+WYABOJ5bpsCFyL/ss7spBDtIDaPv69FUPOIKnzcBoM/Esxd9ETukazs8SvDEqOM3AOaa233f07XmfpGbACF/rg8Vi8QYIHNeQKCAQEA+zxhlU8PInZS39xDRAA3lIpyo5e590CJJE45AFs1aATSfeIpwe4X+w3F/ddq/pa8mVvG2qBHiuSsHvcbvLMcrmJXDJi7Xv3QpbNxKyXwvAJEzEqLWs/dQ049GwE5ifhj1ET45BaXq8TiPuAmRTN6nEvaTZtKtJ+vHwG6wz4izJwCbbkeKXWuIdt7RWuVEMDz0B4qpODAhWZ/GcvVzPNFKDOti55REDVDkMwKDTpghyFrGEz5AwiDLrXTRte8/vFIrWceR8Gu1Fl0x5i5Vpc+HlXp9wnbgJ6VtCgy0bpSbWrrI4M5h+LgHwoI5p9SwSvHqEQGg305xULR7tFW4OnfTQKCAQEA8PfOtzPJeoep14cb1aZjo/iKbYMPtZDaF8EfpG34cyQERqZTMiDDvpzngDqKnxMPmx99ZOJ64dJ/2aM8w7qeKM5s5fIuSgNST4jYb4dP5rtoRi+ttPb7/8dMzhpBEX7RgpZ6HT3Z3+cxkKswOUJ5KHElF6dvElqVh3k4b2rRHpaKH6h2n8Jk6sPykYGsL0pxttFMJt6cbJg+5dldGHfJ9UJP7EF62ipZMIu87VSZj8GwqhaXjSEgYooznzhRKJU3Cp2jd+E6SkZAxDDHTIL0eVgTqCVAZt2hHQgDKjhRl2EjNIUeiIZTjR2saJImuXh4TNl8Wvb+07eMRdURQ3soMwKCAQEA8Zk0QjAf3sUolywq54bJ7CdjbNxoslO2TlqGyEftyNTyUqod++dp2jx5jM0OkDsGrPer9+wwQGn8914spz8bMrtQ+MUbUpMEhYRVLOg0Y6hO3qJE9pR0M9hWPNTlrz9VSPj22+i5/C2+YDaPtvZ1Tz5GdWED60SwVEIGhuEG9/qC/18ImVgYl2iIV87IL69OB3IrEhksKlzRl04EkS3T8IAZwO0DCXIfQmxksinUT76cTWcgA4G5HT8vKK4oioYfnNlQLjMcXAqWmabNfG6/Bs8Zyj4JftuCHRiXHkZuI9iwvJrhxUIwwsiUnKrUGv1t5+ta1DGlT4Qde+qGAI4G9QKCAQB2LyQsyHKF7SnI8pQdSnlOP6UtJv26cGv3AJpTXDhhyIVsXUEqsdUvlOcaBhxc/s1FqMEojMf2H/biuGQiH1JY8z2H3isD0/M33QdCVBxFLx9qZRIOUqCwB4DNCSVmvktlgAYud1p3rX2q1btWKhsgUbX+jvk0CEeUZUjfpIUiOnVOJ6Leoko6tpYu9WmM3Dc/khGGcuJHhLg8pUo6qO3do4NtfYQSoniSDcdAo6CZWEccdO4potiuW9gLvGRZwi13wG8fadDcAu7bCo2FIiljpmYlrveSPBs8EOT0fSDPXS4WIUAjd7Zc3PPlaeEiUEq4bmbGeqPrFPfwfBr2fN3vAoIBAQDPFqHAyfiHI4kgCaC1imV19Rta8X7bYcS9nqBnbhaIKeovrJPmnhF841weINieGqeX8yy4lltLmo0SOKlO4lWxcz9N9AgUS8U/ED4SXNuuuDmb0BlZDIBPdZbpXMzSspAm3yt23ZQ19Gk54yC0K1ONpc5tXtYPbwXKO6VrzRMLs0SWmbLPhzGrbkwyIi1GMPBG+U6GJCyb47i41LESRGvWmpLmVJCEGjMy8AE+1KXADVv8kPv6Od43qpTK5gNJxaBpSay1o1vPH6kpGcL0riuJloIkqHEKrat840iQRDi6dMfKxA+vOE58XpwOIlrV2flYh0QZF3UImk3IfWQMeiqr\n-----END RSA PRIVATE KEY-----"
cache:
paths:
- node_modules/
- client/node_modules/
- server/node_modules/
install:
stage: install
script:
- npm --silent install
- cd server && npm --silent install
- cd ../client && npm --silent install
only:
changes:
- package-lock.json
refs:
- master
- merge_requests
build_and_test:
stage: build_and_test
script:
- npm build
after_script:
- cd server && npm test
only:
refs:
- master
- merge_requests
\ No newline at end of file
This diff is collapsed.
......@@ -20,16 +20,16 @@
"@angular/platform-browser-dynamic": "~11.1.1",
"@angular/router": "~11.1.1",
"@auth0/angular-jwt": "^5.0.2",
"rxjs": "~6.6.0",
"rxjs": "^6.6.6",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
"zone.js": "^0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1101.2",
"@angular/cli": "~11.1.2",
"@angular-devkit/build-angular": "^0.1101.4",
"@angular/cli": "^11.1.4",
"@angular/compiler-cli": "~11.1.1",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"@types/jasmine": "^3.6.6",
"@types/node": "^12.20.5",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
......@@ -41,6 +41,6 @@
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.1.2"
"typescript": "^4.1.5"
}
}
......@@ -6,16 +6,26 @@ import { PostListComponent } from './posts/post-list/post-list.component';
import { UserRegistrationFormComponent } from './users/user-registration-form/user-registration-form.component';
import { UserLoginFormComponent } from './users/user-login-form/user-login-form.component';
import { UserProfileComponent } from './users/user-profile/user-profile.component';
import { UserGuestProfileComponent } from './users/user-guest-profile/user-guest-profile.component';
import { AdminComponent} from './categories/admin/admin.component';
import { HomeComponent } from './home/home.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'annonse/ny', component: PostFormComponent },
{ path: 'annonse/rediger/:id', component: PostFormComponent },
{ path: 'annonse', component: PostListComponent },
{ path: 'annonse/:id', component: PostDetailsComponent },
{ path: 'profile', component: UserProfileComponent },
{ path: 'user/:id', component: UserGuestProfileComponent },
{ path: 'profil', component: UserProfileComponent },
{ path: 'profil/rediger', component: UserRegistrationFormComponent},
{ path: 'register', component: UserRegistrationFormComponent },
{ path: 'login', component: UserLoginFormComponent }
{ path: 'login', component: UserLoginFormComponent },
{ path: 'admin/category', component: AdminComponent }
];
@NgModule({
......
<div class="navbar">
<a class="logo" href="/">SELLPOINT</a>
<nav>
<a href="/">/</a>
<a href="/annonse">/annonse</a>
<a href="/annonse/ny">/annonse/ny</a>
<a href="/register">/register</a>
<a href="/login">/login</a>
<a href="/profile">/profile</a>
</nav>
</div>
<div class="body">
<div class="navbar">
<span class="logo" (click)="navigate('/')">
<img src="../assets/img/SellPointLogo1.png" alt="logo">
</span>
<nav>
<span *ngIf="user.getIsAdmin" (click)="navigate('/admin/category')">Admin</span>
<span (click)="navigate('/annonse')">Annonser</span>
<span (click)="navigate('/annonse/ny')">Lag annonse</span>
<span *ngIf="!user.getUserId" (click)="navigate('/register')">Registrer</span>
<span *ngIf="!user.getUserId" (click)="navigate('/login')">Logg inn</span>
<span *ngIf="user.getUserId" (click)="navigate('/profil')">Profil</span>
<span *ngIf="user.getUserId" (click)="logout()">Logg ut</span>
</nav>
</div>
<div class="splash">
<div class="title"><h1>SELLPOINT</h1></div>
<div class="wave">
<div class="waveImg"></div>
</div>
</div>
<div class="wrapper">
<router-outlet></router-outlet>
</div>
\ No newline at end of file
<div class="wrapper">
<router-outlet></router-outlet>
</div>
</div>
<div class="footer">
<div class="logo">
<a style="display: block;" href="/"><img src="../assets/img/SellPointLogo2.png" alt="logo"></a>
</div>
<div class="info">
<div class="bedrift">
<h3>For bedrifter</h3>
<ul>
<li>Lorem ipsum</li>
<li>Lorem ipsum</li>
<li>Lorem ipsum</li>
</ul>
</div>
<div class="omoss">
<h3>Om SellPoint</h3>
<ul>
<li>Lorem ipsum</li>
<li>Lorem ipsum</li>
<li>Lorem ipsum</li>
</ul>
</div>
<div class="personvern">
<h3>Personvern</h3>
<ul>
<li>Lorem ipsum</li>
<li>Lorem ipsum</li>
<li>Lorem ipsum</li>
</ul>
</div>
</div>
</div>
div.body{
min-height: calc(100vh - 200px);
margin-bottom: 50px;
overflow-y: hidden;
}
div.navbar {
background-color: #666;
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
height: 62px;
font-family: 'Josefin Sans', sans-serif;
padding-top: 8px;
a.logo {
span.logo {
padding: 10px;
background-color: white;
width: 200px;
margin-left: 10px;
font-size: 40px;
font-weight: bold;
cursor: pointer;
img {
width: 170px;
transform: translateY(3px);
}
}
nav {
a{
span{
padding: 10px;
margin: 10px;
background-color: white;
cursor: pointer;
margin: 20px;
font-size: 25px;
background-color: #fff;
}
span:hover{
background-color: #eee;
}
}
}
div.splash {
position: absolute;
width: 100%;
height: 55vh;
background-color: springgreen;
background-image: url("../assets/img/backgroundImage.png");
background-size: cover;
display: flex;
justify-content: space-between;
overflow: hidden;
color: #ffffff;
font-family: 'Inter', sans-serif;
button {
color: #ffffff;
text-decoration: none;
}
div {
position: relative;
}
div.title {
position: absolute;
top: 50px;
left: 50%;
transform: translateX(-50%);
font-family: 'Inter', sans-serif;
font-size: 2em;
text-shadow: -2px 2px 3px #000000;
h1 {
letter-spacing: 10px;
}
}
img {
position: absolute;
height: 100%;
bottom: -10%;
object-fit: cover;
}
div.wave {
position: absolute;
left: 0;
bottom: 0;
position: absolute;
top: 250px;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
div.waveImg {
position: absolute;
bottom: 0;
width: 100%;
height: 110%;
background-image: url(../assets/img/wave2.svg);
background-repeat: no-repeat;
background-attachment: scroll;
background-position: 0% 0%;
background-size: 100vw auto;
}
}
}
div.wrapper {
padding: 10px;
position: relative;
top: 125px;
padding: 5%;
width: 100%;
height: 100%;
}
div.footer {
color: #ffffff;
background-color: rgb(80, 80, 80);
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'Josefin Sans', sans-serif;
clear: both;
height: 200px;
width: 100%;
div.info {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-right: 5%;
width: 400px;
ul {
padding-left: 20px;
}
}
div.logo {
margin-left: 5%;
}
}
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
......@@ -6,7 +7,8 @@ describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
RouterTestingModule,
HttpClientTestingModule
],
declarations: [
AppComponent
......
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { AuthService } from './authentication/auth.service';
import { User } from './models/user.model';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
export class AppComponent implements OnInit {
title: string = 'client';
user: User;
userSubscription: Subscription;
constructor(private authService: AuthService, private router: Router){}
ngOnInit() {
this.user = this.authService.getCurrentUser(false);
this.userSubscription = this.authService.userObservable.subscribe(user => {
this.user = user;
});
}
navigate(url) {
this.router.navigateByUrl(url);
}
logout() {
this.authService.logout();
}
}
......@@ -9,6 +9,8 @@ import { PostModule } from './posts/post.module';
import { UserModule } from './users/user.module';
import { AuthModule } from './authentication/auth.module';
import { SharedModule } from './shared/shared.module';
import { HomeComponent } from './home/home.component';
import { AdminComponent } from './categories/admin/admin.component';
export function tokenGetter() {
return localStorage.getItem("token");
......@@ -16,7 +18,9 @@ export function tokenGetter() {
@NgModule({
declarations: [
AppComponent
AppComponent,
HomeComponent,
AdminComponent,
],
imports: [
BrowserModule,
......
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../shared/shared.module';
......
import { HttpClient, HttpEvent, HttpInterceptor, HttpResponse } from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { User } from '../models/user.model';
import { tap, shareReplay } from 'rxjs/operators';
import { Router } from '@angular/router';
import { UserService } from '../users/user.service';
import { Subject } from 'rxjs';
interface IUserLogin {
username: string;
......@@ -16,7 +18,21 @@ export class AuthService {
loginUrl = "api/auth/login";
registrationUrl = "api/auth/register";
constructor(private http: HttpClient, private router: Router) { }
userObservable: Subject<User> = new Subject<User>();
constructor(private http: HttpClient, private router: Router, private userService: UserService) { }
getCurrentUser(route=true): User{
// Check for token expiration
if (this.checkTokenExpiration(route)) { // redirects to "/" if token is expired
// Get user data from JWT token
const token = localStorage.getItem('token');
const user_data = JSON.parse(atob(token.split(".")[1])).data[0];
return new User(user_data);
}
return new User();
}
/**
* Logins an user, if given correct combination of username and password.
......@@ -41,19 +57,21 @@ export class AuthService {
private login_user(body: IUserLogin) {
// Pipes output to setSession function if a valid user is returned
return this.http.post(this.loginUrl, body).pipe(
tap(res =>this.setSession(res)),
shareReplay());
tap(res =>this.setSession(res)),
shareReplay()
);
}
// Set authentication token on localStorage if a valid user is received
private setSession(authResult) {
console.log(authResult);
localStorage.setItem('token', authResult.token);
this.userObservable.next(this.getCurrentUser());
}
/**
* Checks validity of token, redirects to homepage and removes it if it is expired
*/
checkTokenExpiration() {
checkTokenExpiration(route) {
const token = localStorage.getItem("token");
if (token) {
const {iat, exp} = JSON.parse(atob(token?.split(".")[1]));
......@@ -64,26 +82,31 @@ export class AuthService {
// Expired token
if (now < issued || now >= expires) {
this.logout();
this.router.navigateByUrl("/");
if (route) {
this.router.navigate(["/login"], {replaceUrl: true});
}
return false
}
return true;
}
}
this.router.navigateByUrl("/")
if (route) {
this.router.navigate(["/login"], {replaceUrl: true});
}
return false
}
/**
* Logout an user and redirects to the homepage
* Logout a user and redirects to the homepage
*/
logout() {
localStorage.removeItem("token");
this.router.navigateByUrl("/");
this.userObservable.next(new User());
}
/**
* Register an user, if not duplicate, add to database.
* Register a user, if not duplicate, add to database.
*/
registerUser(user: User): Promise<string> {
return new Promise<string>(
......
<div class="categoryForm">
<div class="cardWrapper">
<h3>Alle kategorier</h3>
<div>
<p *ngFor="let category of categories">{{category.getName}}</p>
</div>
</div>
<div class="cardWrapper">
<h3>Legg til kategori</h3>
<div>
<app-input [(inputModel)]="name" label="Kategorinavn" (blur)="checkForm()"></app-input>
<p class="status">{{statusMessage}}</p>
<app-button (click)="addCategory()" text="Legg til"></app-button>
</div>
</div>
</div>
:host > .categoryForm {
padding: 5%;
display:flex;
justify-content: center;
flex-direction: column;
align-items: center;
margin-bottom: 50px;
h3 {
font-family: 'Josefin Sans', sans-serif;
font-size: 1.5rem;
font-weight: 500;
padding: 10px;
}
& > div.cardWrapper {
color: #fff;
display: flex;
flex-direction: column;
background: linear-gradient(90deg, #14A35A 0%, #24e072 100%);
padding: 10px;
width: 40%;
min-width: 300px;
max-width: 400px;
margin: 10px;
}
}
div.cardWrapper > div {
color: #000;
display: flex;
flex-direction: column;
gap: 10px;
background-color: #fff;
box-shadow: inset 0px 4px 4px rgb(0 0 0 / 50%);
padding: 20px 10px;
}
p.status {
margin: 10px 0 5px 5px;
font-style: italic;
}
div.other {
font-family: 'Josefin Sans', sans-serif;
display: flex;
justify-content: space-between;
padding: 10px 0 5px 0;
& > a {
color: #000;
&:visited {
color: #000;
}
}
}
\ No newline at end of file
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminComponent } from './admin.component';
describe('AdminComponent', () => {
let component: AdminComponent;
let fixture: ComponentFixture<AdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminComponent ],
imports: [
HttpClientTestingModule,
RouterTestingModule.withRoutes([
{ path: 'admin/category', component: AdminComponent}
])
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from 'src/app/authentication/auth.service';
import { Category } from 'src/app/models/category.model';
import { User } from 'src/app/models/user.model';
import { PostService } from 'src/app/posts/post.service';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.scss']
})
export class AdminComponent implements OnInit {
user: User = new User();
categories: Array<Category> = [];
name: string = "";
statusMessage: string = "";
constructor(private postService: PostService, private authService: AuthService, private router: Router) { }
ngOnInit(): void {
// Restrict page load to logged in admin users only
this.user = this.authService.getCurrentUser();
if (!this.user.getIsAdmin) {
this.router.navigateByUrl("/");
}
this.loadCategories();
}
/**
* Validates the form
*/
checkForm(): boolean {
if (this.name == "") {
this.setStatusMessage("Kategorinavn kan ikke være tom");
return false;
}
this.setStatusMessage("");
return true;
}
/**
* Publishes and add category if given arguments are valid
*/
addCategory() {
if (this.checkForm()) {
const category: Category = new Category(
{
id: 0,
name: this.name,
}
);
// Adds user to database and redirects to the homepage afterwards
this.postService.addCategory(category).then(status => {
console.log("Category was with name: " + this.name+ " was added!");
this.loadCategories();
this.categories.push(category);
this.name = ""; // Reset input
}).catch(error => {
console.log("Error adding category: " + error);
});
}
}
/*
* Get the categories from database
*/
loadCategories() {
// Gets all categories from database and displays them
this.postService.getAllCategories().then(categories => {
console.log(categories);
this.categories = categories;
}).catch(error => {
console.log(error);
});
}
/**
* Sets the status message for user feedback on form submit
*/
setStatusMessage(message: string) {
this.statusMessage = message;
}
}
<div class="home">
<div class="categoryWrapper">
<p>Kategorier</p>
<div class="whiteBox"></div>
</div>
<div class="cta">
<h2>Lorem Ipsum</h2>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
<div class="buttons">
<button (click)="navigate('/register')" class="btn">Registrer deg!</button>
<button (click)="navigate('/')" class="btn pink">Les mer</button>
</div>
</div>
</div>
<div class="postContainer">
<h2>Annonser i nærheten av deg</h2>
<p *ngIf="this.user.getUserId == 0">Logg inn for å se annonsene...</p>
<p *ngIf="this.user.getUserId != 0 && allPosts.length == 0">Ingen annonser i nærheten av deg...</p>
<app-post-thumbnail *ngFor="let post of allPosts" [post]="post"></app-post-thumbnail>
</div>
\ No newline at end of file
div.home {
position: relative;
width: 100%;
height: 600px;
background-size: cover;
display: flex;
justify-content: space-between;
overflow: hidden;
color: #ffffff;
font-family: 'Inter', sans-serif;
button {
color: #ffffff;
text-decoration: none;
}
div {
position: relative;
}
div.categoryWrapper {
opacity: 0; /* hided as it is not used */
padding: 10px;
margin-left: 5%;
background: linear-gradient(90deg, #14A35A 0%, #24e072 100%);
width: 40%;
height: 300px;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.5);
p {
padding-left: 10px;
padding-bottom: 5px;
margin-bottom: 5px;
border-bottom: white solid 1px;
width: 105px;
}
div.whiteBox {
width: 100%;
height: 250px;
background-color: #ffffff;
box-shadow: inset 0px 4px 4px rgba(0, 0, 0, 0.5);
}
}
div.cta {
color: #030303;
text-align: right;
margin-right: 5%;
margin-top: 60px;
width: 400px;
z-index: 2;
div.buttons {
display: flex;
text-align: center;
justify-content: flex-end;
}
button.btn {
background: #13D05E;
width: 200px;
margin: 10px 0 10px 10px;
padding: 20px;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 4px 4px rgba(0, 0, 0, 0.25);
font-family: 'Josefin Sans', sans-serif;
font-size: 20px;
border: none;
cursor: pointer;
}
button.pink {
background: #FFA1A1;
}
}
}
div.postContainer{
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fill, 384px);
gap: 30px;
margin-bottom: 80px;
transform: translateY(-150px);
justify-content: center;
h2 {
font-family: 'Josefin Sans', sans-serif;
position: absolute;
top: -50px;
left: 50%;
transform: translateX(-50%);
}
p {
font-family: 'Josefin Sans', sans-serif;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
}
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../authentication/auth.service';
import { Post } from '../models/post.model';
import { User } from '../models/user.model';
import { PostService } from '../posts/post.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
allPosts: Array<Post> = [];
user: User = new User();
constructor(private postService: PostService, private authService: AuthService, private router: Router){}
ngOnInit(): void {
// Gets current user information
this.user = this.authService.getCurrentUser(false);
this.getPosts();
}
navigate(url) {
this.router.navigateByUrl(url);
}
getPosts() {
if (this.user.getUserId != 0) {
this.postService.getPostsByCategory(undefined, [this.user.getLocation], undefined, undefined, undefined).then(posts => {
this.allPosts = posts.filter((post: Post) => post.getStatus == 0); // Filter out closed post
}).catch(error => {
console.log(error);
});
}
}
}
import { defaultThrottleConfig } from "rxjs/internal/operators/throttle";
import { Deserializable } from "./deserializable.model";
import { Serializable } from "./serializable.model";
......@@ -6,10 +7,11 @@ export class Post implements Deserializable, Serializable {
private title: string;
private description: string;
private timestamp: Date;
private owner: string;
private owner: number;
private imageUrl: string;
private price: number;
private categoryid: number;
private status: number;
constructor(input: any = null) {
if (input) {
......@@ -23,6 +25,7 @@ export class Post implements Deserializable, Serializable {
this.imageUrl = null;
this.price = null;
this.categoryid = null;
this.status = 0;
}
}
......@@ -43,7 +46,8 @@ export class Post implements Deserializable, Serializable {
owner: this.owner,
imageUrl: this.imageUrl,
price: this.price,
categoryid: this.categoryid
categoryid: this.categoryid,
status: this.status
};
}
......@@ -83,7 +87,7 @@ export class Post implements Deserializable, Serializable {
return this.owner;
}
set setOwner(owner: string) {
set setOwner(owner: number) {
this.owner = owner;
}
......@@ -110,4 +114,22 @@ export class Post implements Deserializable, Serializable {
set setCategory(categoryid: number) {
this.categoryid = categoryid;
}
get getStatus() {
return this.status;
}
get getStatusInfo() {
if (this.status == 0) {
return "Open";
} else if (this.status == 2) {
return "Draft";
} else {
return "Closed";
}
}
set setStatus(status: number) {
this.status = status;
}
}
\ No newline at end of file
import { Deserializable } from "./deserializable.model";
import { Serializable } from "./serializable.model";
export class Review implements Deserializable, Serializable {
private id: number;
private userId: number;
private stars: number;
private comment: string;
constructor(input: any = null) {
if (input) {
this.deserialize(input);
} else {
this.id = 0;
this.userId = 0;
this.stars = 0;
this.comment = null;
}
}
deserialize(input: Object): this {
Object.assign(this, input);
return this;
}
serialize(): Object {
return {
id: this.id,
userId: this.userId,
stars: this.stars,
comment: this.comment,
};
}
get getId() {
return this.id;
}
set setId(id: number) {
this.id = id;
}
get getUserId() {
return this.userId;
}
set setUserId(userId: number) {
this.userId = userId;
}
get getStars() {
return this.stars;
}
set setStars(stars: number) {
this.stars = stars;
}
get getComment() {
return this.comment;
}
set setComment(comment: string) {
this.comment = comment;
}
}
\ No newline at end of file