<file_sep>import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class TagsService {

  private tags = [
    'All',
    'Web',
    'Database',
    'API',
    'Fullstack',
    'DevOps',
    'Operating Systems',
    'Embedded Systems',
    'Machine Learning',
    'Game Development',
    'Cloud Computing',
    'Programming Languages'
  ];;

  constructor() { }

  async getTags(): Promise<string[]> {
    return this.tags;
  }
}
<file_sep>export const environment = {
  production: true,
  BASE_API_URL: 'https://api-m5xbm75jva-uc.a.run.app'
};
<file_sep>import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
import { Project } from '../types/project';

@Injectable({
  providedIn: 'root'
})
export class ProjectsService {

  constructor(private http: HttpClient) { }

  async searchProjects(query: string, tags: string[]) {

  }

  async getProjects(): Promise<Project[]> {
    return await this.http.get<Project[]>(`${environment.BASE_API_URL}/projects`).toPromise();
  }

  async getProject(id: string): Promise<Project> {
    return await this.http.get<Project>(`${environment.BASE_API_URL}/projects/${id}`).toPromise();
  }

  async createProject(project: Project): Promise<void> {
    await this.http.post(`${environment.BASE_API_URL}/projects`, project, { withCredentials: true }).toPromise();
  }

  async updateProject(id: string, project: Partial<Project>) {
    await this.http.patch(`${environment.BASE_API_URL}/projects/${id}`, project, { withCredentials: true }).toPromise();
  }

  async deleteProject(id: string) {
    await this.http.delete(`${environment.BASE_API_URL}/projects/${id}`).toPromise();
  }

  async getAuthorProjects(authId: string): Promise<Project[]> {
    return await  this.http.get<Project[]>(`${environment.BASE_API_URL}/users/${authId}/projects`).toPromise();
  }
}
<file_sep>import { DOCUMENT } from '@angular/common';
import { Component, Inject, OnInit } from '@angular/core';
import { environment } from 'src/environments/environment';

@Component({
  selector: 'app-navbar',
  templateUrl: './navbar.component.html',
  styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit {

  constructor(@Inject(DOCUMENT) private document: Document) { }

  ngOnInit(): void {
    console.log(environment.BASE_API_URL);
  }

  login() {
    this.document.location.href = environment.BASE_API_URL + '/google';
  }

}
<file_sep>import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { CookieService } from 'ngx-cookie-service';
import { environment } from 'src/environments/environment';
import { User } from '../types/user';

export const COOKIE = 'hackhub';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private _user: { id: string, name: string, email: string, profileURL: string } = null;

  constructor(private http: HttpClient, private cookieService: CookieService) { }

  async getUser(id: string): Promise<User> {
    return await this.http.get<User>(`${environment.BASE_API_URL}/users/${id}`).toPromise();
  }

  async getUsers(): Promise<User[]> {
    return await this.http.get<User[]>(`${environment.BASE_API_URL}/users`).toPromise();
  }

  async updateUser(id: string, updates: Partial<User>) {
    await this.http.patch(`${environment.BASE_API_URL}/users/${id}`, updates).toPromise()
  }

  async deleteUser(id: string) {
    await this.http.delete(`${environment.BASE_API_URL}/users/${id}`).toPromise();
  }

  async user() {
    if (!this._user) {
      this._user = await this.http.get<any>(`${environment.BASE_API_URL}/users/token`, { withCredentials: true }).toPromise();
    }
    return this._user;
  }

  getCookie() {
    if (this.cookieService.check(COOKIE)) {
      return this.cookieService.get(COOKIE);
    } else {
      throw new Error('User is not logged in!');
    }
  }
}
<file_sep>import { Component, Inject, OnInit } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ProjectsService } from 'src/app/services/projects.service';
import { UserService } from 'src/app/services/user.service';
import { Project } from 'src/app/types/project';

@Component({
  selector: 'app-project-popup',
  templateUrl: './project-popup.component.html',
  styleUrls: ['./project-popup.component.scss']
})
export class ProjectPopupComponent implements OnInit {

  mode: string;

  model: Project = {
    authorID: '',
    name: '',
    description: '',
    repositoryURL: null
  }

  constructor(private projectsService: ProjectsService, private userService: UserService, private dialogRef: MatDialogRef<ProjectPopupComponent>, @Inject(MAT_DIALOG_DATA) data) {
    this.mode = data.mode;
    if (this.mode === 'update') {
      this.model = data.params;
    }
  }

  ngOnInit(): void {
  }

  async submit() {
    switch(this.mode) {
      case 'create':
        const user = (await this.userService.user()).id;
        this.model.authorID = user;
        await this.projectsService.createProject(this.model);
        break;
      case 'update':
        await this.projectsService.updateProject(this.model._id, this.model);
        break;
    }
    
    this.cancel();
  }

  cancel() {
    this.dialogRef.close();
  }

}
<file_sep>import { Component, OnInit } from '@angular/core';
import { TagsService } from 'src/app/services/tags.service';

@Component({
  selector: 'app-searchbar',
  templateUrl: './searchbar.component.html',
  styleUrls: ['./searchbar.component.scss']
})
export class SearchbarComponent implements OnInit {

  tags: string[] = [];

  constructor(private tagsService: TagsService) { }

  async ngOnInit(): Promise<void> {
    this.tags = await this.tagsService.getTags();
  }

}
<file_sep>import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ProjectsService } from '../services/projects.service';
import { UserService } from '../services/user.service';
import { Project } from '../types/project';
import { ProjectPopupComponent } from './project-popup/project-popup.component';

@Component({
  selector: 'app-author-dashboard',
  templateUrl: './author-dashboard.component.html',
  styleUrls: ['./author-dashboard.component.scss']
})
export class AuthorDashboardComponent implements OnInit {

  projects: Project[] = [];

  constructor(private projectService: ProjectsService, private userService: UserService, private dialog: MatDialog) { }

  async ngOnInit(): Promise<void> {
    await this.detectChange();
  }

  async openCreateProjectPopup() {
    const dialogRef = this.dialog.open(ProjectPopupComponent, {
      data: { mode: 'create' }
    });
    await dialogRef.afterClosed().toPromise()
    await this.detectChange();
  }

  async detectChange() {
    const user = (await this.userService.user()).id;
    this.projects = await this.projectService.getAuthorProjects(user);
  }

}
<file_sep>FROM nginx:alpine

COPY /dist/hackhub-client /usr/share/nginx/html

EXPOSE 80<file_sep>import { Component, Inject, OnInit } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ProjectsService } from 'src/app/services/projects.service';

@Component({
  selector: 'app-delete-confirm',
  templateUrl: './delete-confirm.component.html',
  styleUrls: ['./delete-confirm.component.scss']
})
export class DeleteConfirmComponent implements OnInit {

  mode: string;
  id: string;

  constructor(private projectsService: ProjectsService, private dialogRef: MatDialogRef<DeleteConfirmComponent>, @Inject(MAT_DIALOG_DATA) data) {
    this.mode = data.mode;
    this.id = data.params
  }

  ngOnInit(): void {
  }

  async submit() {
    await this.projectsService.deleteProject(this.id);
    this.cancel();
  }

  cancel() {
    this.dialogRef.close();
  }

}
<file_sep>import { Tag } from './tags';

export class Project {
    _id?: string;
    authorID: string;
    name: string;
    description: string;
    repositoryURL?: string;
    tags?: Tag[];
}<file_sep>import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ProjectPopupComponent } from './author-dashboard/project-popup/project-popup.component';
import { MatDialogModule } from "@angular/material/dialog";
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NavbarComponent } from './shared/navbar/navbar.component';
import { SearchbarComponent } from './shared/searchbar/searchbar.component';
import { ProjectsComponent } from './projects/projects.component';
import { ProjectCardComponent } from './shared/project-card/project-card.component';
import { ProjectDetailsComponent } from './project-details/project-details.component';
import { AuthorDashboardComponent } from './author-dashboard/author-dashboard.component';
import { ProjectTileComponent } from './author-dashboard/project-tile/project-tile.component';
import { FormsModule } from '@angular/forms';
import { DeleteConfirmComponent } from './author-dashboard/delete-confirm/delete-confirm.component';
import { CookieService } from 'ngx-cookie-service';
import { AuthInterceptor } from './services/auth.intercepter';

@NgModule({
  declarations: [
    AppComponent,
    NavbarComponent,
    SearchbarComponent,
    ProjectsComponent,
    ProjectCardComponent,
    ProjectDetailsComponent,
    AuthorDashboardComponent,
    ProjectTileComponent,
    ProjectPopupComponent,
    DeleteConfirmComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatDialogModule,
    FormsModule,
    HttpClientModule,
  ],
  providers: [
    CookieService,
    {
      provide : HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi   : true,
    },
  ],
  bootstrap: [AppComponent],
  entryComponents: [ProjectPopupComponent, DeleteConfirmComponent]
})
export class AppModule { }
<file_sep>export const environment = {
    production: true,
    BASE_API_URL: 'https://api-staging-m5xbm75jva-uc.a.run.app'
};<file_sep>import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ProjectsService } from '../services/projects.service';
import { UserService } from '../services/user.service';
import { Project } from '../types/project';
import { User } from '../types/user';

@Component({
  selector: 'app-project-details',
  templateUrl: './project-details.component.html',
  styleUrls: ['./project-details.component.scss']
})
export class ProjectDetailsComponent implements OnInit {

  project: Project;
  author: User;

  constructor(private route: ActivatedRoute, private projectService: ProjectsService, private userService: UserService) { }

  async ngOnInit(): Promise<void> {
    const id = this.route.snapshot.paramMap.get('id');
    this.project = await this.projectService.getProject(id);
    this.author = await this.userService.getUser(this.project.authorID);
  }

}
<file_sep>import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Project } from 'src/app/types/project';
import { DeleteConfirmComponent } from '../delete-confirm/delete-confirm.component';
import { ProjectPopupComponent } from '../project-popup/project-popup.component';

@Component({
  selector: 'app-project-tile',
  templateUrl: './project-tile.component.html',
  styleUrls: ['./project-tile.component.scss']
})
export class ProjectTileComponent implements OnInit {

  @Input() project: Project;
  @Output() detectChange = new EventEmitter<boolean>();

  constructor(private dialog: MatDialog) { }

  ngOnInit(): void {
  }

  async updateProject() {
    const dialogRef = this.dialog.open(ProjectPopupComponent, {
      data: { mode: 'update', params: this.project }
    });
    await dialogRef.afterClosed().toPromise();
    this.detectChange.emit(true);
  }

  async deleteProject() {
    const dialogRef = this.dialog.open(DeleteConfirmComponent, {
      data: { mode: 'project', params: this.project._id }
    });
    await dialogRef.afterClosed().toPromise();
    this.detectChange.emit(true);
  }
}
