diff --git a/client/src/app/posts/post-details/post-details.component.html b/client/src/app/posts/post-details/post-details.component.html index 2629f2e372adebe0496c0ddbe2417c87a5a3c444..bf5baa3fb03649bbb82a21fe3bf3b55dde1046bd 100644 --- a/client/src/app/posts/post-details/post-details.component.html +++ b/client/src/app/posts/post-details/post-details.component.html @@ -25,6 +25,8 @@ <app-button class="ownerButton" text="Rediger annonse" (click)="editPost()"></app-button> <app-button class="ownerButton" text="Slett annonse" (click)="deletePost()"></app-button> </div> + <app-button *ngIf="userId != post.getOwner && !isFavourited" text="Legg til som favoritt ♡" (click)="addFavourite()"></app-button> + <app-button *ngIf="userId != post.getOwner && isFavourited" text="Slett fra dine favoritter" (click)="removeFavourite()"></app-button> </div> </div> </div> diff --git a/client/src/app/posts/post-details/post-details.component.scss b/client/src/app/posts/post-details/post-details.component.scss index 35223ab590dfa1bc8d7c52be7d6eddf7c312a549..6c4e211d72acfeb2a30e9129b279f7d0dec5e44a 100644 --- a/client/src/app/posts/post-details/post-details.component.scss +++ b/client/src/app/posts/post-details/post-details.component.scss @@ -42,6 +42,7 @@ div.container{ margin-top: 30px; display: grid; gap: 15px; + margin-bottom: 15px; } hr{ @@ -64,6 +65,7 @@ div.container{ p#timestamp{ margin-top: 30px; font-size: 14pt; + margin-bottom: 10px; } } } diff --git a/client/src/app/posts/post-details/post-details.component.ts b/client/src/app/posts/post-details/post-details.component.ts index bd2d6051038aa7e4bc9e347354d7597e5eac0a57..f654a5a38693feaa507ffbe9c702b37ebaa5a466 100644 --- a/client/src/app/posts/post-details/post-details.component.ts +++ b/client/src/app/posts/post-details/post-details.component.ts @@ -18,7 +18,7 @@ export class PostDetailsComponent implements OnInit { user: User = new User(); isAdmin: number = 0; userId: number = 0; - + isFavourited: boolean = false; constructor(private postService: PostService, private activatedRoute: ActivatedRoute, private router: Router, private authService: AuthService, private userService: UserService) { } @@ -36,7 +36,10 @@ export class PostDetailsComponent implements OnInit { // Gets Post with id from database this.postService.getPost(id).then(post => { this.post = post; - + + // Checks for user favourite + this.checkFavourite(); + // Gets Post owner from database this.userService.getUser(this.post.getOwner).then(user => { this.owner = user; @@ -69,4 +72,47 @@ export class PostDetailsComponent implements OnInit { }); } } + + /** + * Add post to favourites in database + */ + addFavourite() { + // Check if user is not the owner of the post + if (this.userId != this.post.getOwner) { + this.postService.addFavourite(this.post.getId, this.userId).then(data => { + console.log("Successfully added post to favourites: " + this.post.getId); + this.isFavourited = true; + }).catch(error => { + console.log(error); + }); + } + } + /** + * Check if post is favourited in database + */ + checkFavourite() { + // Check if user is not the owner of the post + if (this.userId != this.post.getOwner) { + this.postService.getFavourite(this.post.getId, this.userId).then(data => { + console.log("Successfully received post from favourites: " + this.post.getId); + this.isFavourited = data; + }).catch(error => { + console.log(error); + }); + } + } + /** + * Remove post from favourites in database + */ + removeFavourite() { + // Check if user is not the owner of the post + if (this.userId != this.post.getOwner) { + this.postService.deleteFavourite(this.post.getId, this.userId).then(data => { + console.log("Successfully removed post from favourites: " + this.post.getId); + this.isFavourited = false; + }).catch(error => { + console.log(error); + }); + } + } } diff --git a/client/src/app/posts/post.service.ts b/client/src/app/posts/post.service.ts index 63ea70293f18426b7d0e13beb466bd2ec29b4b4f..33e9357d9f1e69649bd3fed95d62afd733631dde 100644 --- a/client/src/app/posts/post.service.ts +++ b/client/src/app/posts/post.service.ts @@ -10,6 +10,7 @@ export class PostService { postUrl = "api/post/"; categoryUrl = "api/category/"; + favouriteUrl = "api/post/favourite/"; categories: Array<Category>; @@ -266,4 +267,117 @@ export class PostService { private get_posts_by_user_id(userId: number) { return this.http.get(this.postUrl, {params: {userId: String(userId)}}); } + + /** + * Check favourite status in database by id. + */ + getFavourite(id: number, userId: number): Promise<any> { + return new Promise<any>( + (resolve, reject) => { + this.get_favourite(id, userId).subscribe((data: any) => { + try { + let favourited = false; + if (data?.data[0]?.favourited == 1) { + favourited = true; + } + resolve(favourited); + } catch (err: any) { + reject(err); + } + }, + (err: any) => { + console.log(err.message); + reject(err); + }); + } + ); + } + + private get_favourite(id: number, userId: number) { + return this.http.get(this.favouriteUrl + id + "/" + userId); + } + + /** + * Delete favourite in database by id. + */ + addFavourite(id: number, userId: number): Promise<any> { + return new Promise<any>( + (resolve, reject) => { + this.add_favourite(id, userId).subscribe((data: any) => { + try { + resolve(data); + } catch (err: any) { + reject(err); + } + }, + (err: any) => { + console.log(err.message); + reject(err); + }); + } + ); + } + + private add_favourite(id: number, userId: number) { + return this.http.post(this.favouriteUrl, {id: id, userId: userId}); + } + + /** + * Delete favourite in database by id. + */ + deleteFavourite(id: number, userId: number): Promise<any> { + return new Promise<any>( + (resolve, reject) => { + this.delete_favourite(id, userId).subscribe((data: any) => { + try { + resolve(data); + } catch (err: any) { + reject(err); + } + }, + (err: any) => { + console.log(err.message); + reject(err); + }); + } + ); + } + + private delete_favourite(id: number, userId: number) { + return this.http.delete(this.favouriteUrl + id + "/" + userId); + } + + /** + * Delete favourite in database by id. + */ + getFavouritedPosts(userId: number): Promise<any> { + return new Promise<any>( + (resolve, reject) => { + this.get_favourited_posts(userId).subscribe((data: any) => { + try { + let outputPosts = []; + for (let post of data.data) { + outputPosts.push(new Post(post)); + + if (!post.id || post.id == 0) { + reject("Could not deserialize Post"); + return; + } + } + resolve(outputPosts); + } catch (err: any) { + reject(err); + } + }, + (err: any) => { + console.log(err.message); + reject(err); + }); + } + ); + } + + private get_favourited_posts(userId: number) { + return this.http.get(this.favouriteUrl+userId); + } } diff --git a/client/src/app/users/user-profile/user-profile.component.html b/client/src/app/users/user-profile/user-profile.component.html index 7a86d7efb778090b51069d84d9bfa4c27c8b450a..f2e4ef40d355554388e5c37d3d626fbaf2b06d09 100644 --- a/client/src/app/users/user-profile/user-profile.component.html +++ b/client/src/app/users/user-profile/user-profile.component.html @@ -33,7 +33,7 @@ Fotball er livet ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </pre> </div> - <app-button (click)="placeholder()" text="Rediger profil" class="btn pink"></app-button> + <a href="/profil/rediger"><app-button text="Rediger profil" class="btn pink"></app-button></a> </div> </div> </div> @@ -43,6 +43,12 @@ Fotball er livet <!-- All users posts for now :) --> <app-post-thumbnail *ngFor="let post of allPosts" [post]="post"></app-post-thumbnail> </div> - <a href="#">Se flere annonser</a> + <br><br> + <h2>Mine favoritter</h2> + <div class="posts"> + <!-- All users posts for now :) --> + <app-post-thumbnail *ngFor="let post of favouritedPosts" [post]="post"></app-post-thumbnail> + </div> </div> + </div> diff --git a/client/src/app/users/user-profile/user-profile.component.ts b/client/src/app/users/user-profile/user-profile.component.ts index b19520376a434ac3e53d887dc709765ca0d11869..08126d0203a2340f11584e9b74f67a7835457cce 100644 --- a/client/src/app/users/user-profile/user-profile.component.ts +++ b/client/src/app/users/user-profile/user-profile.component.ts @@ -18,16 +18,16 @@ import { UserService } from '../user.service'; export class UserProfileComponent implements OnInit { allPosts: Array<Post> = []; + favouritedPosts: Array<Post> = []; user: User = new User(); constructor(private authService: AuthService, private userService: UserService, private postService: PostService, private router: Router) { } ngOnInit(): void { this.user = this.authService.getCurrentUser(); this.getPostsByUserId(); + this.getFavouritedPosts(); } - placeholder() { - console.log(":)"); - } + getPostsByUserId() { // Gets all posts from database, and displays them this.postService.getPostsByUserId(this.user.getUserId).then(posts => { @@ -37,6 +37,16 @@ export class UserProfileComponent implements OnInit { }); } + getFavouritedPosts() { + // Gets all favourited posts from database, and displays them + this.postService.getFavouritedPosts(this.user.getUserId).then(posts => { + this.favouritedPosts = posts; + console.log(posts); + }).catch(error => { + console.log(error); + }); + } + /** * Deletes user in database and navigates to login */ diff --git a/server/src/controllers/postController/index.ts b/server/src/controllers/postController/index.ts index f7290592939e3e3f3b6ad2e6566a14f34d0c06c5..bf94301c9ea473422a19a227306df9785256ca09 100644 --- a/server/src/controllers/postController/index.ts +++ b/server/src/controllers/postController/index.ts @@ -41,6 +41,25 @@ router.route("/").post(async (request: Request, response: Response) => { } }); +// Add favourite post by id and userId `/api/post/favourite/` +router.route("/favourite").post(authenticateToken, async (request: Request, response: Response) => { + const {id, userId} = request.body; + try { + if (id == undefined || userId == undefined) return response.status(500).send("Error"); + // Check for user duplicates + const duplicate_input = "SELECT * FROM postFavourite WHERE id=? AND userId=?;" + const user = await query(duplicate_input,[id, userId]); + const retrievedUserObj = Object.values(JSON.parse(JSON.stringify(user.data)))[0]; + if (retrievedUserObj) { + return response.status(403).send("Already favourited!"); + } + const input = `INSERT INTO postFavourite (id, userId) VALUES (?, ?);`; + response.status(200).json(await query(input, [id, userId])); + } catch (error) { + response.status(400).send("Bad Request"); + } +}); + /* ============================= READ ============================= */ // Get all posts `/api/post/?categoryid=:categoryid&userId=:userId` router.route("/").get(async (request: Request, response: Response) => { @@ -75,6 +94,31 @@ router.route("/:id").get(async (request: Request, response: Response) => { } }); +// Get status of post is favoritted by id `/api/post/favourite/:id/:userId` +router.route("/favourite/:id/:userId").get(authenticateToken, async (request: Request, response: Response) => { + const id: string = request.params.id as string; + const userId: string = request.params.userId as string; + try { + if (id == undefined || userId == undefined) return response.status(500).send("Error"); + const input = `SELECT COUNT(*) as favourited FROM postFavourite WHERE id = ? AND userId = ?;`; + response.status(200).json(await query(input, [parseInt(id), parseInt(userId)])); + } catch (error) { + response.status(400).send("Bad Request"); + } +}); + +// Get favourited post of userid `/api/post/favourite/:userId` +router.route("/favourite/:userId").get(authenticateToken, async (request: Request, response: Response) => { + const userId: string = request.params.userId as string; + try { + if (userId == undefined) return response.status(500).send("Error"); + const input = `SELECT P.id, P.title, P.description, P.price, P.timestamp, P.owner, P.categoryId, P.imageUrl, P.status FROM postFavourite as PF INNER JOIN post as P ON P.id = PF.id WHERE PF.userId = ?;`; + response.status(200).json(await query(input, [parseInt(userId)])); + } catch (error) { + response.status(400).send("Bad Request"); + } +}); + /* ============================= UPDATE ============================= */ // Edit post with id `/api/post/:id` router.route("/:id").put(authenticateToken, async (request: Request, response: Response) => { @@ -120,4 +164,17 @@ router.route("/:id").delete(authenticateToken, async (request: Request, response } }); +// Remove favourites with id and userId `/api/post/favourite/:id/:userId` +router.route("/favourite/:id/:userId").delete(authenticateToken, async (request: Request, response: Response) => { + const id: string = request.params.id as string; + const userId: string = request.params.userId as string; + try { + if (id == undefined || userId == undefined) return response.status(500).send("Error"); + response + .status(200) + .json(await query("DELETE FROM postFavourite WHERE id=? AND userId=?;", [parseInt(id), parseInt(userId)])); + } catch (error) { + response.status(400).send("Bad Request"); + } +}); export default router; \ No newline at end of file