diff --git a/src/api.ts b/src/api.ts index aea22d11..e16731d9 100644 --- a/src/api.ts +++ b/src/api.ts @@ -41,5 +41,14 @@ export const API = { WebPushSubscribe: 'api/user-me/subscribe-wp', UserTags: 'api/user-tags', - UserTagsReach: 'api/user-tags/reach' + UserTagsReach: 'api/user-tags/reach', + + Complaints: 'api/venter/complaints', + MyComplaints: 'api/venter/complaints?filter=me', + Complaint: 'api/venter/complaints/{complaintId}', + UpVote: 'api/venter/complaints/{complaintId}/upvote{?action}', + CommentPost: 'api/venter/complaints/{complaintId}/comments', + CommentEdit: 'api/venter/comments/{commentId}', + TagCategories: 'api/venter/tags', + SubscribeToComplaint: 'api/venter/complaints/{complaintId}/subscribe{?action}' }; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c467e23c..918031ba 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -58,6 +58,13 @@ import { RedirComponent } from './redir/redir.component'; import { UpdateRoleComponent } from './update-role/update-role.component'; import { CardComponent } from './card/card.component'; +import { ComplaintsHomeComponent } from './venter/complaints/complaints-home.component'; +import { FileComplaintComponent } from './venter/file/file-complaint.component'; +import { DetailedComplaintComponent } from './venter/complaint/detailed-complaint.component'; +import { EditCommentComponent } from './venter/edit-comment/edit-comment.component'; +import { VenterDataService } from './venter/venter-data.service'; +import { ComplaintCardComponent } from './venter/complaint-card/complaint-card.component'; + @NgModule({ declarations: [ AppComponent, @@ -91,7 +98,12 @@ import { CardComponent } from './card/card.component'; NotifyCardComponent, RedirComponent, UpdateRoleComponent, - CardComponent + CardComponent, + ComplaintsHomeComponent, + FileComplaintComponent, + DetailedComplaintComponent, + EditCommentComponent, + ComplaintCardComponent, ], imports: [ BrowserModule, @@ -131,6 +143,11 @@ import { CardComponent } from './card/card.component'; { path: 'blog/:blog', component: BlogComponent, data: { state: 'base' }, canActivate: [LoginActivate] }, { path: 'login', component: LoginComponent, data: { state: 'base' } }, + + { path: 'venter/complaints', component: ComplaintsHomeComponent, data: { state: 'base' }, canActivate: [LoginActivate] }, + { path: 'venter/file', component: FileComplaintComponent, data: { state: 'base' }, canActivate: [LoginActivate] }, + { path: 'venter/complaint/:id', component: DetailedComplaintComponent, data: { state: 'base' }, canActivate: [LoginActivate] }, + { path: 'feedback', component: RedirComponent, data: { state: 'base' } }, { path: 'android', component: RedirComponent, data: { state: 'base' } }, { path: '**', redirectTo: 'feed' }, @@ -141,10 +158,12 @@ import { CardComponent } from './card/card.component'; LayoutModule, ], entryComponents: [ - NotifyCardComponent + NotifyCardComponent, + EditCommentComponent ], providers: [ DataService, + VenterDataService, { provide: RouteReuseStrategy, useClass: CustomReuseStrategy }, LoginActivate, ], diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts index f05f082f..949fa880 100644 --- a/src/app/interfaces.ts +++ b/src/app/interfaces.ts @@ -162,6 +162,52 @@ export interface IUserTagCategory { tags: IUserTag[]; } +export interface IComplaintTag { + id: string; + tag_uri: string; +} + +export interface IComplaintComment { + id: string; + time: string; + text: string; + commented_by: IUserProfile; + reported_date: string; +} + +export interface IComplaint { + id: string; + created_by: IUserProfile; + description: string; + suggestions: string; + location_details: string; + report_date: string; + reported_date: string; + status: string; + status_color: string; + latitude: number; + longitude: number; + location_description: string; + tags: IComplaintTag; + users_up_voted: IUserProfile[]; + images: string[]; + comments: IComplaintComment[]; + vote_count: number; + is_subscribed: number; + upvoted: boolean; +} + +export interface IComplaintPost { + description: string; + suggestions: string; + location_details: string; + latitude: number; + longitude: number; + location_description: string; + tags: string[]; + images: string[]; +} + export interface IInstituteRole { id: string; name: string; diff --git a/src/app/material-angular.module.ts b/src/app/material-angular.module.ts index 249904cc..c994fa64 100644 --- a/src/app/material-angular.module.ts +++ b/src/app/material-angular.module.ts @@ -17,6 +17,10 @@ import { MatExpansionModule } from '@angular/material/expansion'; import { MatBadgeModule } from '@angular/material/badge'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatRippleModule } from '@angular/material/core'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatDialogModule } from '@angular/material/dialog'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ReactiveFormsModule } from '@angular/forms'; @@ -46,7 +50,11 @@ import { MatNativeDateModule } from '@angular/material/core'; MatExpansionModule, MatBadgeModule, MatSidenavModule, - MatProgressSpinnerModule, + MatTabsModule, + MatFormFieldModule, + MatDividerModule, + MatDialogModule, + MatProgressSpinnerModule ], exports: [ ReactiveFormsModule, @@ -70,7 +78,11 @@ import { MatNativeDateModule } from '@angular/material/core'; MatExpansionModule, MatBadgeModule, MatSidenavModule, - MatProgressSpinnerModule, + MatTabsModule, + MatFormFieldModule, + MatDividerModule, + MatDialogModule, + MatProgressSpinnerModule ], }) export class MyMaterialClass { } diff --git a/src/app/navmenu/navmenu.component.html b/src/app/navmenu/navmenu.component.html index e0b5d2f4..5e6a0c1c 100644 --- a/src/app/navmenu/navmenu.component.html +++ b/src/app/navmenu/navmenu.component.html @@ -58,6 +58,12 @@ map   Map + + announcement   Complaints/ Suggestions + + diff --git a/src/app/venter/complaint-card/complaint-card.component.css b/src/app/venter/complaint-card/complaint-card.component.css new file mode 100644 index 00000000..f79c4a60 --- /dev/null +++ b/src/app/venter/complaint-card/complaint-card.component.css @@ -0,0 +1,48 @@ +.complaint-card { + text-decoration: none; +} + +.complaint-divider { + padding: 1%; +} + +.complaint-card-content { + font-size: medium; + padding-top: 3%; +} + +.complaint-description { + white-space: pre-wrap; +} + +.complaint-location-details { + padding-top: 2%; +} + +.card { + margin: 5%; + width: 80%; +} + +.footer { + position: relative; + text-align: center; +} + +.footer-button-container { + display: inline; +} + +.complaint-upvote-active { + color: #536dfe; +} + +.status { + float: right; + background-color: red; + border-width: medium; + color: white; + padding: 0 2.5%; + font-weight: bold; + text-transform: uppercase; +} diff --git a/src/app/venter/complaint-card/complaint-card.component.html b/src/app/venter/complaint-card/complaint-card.component.html new file mode 100644 index 00000000..36eeb9ee --- /dev/null +++ b/src/app/venter/complaint-card/complaint-card.component.html @@ -0,0 +1,73 @@ +
+ +
+ {{ complaint.status }} +
+ + + {{ complaint.created_by.name }} + +
{{ complaint.reported_date }}
+
{{ complaint.location_description }}
+
+
+ + + + +
+
+
Complaint:
+
{{ complaint.description }}
+
+
+
Suggestions:
+
{{ complaint.suggestions }}
+
+
+
Location Details:
+
{{ complaint.location_details }}
+
+
+
+
+ +
+ + + +
+
diff --git a/src/app/venter/complaint-card/complaint-card.component.ts b/src/app/venter/complaint-card/complaint-card.component.ts new file mode 100644 index 00000000..2048990f --- /dev/null +++ b/src/app/venter/complaint-card/complaint-card.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { IComplaint } from '../../interfaces'; +import { DataService } from '../../data.service'; +import { API } from '../../../api'; +import { VenterDataService } from '../venter-data.service'; + +@Component({ + selector: 'app-complaint-card', + templateUrl: './complaint-card.component.html', + styleUrls: ['./complaint-card.component.css'] +}) +export class ComplaintCardComponent implements OnInit { + + @Input() complaint: IComplaint; + constructor( + public dataService: DataService, + public venterDataService: VenterDataService, + ) { } + + ngOnInit() { + } + + refresh() { + /* Get complaint from server*/ + this.dataService.FireGET(API.Complaint, { complaintId: this.complaint.id }).subscribe(result => { + this.complaint = result; + this.venterDataService.getDetailedComplaintInfo(this.complaint); + }); + } + + onClickEvent(event) { + event.stopPropagation(); + } + + subscribeToComplaint(complaint_subscribed: number, complaintId: string) { + this.venterDataService.subscribeToComplaint(complaint_subscribed, complaintId); + this.refresh(); + } + + upVoteComplaint(has_upvoted: boolean, complaintId: string) { + this.venterDataService.upVoteComplaint(has_upvoted, complaintId); + this.refresh(); + } +} diff --git a/src/app/venter/complaint/detailed-complaint.component.css b/src/app/venter/complaint/detailed-complaint.component.css new file mode 100644 index 00000000..fe29e945 --- /dev/null +++ b/src/app/venter/complaint/detailed-complaint.component.css @@ -0,0 +1,180 @@ +.container { + margin: 0 175px; + padding: 0 5%; +} + +.placeholder-image { + background-size: cover; +} + +/* Slideshow container */ + +.slideshow-container { + max-width: 500px; + max-height: 500px; + position: relative; + margin: auto; +} + +.display-image { + max-height: 400px; + max-width: 400px; +} + +.image-dot { + text-align: center; +} + +.slideshow-container img { + display: none; +} + +.slideshow-container img.image-active { + display: block; + width: 100%; +} + +/* The dots/bullets/indicators */ + +.dot { + cursor: pointer; + height: 15px; + width: 15px; + margin: 0 2px; + background-color: #bbb; + border-radius: 50%; + display: inline-block; + transition: background-color 0.6s ease; +} + +.active, +.dot:hover { + background-color: #717171; +} + +/* Fading animation */ + +.fade { + -webkit-animation-name: fade; + -webkit-animation-duration: 1.5s; + animation-name: fade; + animation-duration: 1.5s; +} + +.complaint-card { + padding: 2.5%; + text-decoration: none; +} + +.card { + width: 80%; + margin: 5%; +} + +.complaint-creator { + color: black; + font-weight: bold; + font-size: medium; +} + +.complaint-creator-name { + padding-left: 2%; +} + +.complaint-creator-image { + border-radius: 50%; + width: 50px; + height: 50px; + float: left; + padding: 0 2%; +} + +.complaint-time-location { + color: darkgrey; + font-size: small; + padding-left: 2%; +} + +.complaint-status { + float: right; + background-color: red; + border-width: medium; + color: white; + padding: 0 2.5%; + font-weight: bold; + text-transform: uppercase; +} + +.complaint-divider { + padding: 2.5%; + color: royalblue; +} + +.subscribe-button { + float: right; +} + +.complaint-card-content { + font-size: medium; + padding-bottom: 3%; +} + +.complaint-description { + white-space: pre-wrap; +} + +.complaint-tag { + padding: 1% 0; + background-color: yellowgreen +} + +.complaint-comment-name { + background-size: cover; +} + +.complaint-comment-buttons { + float: right; +} + +.upVote-button { + color: black; + background-color: yellow; + padding: 10px 50px; + font-weight: bold; + text-decoration: none; +} + +@media (max-width: 970px) { + .container { + margin: 0%; + padding: 2%; + } +} + +@media (max-width: 520px) { + .container { + margin: 0%; + padding: 0%; + } + img { + max-width: 100%; + max-height: 20%; + } + .slideshow-container { + max-width: 100%; + max-height: 20%; + position: relative; + margin: auto; + } +} + +@media (max-width: 470px) { + .subscribe-button { + float: right; + padding: 0%; + margin: 0%; + } + .complaint-content { + padding-top: 5%; + } +} diff --git a/src/app/venter/complaint/detailed-complaint.component.html b/src/app/venter/complaint/detailed-complaint.component.html new file mode 100644 index 00000000..af7a7b8f --- /dev/null +++ b/src/app/venter/complaint/detailed-complaint.component.html @@ -0,0 +1,126 @@ +
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+ {{ detailedComplaint.created_by.name }} +
+
+
{{ detailedComplaint.reported_date }}
+
{{ detailedComplaint.location_description }}
+
+
+ {{ detailedComplaint.status }} +
+
+
+ +
+
+
+ +
+
Complaint: +
{{ detailedComplaint.description }}
+
+
+
+
Suggestions: +
{{ detailedComplaint.suggestions }}
+
+
+
+
+
+
Location Details: +
{{ detailedComplaint.location_details }}
+
+
+
+ Tags +
+ + + {{ tag }} + + +
+
+
+ Comments({{ detailedComplaint.comments.length }}) +
+ + + + + {{ complaintComment.commented_by.name }} + + {{ complaintComment.reported_date }} + + + + + {{ complaintComment.text }} + + +
+ + + + +
+ UpVotes({{ detailedComplaint.users_up_voted.length }}) +
+ + + + {{ complaintupVote.name }} + + +
+
+ +
+ UPVOTE + +
diff --git a/src/app/venter/complaint/detailed-complaint.component.ts b/src/app/venter/complaint/detailed-complaint.component.ts new file mode 100644 index 00000000..f1d9a94d --- /dev/null +++ b/src/app/venter/complaint/detailed-complaint.component.ts @@ -0,0 +1,124 @@ +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute, Params } from '@angular/router'; +import { IComplaint, IComplaintComment } from '../../interfaces'; +import { DataService } from '../../data.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { API } from '../../../api'; +import { MatDialog } from '@angular/material'; +import { EditCommentComponent } from '../edit-comment/edit-comment.component'; +import { VenterDataService } from '../venter-data.service'; + +@Component({ + selector: 'app-detailed-complaint', + templateUrl: './detailed-complaint.component.html', + styleUrls: ['./detailed-complaint.component.css'] +}) +export class DetailedComplaintComponent implements OnInit { + detailedComplaint: IComplaint; + public complaintId: string; + public comment: IComplaintComment; + public userid: string = null; + public SubscribeToComplaint: string; + public selectedindex = 0; + public editedComment: IComplaintComment; + + constructor(public dataService: DataService, + public router: Router, + public activatedRoute: ActivatedRoute, + public snackBar: MatSnackBar, + public editCommentDialog: MatDialog, + public venterDataService: VenterDataService, + ) { } + + ngOnInit() { + this.comment = {} as IComplaintComment; + this.editedComment = {} as IComplaintComment; + /* Get profile if the user is logged in */ + if (this.dataService.isLoggedIn()) { + this.getUser(); + } + this.dataService.setTitle('Complaints/Suggestions'); + this.activatedRoute.params.subscribe((params: Params) => { + this.complaintId = params['id']; + this.refresh(); + }); + } + + getUser() { + this.dataService.GetFillCurrentUser().subscribe(user => { + this.userid = user.id; + }); + } + + /** Loads the data */ + refresh() { + if (this.complaintId) { + this.dataService.FireGET(API.Complaint, + { complaintId: this.complaintId }).subscribe(result => { + this.detailedComplaint = result; + this.venterDataService.getDetailedComplaintInfo(this.detailedComplaint); + }, (error) => { + if (error.status === 404) { + alert('Complaint not found'); + } + }); + } + } + + selectImage(index: number) { + this.selectedindex = index; + } + + postComment() { + this.dataService.FirePOST(API.CommentPost, + this.comment, { complaintId: this.detailedComplaint.id }).subscribe(() => { + this.refresh(); + this.comment.text = ''; + }); + } + + onClickEvent(event) { + event.stopPropagation(); + } + + upVoteComplaint(has_upvoted: boolean) { + this.venterDataService.upVoteComplaint(has_upvoted, this.detailedComplaint.id); + this.refresh(); + } + + subscribeToComplaint(complaint_subscribed: number) { + this.venterDataService.subscribeToComplaint(complaint_subscribed, this.detailedComplaint.id); + this.refresh(); + } + + deleteComment(commentId: string) { + this.dataService.FireDELETE(API.CommentEdit, + { commentId: commentId }).subscribe(() => { + this.refresh(); + this.venterDataService.getSnackbar('Your comment has been deleted', null); + }, (error) => { + this.venterDataService.getSnackbar('Delete Failed', error); + }); + } + + openDialog(commentId: string, comment: string) { + const dialogRef = this.editCommentDialog.open(EditCommentComponent, { + width: '50%', + data: { commentId: commentId, comment: comment } + }); + + dialogRef.afterClosed().subscribe(result => { + comment = result; + this.editComment(commentId, comment); + }); + } + + editComment(commentId: string, comment: string) { + this.editedComment.text = comment; + this.dataService.FirePUT(API.CommentEdit, + this.editedComment, { commentId: commentId }).subscribe(() => { + this.refresh(); + this.editedComment.text = ''; + }); + } +} diff --git a/src/app/venter/complaints/complaints-home.component.css b/src/app/venter/complaints/complaints-home.component.css new file mode 100644 index 00000000..3d827b7c --- /dev/null +++ b/src/app/venter/complaints/complaints-home.component.css @@ -0,0 +1,41 @@ +.container { + margin: 0 175px; +} + +.toolbar { + padding: 150px 0; + background-color: #536dfe; + text-align: center; +} + +.complaint-text { + color: white; + font-weight: bolder; +} + +.complaint-button { + color: black; + background-color: yellow; + padding: 10px 50px; + font-weight: bold; + text-decoration: none; +} + +.complaint-tabs { + font-weight: bold; + text-decoration: none; +} + +.no-complaints-container { + text-align: center; +} + +.no-complaints-image { + width: 50%; +} + +@media (max-width: 970px) { + .container { + margin: 0%; + } +} diff --git a/src/app/venter/complaints/complaints-home.component.html b/src/app/venter/complaints/complaints-home.component.html new file mode 100644 index 00000000..78d4c4fe --- /dev/null +++ b/src/app/venter/complaints/complaints-home.component.html @@ -0,0 +1,38 @@ +
+
+
+ Nobody taking care of your civic complaints? Be it garbage, water, potholes etc. +
+
+ + Vent your issues now! + +
+
+ + +
+ +
No complaints to show
+
+
+
+ +
+
+
+ +
+ +
No complaints to show
+
+
+
+ +
+
+
+
+
+
diff --git a/src/app/venter/complaints/complaints-home.component.ts b/src/app/venter/complaints/complaints-home.component.ts new file mode 100644 index 00000000..aa034edd --- /dev/null +++ b/src/app/venter/complaints/complaints-home.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit } from '@angular/core'; +import { IComplaint } from '../../interfaces'; +import { DataService } from '../../data.service'; +import { API } from '../../../api'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { VenterDataService } from '../venter-data.service'; + +@Component({ + selector: 'app-complaints-home', + templateUrl: './complaints-home.component.html', + styleUrls: ['./complaints-home.component.css'], +}) +export class ComplaintsHomeComponent implements OnInit { + + public complaints: IComplaint[] = []; + public myComplaints: IComplaint[] = []; + public userid: string; + public myComplaintsFlag: boolean; + public complaintsFlag: boolean; + + constructor( + public dataService: DataService, + public snackBar: MatSnackBar, + public venterDataService: VenterDataService, + ) { } + + ngOnInit() { + /* Get profile if the user is logged in */ + if (this.dataService.isLoggedIn()) { + this.getUser(); + } + /* Set title */ + this.dataService.setTitle('Complaints & Suggestions'); + this.refresh(); + } + + getUser() { + this.dataService.GetFillCurrentUser().subscribe(user => { + this.userid = user.id; + }); + } + + refresh() { + /* Get all the complaints from server*/ + this.dataService.FireGET(API.Complaints).subscribe(result => { + this.complaints = result; + if (this.complaints.length === 0) { + this.complaintsFlag = true; + } else { + this.venterDataService.getComplaintInfo(this.complaints); + } + }); + /* Get the current user complaints from server*/ + this.dataService.FireGET(API.MyComplaints).subscribe(result => { + this.myComplaints = result; + if (this.myComplaints.length === 0) { + this.myComplaintsFlag = true; + } else { + this.venterDataService.getComplaintInfo(this.myComplaints); + } + }); + } +} diff --git a/src/app/venter/edit-comment/edit-comment.component.css b/src/app/venter/edit-comment/edit-comment.component.css new file mode 100644 index 00000000..9139dbce --- /dev/null +++ b/src/app/venter/edit-comment/edit-comment.component.css @@ -0,0 +1,3 @@ +.comment-input-field { + width: 100%; +} diff --git a/src/app/venter/edit-comment/edit-comment.component.html b/src/app/venter/edit-comment/edit-comment.component.html new file mode 100644 index 00000000..571a9f9c --- /dev/null +++ b/src/app/venter/edit-comment/edit-comment.component.html @@ -0,0 +1,10 @@ +
+

Enter new Comment:

+ + + +
+
+ + +
diff --git a/src/app/venter/edit-comment/edit-comment.component.ts b/src/app/venter/edit-comment/edit-comment.component.ts new file mode 100644 index 00000000..31dd4550 --- /dev/null +++ b/src/app/venter/edit-comment/edit-comment.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; +import { IComplaintComment } from '../../interfaces'; + +@Component({ + selector: 'app-edit-comment', + templateUrl: './edit-comment.component.html', + styleUrls: ['./edit-comment.component.css'] +}) +export class EditCommentComponent implements OnInit { + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: IComplaintComment, + ) { } + + ngOnInit() { + } + + onNoClick(): void { + this.dialogRef.close(); + } +} diff --git a/src/app/venter/file/file-complaint.component.css b/src/app/venter/file/file-complaint.component.css new file mode 100644 index 00000000..1e4672c5 --- /dev/null +++ b/src/app/venter/file/file-complaint.component.css @@ -0,0 +1,169 @@ +.container { + margin: 0 175px; + padding: 0 2.5%; +} + +.placeholder-image { + display: block; + margin: auto; +} + +.input-fields { + padding-top: 2%; + display: flex; + flex-direction: column; + width: 100%; +} + +.complaint-description { + width: 90%; +} + +.complaint-image { + display: none; +} + +.add-image-button { + right: true; +} + +mat-form-field { + padding: 1.5% 0; +} + +.added-tags { + padding: 0 1% 1%; + background: lightgrey; +} + +.complaint-button { + color: black; + background-color: yellow; + padding: 10px 50px; + font-weight: bold; + text-decoration: none; + margin: auto; +} + +.tag-chip { + padding: 1% 0; + background-color: yellowgreen; +} + +.add-tag-field { + width: 50%; +} + +* {box-sizing: border-box} +body {margin:0} +.mySlides {display: none; padding-right: 5%;} +img {vertical-align: middle; height: 500px;} + +/* Slideshow container */ +.slideshow-container { + max-width: 500px; + max-height: 500px; + position: relative; + margin: auto; +} + +.slideshow-container img{ + display: none; +} + +.slideshow-container img.image-active { + display: block; + width: 100%; + margin: auto; +} + +/* Next & previous buttons */ +.prev, .next { + cursor: pointer; + position: absolute; + top: 50%; + width: auto; + padding: 16px; + margin-top: -5%; + color: white; + font-weight: bold; + font-size: 18px; + border-radius: 0 3px 3px 0; + user-select: none; + background-color: rgba(0,0,0,0.8); +} + +.image-dot { + text-align: center; +} + +/* The dots/bullets/indicators */ +.dot { + cursor: pointer; + height: 15px; + width: 15px; + margin: 0 2px; + background-color: #bbb; + border-radius: 50%; + display: inline-block; + transition: background-color 0.6s ease; +} + +.active, .dot:hover { + background-color: #717171; +} + +/* Fading animation */ +.fade { + -webkit-animation-name: fade; + -webkit-animation-duration: 1.5s; + animation-name: fade; + animation-duration: 1.5s; +} + +@-webkit-keyframes fade { + from {opacity: .4} + to {opacity: 1} +} + +@keyframes fade { + from {opacity: .4} + to {opacity: 1} +} + +@media (max-width: 970px) { + .container { + margin: 0%; + padding: 2%; + } +} + +@media (max-width: 520px) { + .container { + margin: 0%; + padding: 0%; + } + + img { + max-width: 100%; + max-height: 25%; + } + + .slideshow-container { + max-width: 100%; + max-height: 20%; + position: relative; + margin: auto; + } + + * {box-sizing: border-box} + body {margin:0} + .mySlides {display: none;} + img {vertical-align: middle; height: 20%;} +} + +@media (max-width: 450px) { + .complaint-description { + width: 80%; + } +} diff --git a/src/app/venter/file/file-complaint.component.html b/src/app/venter/file/file-complaint.component.html new file mode 100644 index 00000000..fcb8151d --- /dev/null +++ b/src/app/venter/file/file-complaint.component.html @@ -0,0 +1,69 @@ +
+
+ +
+ +
+
+ + +
+ + +
+ +
+
+ +
+ + + + + + + + + + + + + +
+
+
+ "Added Tags" +
+ + + {{ selectedTag }} + + + +
+
+ + + + + {{ option }} + + + + +
+
+ +
+
diff --git a/src/app/venter/file/file-complaint.component.ts b/src/app/venter/file/file-complaint.component.ts new file mode 100644 index 00000000..e3a54cca --- /dev/null +++ b/src/app/venter/file/file-complaint.component.ts @@ -0,0 +1,126 @@ +import { Component, OnInit } from '@angular/core'; +import { IComplaint, IComplaintTag, IComplaintPost } from '../../interfaces'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { DataService } from '../../data.service'; +import { API } from '../../../api'; +import { Observable } from 'rxjs'; +import { FormControl } from '@angular/forms'; +import { startWith, map } from 'rxjs/operators'; +import { VenterDataService } from '../venter-data.service'; + +const PLACEHOLDER = 'assets/add_image_placeholder.svg'; +const currentLat = 19.1310; +const currentLong = 72.9077; + +@Component({ + selector: 'app-file-complaint', + templateUrl: './file-complaint.component.html', + styleUrls: ['./file-complaint.component.css'] +}) +export class FileComplaintComponent implements OnInit { + + public networkBusy = false; + + tagCategories: IComplaintTag[]; + option: string[]; + + myControl = new FormControl(); + filteredOptions: Observable; + public complaint: IComplaintPost; + public image: string; + public tag: string; + public selectedTags: string[] = []; + public selectedindex = 0; + + constructor( + public dataService: DataService, + public snackBar: MatSnackBar, + public venterDataService: VenterDataService, + ) { } + + ngOnInit() { + this.tagCategories = []; + this.complaint = {} as IComplaintPost; + this.option = []; + this.complaint.latitude = currentLat; + this.complaint.longitude = currentLong; + this.complaint.images = []; + this.complaint.location_description = 'IIT Area'; + + this.dataService.setTitle('Complaints & Suggestions'); + /* Get all the tags from server*/ + this.dataService.FireGET(API.TagCategories).subscribe(result => { + this.tagCategories = result; + this.tagCategories.forEach(element => { + this.option.push(element.tag_uri); + }); + }); + this.filteredOptions = this.myControl.valueChanges + .pipe( + startWith(''), + map(value => this._filter(value)) + ); + } + + private _filter(value: any): string[] { + const filterValue = value.toLowerCase(); + + return this.option.filter(option => option.toLowerCase().includes(filterValue)); + } + + MarkNetworkBusy(): Boolean { + if (this.networkBusy) { return false; } + this.networkBusy = true; + return true; + } + + selectImage(index: number) { + this.selectedindex = index; + } + + uploadImage(files: FileList) { + if (!this.MarkNetworkBusy()) { return; } + this.dataService.UploadImage(files[0]).subscribe(result => { + this.image = result.picture; + this.complaint.images.push(this.image); + this.networkBusy = false; + this.venterDataService.getSnackbar('Image Uploaded', null); + }, (error) => { + this.networkBusy = false; + this.venterDataService.getSnackbar('Upload Failed', error); + }); + } + + /** + * Gets the image URL or placeholder + */ + getImageUrl() { + if (this.complaint && this.image) { + return this.image; + } else { + return PLACEHOLDER; + } + } + + addTag() { + this.selectedTags.push(this.tag); + this.tag = ''; + } + + clearTag(deleteTag: string) { + for (let i = 0; i < this.selectedTags.length; i++) { + if (this.selectedTags[i] === deleteTag) { + this.selectedTags.splice(i, 1); + } + } + } + + submitComplaint() { + this.complaint.tags = this.selectedTags; + if (this.complaint.description === '') { + this.venterDataService.getSnackbar('Please enter a description before submitting the complaint!', null); + } else { + this.dataService.FirePOST(API.Complaints, this.complaint).subscribe(() => {}); + } + } +} diff --git a/src/app/venter/venter-data.service.ts b/src/app/venter/venter-data.service.ts new file mode 100644 index 00000000..adb9eef5 --- /dev/null +++ b/src/app/venter/venter-data.service.ts @@ -0,0 +1,107 @@ +import { Injectable } from '@angular/core'; +import * as moment from 'moment'; +import { IComplaint } from '../interfaces'; +import { MatSnackBar } from '@angular/material'; +import { DataService } from '../data.service'; +import { API } from '../../api'; + +@Injectable() +export class VenterDataService { + + constructor( + public snackBar: MatSnackBar, + public dataService: DataService, + ) { } + + getComplaintInfo(complaintList: IComplaint[]) { + complaintList.forEach(element => { + this.getStatusColor(element); + this.getComplaintReportedTime(element); + }); + } + + getDetailedComplaintInfo(complaint: IComplaint) { + this.getStatusColor(complaint); + this.getComplaintReportedTime(complaint); + this.getCommentReportedTime(complaint); + } + + getStatusColor(complaint: IComplaint) { + switch (complaint.status.toLowerCase()) { + case 'reported': { + complaint.status_color = 'red'; + break; + } + case 'resolved': { + complaint.status_color = 'green'; + break; + } + case 'in progress': { + complaint.status_color = 'yellow'; + break; + } + default: { + complaint.status_color = 'red'; + break; + } + } + } + + getComplaintReportedTime(complaint: IComplaint) { + complaint.reported_date = moment(complaint.report_date).format('MMMM Do YYYY'); + } + + getCommentReportedTime(complaint: IComplaint) { + complaint.comments.forEach(element => { + element.reported_date = moment(element.time).format('MMMM Do YYYY'); + }); + } + + getSnackbar(text: string, error: any) { + if (error === null) { + this.snackBar.open(text, 'Dismiss', { + duration: 2000, + }); + } else { + this.snackBar.open(text + ` - ${error.message}`, 'Dismiss', { + duration: 2000, + }); + } + } + + subscribeToComplaint(complaint_subscribed: number, complaintId: string) { + this.dataService.FireGET(API.SubscribeToComplaint, + { complaintId: complaintId, action: complaint_subscribed === 0 ? 1 : 0 }).subscribe(() => { + if (complaint_subscribed === 1) { + this.getSnackbar('You are unsubscribed from this complaint', null); + this.snackBar.open('', 'Dismiss', { + duration: 2000, + }); + } else { + this.getSnackbar('You are subscribed to this complaint', null); + } + }, (error) => { + this.getSnackbar('Subscription Failed', error); + }); + } + + upVoteComplaint(has_upvoted: boolean, complaintId: string) { + let complaint_upvoted: number; + if (has_upvoted === true) { + complaint_upvoted = 1; + } else { + complaint_upvoted = 0; + } + this.dataService.FireGET(API.UpVote, + { complaintId: complaintId, action: complaint_upvoted === 0 ? 1 : 0 }) + .subscribe(() => { + if (complaint_upvoted === 1) { + this.getSnackbar('Your upvote has been removed', null); + } else { + this.getSnackbar('You have upvoted this complaint', null); + } + }, (error) => { + this.getSnackbar('Upvote Removal Failed', error); + }); + } +}