diff --git a/backend/api/post.py b/backend/api/post.py new file mode 100644 index 0000000..dc52f9c --- /dev/null +++ b/backend/api/post.py @@ -0,0 +1,19 @@ +from fastapi import APIRouter, Depends, HTTPException +from ..services import PostService, UserService +from ..models import Post +from .authentication import registered_user + +api = APIRouter(prefix="/api/post") + + +@api.post("", response_model=Post, tags=['Post']) +def create(post: Post, post_svc: PostService = Depends(), usr_svc: UserService = Depends()): + try: + user_entity = usr_svc.findUser(post.user) + except: + raise HTTPException(status_code=422, detail=str("User Not Registered")) + return post_svc.create(post,user_entity) + +@api.get("", response_model=list[Post], tags=['Post']) +def getAll(post_svc: PostService = Depends()): + return post_svc.getAll() diff --git a/backend/entities/__init__.py b/backend/entities/__init__.py index 8f3a6ef..4c82a91 100644 --- a/backend/entities/__init__.py +++ b/backend/entities/__init__.py @@ -18,6 +18,7 @@ from .role_entity import RoleEntity from .permission_entity import PermissionEntity from .user_role_entity import user_role_table +from .post_entity import PostEntity __authors__ = ["Kris Jordan"] diff --git a/backend/entities/post_entity.py b/backend/entities/post_entity.py new file mode 100644 index 0000000..c1223b3 --- /dev/null +++ b/backend/entities/post_entity.py @@ -0,0 +1,60 @@ +'''User accounts for all registered users in the application.''' + + +from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from typing import Self +from .entity_base import EntityBase +#from ..models import User +from ..models import Post +from fastapi import Depends +from .user_entity import UserEntity +from .post_votes_entity import post_votes_table +#from ..services import UserPostService + +__authors__ = ['Kris Jordan'] +__copyright__ = 'Copyright 2023' +__license__ = 'MIT' + + +class PostEntity(EntityBase): + __tablename__ = 'posts' + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + content: Mapped[str] = mapped_column(String(64), nullable=False, default='') + + user_id: Mapped[int] = mapped_column(ForeignKey('user.id')) + user: Mapped[UserEntity] = relationship("UserEntity",back_populates='posts') + + votes: Mapped[list[UserEntity]] = relationship(secondary=post_votes_table) + + timestamp: Mapped[str] = mapped_column(String(64), nullable=False, default='') + + @classmethod + def from_model(cls, model: Post, user: UserEntity ) -> Self: + #user_svc: UserPostService = Depends() + return cls( + id=model.id, + content=model.content, + user = user, + votes= [], + timestamp=model.timestamp, + + #user_svc.findUser(model.user) + #[user_svc.findUser(vote) for vote in model.votes] + ) + + def to_model(self) -> Post: + vote_num = [vote.to_model() for vote in self.votes] + return Post( + id=self.id, + content=self.content, + user=self.user.to_model(), + votes=vote_num, + timestamp=self.timestamp, + ) + + def update(self, model: Post) -> None: + self.content = model.content + + diff --git a/backend/entities/post_votes_entity.py b/backend/entities/post_votes_entity.py new file mode 100644 index 0000000..471e872 --- /dev/null +++ b/backend/entities/post_votes_entity.py @@ -0,0 +1,9 @@ +from sqlalchemy import Table, Column, ForeignKey +from .entity_base import EntityBase + +post_votes_table = Table( + "post_votes", + EntityBase.metadata, + Column('user_id', ForeignKey('user.id'), primary_key=True), + Column('post_id', ForeignKey('posts.id'), primary_key=True) +) \ No newline at end of file diff --git a/backend/entities/role_entity.py b/backend/entities/role_entity.py index ea317c4..45fe5e3 100644 --- a/backend/entities/role_entity.py +++ b/backend/entities/role_entity.py @@ -12,7 +12,7 @@ class RoleEntity(EntityBase): id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String, unique=True) - users: Mapped[list['UserEntity']] = relationship(secondary=user_role_table, back_populates='roles') + users: Mapped[list['UserEntity']] = relationship(secondary=user_role_table) permissions: Mapped[list['PermissionEntity']] = relationship(back_populates='role') @classmethod diff --git a/backend/entities/user_entity.py b/backend/entities/user_entity.py index 15cdc61..ab475bd 100644 --- a/backend/entities/user_entity.py +++ b/backend/entities/user_entity.py @@ -1,12 +1,13 @@ '''User accounts for all registered users in the application.''' -from sqlalchemy import Integer, String +from sqlalchemy import Integer, String, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship from typing import Self from .entity_base import EntityBase from .user_role_entity import user_role_table from ..models import User +from .post_votes_entity import post_votes_table __authors__ = ['Kris Jordan'] @@ -32,6 +33,12 @@ class UserEntity(EntityBase): roles: Mapped[list['RoleEntity']] = relationship(secondary=user_role_table, back_populates='users') permissions: Mapped['PermissionEntity'] = relationship(back_populates='user') + + #create Secondary table for posts??? + posts: Mapped[list['PostEntity']] = relationship(back_populates='user') + + votes: Mapped[list['PostEntity']] = relationship(secondary=post_votes_table, back_populates='votes') + @classmethod def from_model(cls, model: User) -> Self: diff --git a/backend/main.py b/backend/main.py index 6bfad33..7ab8457 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,7 +1,7 @@ """Entrypoint of backend API exposing the FastAPI `app` to be served by an application server such as uvicorn.""" from fastapi import FastAPI -from .api import health, static_files, profile, authentication, user +from .api import health, static_files, profile, authentication, user, post from .api.admin import users as admin_users from .api.admin import roles as admin_roles @@ -26,4 +26,5 @@ app.include_router(authentication.api) app.include_router(admin_users.api) app.include_router(admin_roles.api) +app.include_router(post.api) app.mount("/", static_files.StaticFileMiddleware(directory="./static")) \ No newline at end of file diff --git a/backend/models/__init__.py b/backend/models/__init__.py index 6255f08..0450829 100644 --- a/backend/models/__init__.py +++ b/backend/models/__init__.py @@ -5,6 +5,12 @@ from .user import User, ProfileForm, NewUser from .role import Role from .role_details import RoleDetails +from .post import Post +# import sys +# sys.path.append("/workspace/backend/models") +# import + + __authors__ = ["Kris Jordan"] __copyright__ = "Copyright 2023" diff --git a/backend/models/post.py b/backend/models/post.py new file mode 100644 index 0000000..7de30a2 --- /dev/null +++ b/backend/models/post.py @@ -0,0 +1,15 @@ +"""Post model serves as data model for posts in the Forum""" + +from pydantic import BaseModel +from . import User + +class Post(BaseModel): + id: int | None = None + content: str + user: User + votes: list[User] = [] + timestamp: str + + + + diff --git a/backend/services/__init__.py b/backend/services/__init__.py index 5408cff..13ce1d7 100644 --- a/backend/services/__init__.py +++ b/backend/services/__init__.py @@ -1,3 +1,5 @@ from .user import UserService from .permission import PermissionService, UserPermissionError -from .role import RoleService \ No newline at end of file +from .role import RoleService +from .post import PostService +from .user_post import UserPostService \ No newline at end of file diff --git a/backend/services/post.py b/backend/services/post.py new file mode 100644 index 0000000..5ef6a30 --- /dev/null +++ b/backend/services/post.py @@ -0,0 +1,26 @@ +from fastapi import Depends +from sqlalchemy import select, or_, func +from sqlalchemy.orm import Session +from ..database import db_session +from ..models import User, Role, RoleDetails, Permission, Post +from ..entities import RoleEntity, PermissionEntity, UserEntity +from ..entities.post_entity import PostEntity +from .permission import PermissionService, UserPermissionError + + +class PostService: + + def __init__(self, session: Session = Depends(db_session), permission: PermissionService = Depends()): + self._session = session + self._permission = permission + + def create(self, post: Post, user: UserEntity) -> Post: + post_entity = PostEntity.from_model(post,user) + self._session.add(post_entity) + self._session.commit() + return post_entity.to_model() + + def getAll(self) -> list[Post]: + query = select(PostEntity) + entities = self._session.scalars(query).all() + return [entity.to_model() for entity in entities] \ No newline at end of file diff --git a/backend/services/user.py b/backend/services/user.py index 3bf9f16..67a1a6e 100644 --- a/backend/services/user.py +++ b/backend/services/user.py @@ -64,6 +64,14 @@ def search(self, _subject: User, query: str) -> list[User]: statement = statement.where(criteria).limit(10) entities = self._session.execute(statement).scalars() return [entity.to_model() for entity in entities] + + def findUser(self, _subject: User) -> UserEntity: + # query = select(UserEntity).filter_by(id=_subject.id) + query = select(UserEntity).where(UserEntity.pid == _subject.pid) + user_entity: UserEntity = self._session.execute(query).scalar() + return user_entity + + def list(self, subject: User, pagination_params: PaginationParams) -> Paginated[User]: """List Users. @@ -148,3 +156,4 @@ def update(self, subject: User, user: User) -> User: entity.update(user) self._session.commit() return entity.to_model() + diff --git a/backend/services/user_post.py b/backend/services/user_post.py new file mode 100644 index 0000000..ef09c81 --- /dev/null +++ b/backend/services/user_post.py @@ -0,0 +1,22 @@ +from fastapi import Depends +from sqlalchemy import select, or_, func +from sqlalchemy.orm import Session +from ..database import db_session +from ..models import User, Paginated, PaginationParams +from ..entities import UserEntity +from .permission import PermissionService +from . import * + +class UserPostService: + + _session: Session + _permission: PermissionService + + def findUser(self, _subject: User, user_svc: 'UserService' = Depends()) -> UserEntity: + return user_svc.findUser(_subject) + + + + + + diff --git a/backend/test/services/post_test.py b/backend/test/services/post_test.py new file mode 100644 index 0000000..fca1886 --- /dev/null +++ b/backend/test/services/post_test.py @@ -0,0 +1,50 @@ +import pytest + +from sqlalchemy.orm import Session +from ...models import User, Post, Role +from ...entities import UserEntity, RoleEntity +from ...services import PostService, UserService +from sqlalchemy.exc import IntegrityError + +# Mock Models +user = User(id=1, pid=111111111, onyen='onyen',first_name="first", last_name = "last", email='user@unc.edu', pronouns="He/Him/His", permissions = []) + +post_0 = Post(id = 1, content = "post", user = user, votes = [], timestamp = "date0") +post_1 = Post(id = 2, content = "content", user = user, votes = [], timestamp = "date1") + +@pytest.fixture(autouse=True) +def setup_teardown(test_session: Session): + # Bootstrap root User without any perms/roles + user_entity = UserEntity.from_model(user) + test_session.add(user_entity) + + test_session.commit() + yield + +@pytest.fixture() +def postservice(test_session: Session): + return PostService(test_session) + +@pytest.fixture() +def userservice(test_session: Session): + return UserService(test_session) + +def test_create_single_post_pass(postservice: PostService, userservice: UserService): + user_entity: UserEntity = userservice.findUser(user) + post_entity: Post = postservice.create(post_0, user_entity) + assert post_entity == post_0 + +def test_create_two_posts_pass(postservice: PostService, userservice: UserService): + user_entity: UserEntity = userservice.findUser(user) + post_entity_0: Post = postservice.create(post_0, user_entity) + post_entity_1: Post = postservice.create(post_1, user_entity) + assert post_entity_0 == post_0 + assert post_entity_1 == post_1 + +def test_getall_posts_pass(postservice: PostService, userservice: UserService): + user_entity: UserEntity = userservice.findUser(user) + post_entity_0: Post = postservice.create(post_0, user_entity) + post_entity_1: Post = postservice.create(post_1, user_entity) + posts = postservice.getAll() + assert posts[0] == post_0 + assert posts[1] == post_1 \ No newline at end of file diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 0acaef7..2d82f24 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -4,13 +4,15 @@ import { AppTitleStrategy } from './app-title.strategy'; import { GateComponent } from './gate/gate.component'; import { HomeComponent } from './home/home.component'; import { ProfileEditorComponent } from './profile/profile-editor/profile-editor.component'; -import { ForumComponent } from './forum/forum.component'; +import { viewforumComponent } from './viewforum/viewforum.component'; +import { ForumComponent } from './makeforum/makeforum.component'; const routes: Routes = [ HomeComponent.Route, ProfileEditorComponent.Route, GateComponent.Route, + viewforumComponent.Route, ForumComponent.Route, { path: 'admin', title: 'Admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }, ]; diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 72bccfe..bdbf879 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -32,8 +32,9 @@ import { NavigationComponent } from './navigation/navigation.component'; import { ErrorDialogComponent } from './navigation/error-dialog/error-dialog.component'; import { HomeComponent } from './home/home.component'; import { GateComponent } from './gate/gate.component'; -import { ProfileEditorComponent } from './profile/profile-editor/profile-editor.component'; -import { ForumComponent } from './forum/forum.component'; +import { ProfileEditorComponent } from './profile/profile-editor/profile-editor.component'; +import { ForumComponent } from './makeforum/makeforum.component'; +import { viewforumComponent } from './viewforum/viewforum.component'; @NgModule({ declarations: [ @@ -43,7 +44,8 @@ import { ForumComponent } from './forum/forum.component'; HomeComponent, GateComponent, ProfileEditorComponent, - ForumComponent + ForumComponent, + viewforumComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/forum/forum.component.css b/frontend/src/app/forum/forum.component.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/app/forum/forum.component.html b/frontend/src/app/forum/forum.component.html deleted file mode 100644 index 0ca0912..0000000 --- a/frontend/src/app/forum/forum.component.html +++ /dev/null @@ -1 +0,0 @@ -

Welcome to the Forum!

diff --git a/frontend/src/app/forum/forum.component.ts b/frontend/src/app/forum/forum.component.ts deleted file mode 100644 index edfd79e..0000000 --- a/frontend/src/app/forum/forum.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-forum', - templateUrl: './forum.component.html', - styleUrls: ['./forum.component.css'] -}) -export class ForumComponent { - public static Route = { - path: 'forum', - component: ForumComponent - }; -} diff --git a/frontend/src/app/makeforum/makeforum.component.css b/frontend/src/app/makeforum/makeforum.component.css new file mode 100644 index 0000000..765ecf9 --- /dev/null +++ b/frontend/src/app/makeforum/makeforum.component.css @@ -0,0 +1,13 @@ +.button { + transition-duration: 0.4s; + } + + .button:hover { + background-color: #4CAF50; /* Green */ + color: white; + } + + +a { + text-decoration: underline; + } \ No newline at end of file diff --git a/frontend/src/app/makeforum/makeforum.component.html b/frontend/src/app/makeforum/makeforum.component.html new file mode 100644 index 0000000..e32ecc7 --- /dev/null +++ b/frontend/src/app/makeforum/makeforum.component.html @@ -0,0 +1,14 @@ +

Forum

+ +

Make a Forum Post With Resources

+ +
+
+ +
+ +
+ +Back + + diff --git a/frontend/src/app/forum/forum.component.spec.ts b/frontend/src/app/makeforum/makeforum.component.spec.ts similarity index 90% rename from frontend/src/app/forum/forum.component.spec.ts rename to frontend/src/app/makeforum/makeforum.component.spec.ts index df9ae41..cab4c92 100644 --- a/frontend/src/app/forum/forum.component.spec.ts +++ b/frontend/src/app/makeforum/makeforum.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ForumComponent } from './forum.component'; +import { ForumComponent } from './makeforum.component'; describe('ForumComponent', () => { let component: ForumComponent; diff --git a/frontend/src/app/makeforum/makeforum.component.ts b/frontend/src/app/makeforum/makeforum.component.ts new file mode 100644 index 0000000..b7ac590 --- /dev/null +++ b/frontend/src/app/makeforum/makeforum.component.ts @@ -0,0 +1,89 @@ +import { Component } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { PostService, Post } from '../post.service'; +import { Role } from "../role"; +import { HttpErrorResponse } from '@angular/common/http'; +import { Profile, ProfileService } from '../profile/profile.service'; +import { viewforumComponent } from '../viewforum/viewforum.component'; + +// things we need to do - change the name of forum component and you have to call one: makeforum and one should be seeforum +//also we need to figure out how to create an actual user and not just a dummy one +// also we need to fix onerror +@Component({ + selector: 'app-forum', + templateUrl: './makeforum.component.html', + styleUrls: ['./makeforum.component.css'] +}) +export class ForumComponent { + + public static Route = { + path: 'forum', + component: ForumComponent + }; + + form = this.formBuilder.group({ + content: '' + }); + + constructor ( + private postService: PostService, + private formBuilder: FormBuilder, + private profileService: ProfileService + ) {} + + onSubmit(): void { + let form = this.form.value; + let formContent = this.form.value.content ?? ""; + console.log(form) + this.profileService.profile$ + .subscribe({ + next: (profile) => this.onSuccess(profile, formContent), + error: (err) => this.onError(err) + }); + + + if (this.form.value.content?.length == 0) { + window.alert("Please check your input!") + + } + // we need to define onerror +} + +private onSuccess(profile: Profile | undefined, formContent: string): void { + // this is where we have something in scope of type profile + // let current: new Date + let unique = Math.floor(Number(Math.random()*1000)) // generates date of successful form + + let new_Date: Date = new Date(); + // Converting date to string + let result: string = new_Date.toLocaleString(); + // Convert the date object to US specific date string + let date = new_Date.toLocaleString("en-US"); + + if (profile == undefined) { + // handle this case better? + return; + } + + this.postService.makePost(unique, formContent, profile, [], date) + .subscribe({ + next: (post) => this.onSuccessMP(), + error: (err) => this.onError(err) + }); + console.log(profile.pid) +} +private onError(error: Error): void { + if (error.message) { + window.alert(error.message); + } else { + window.alert("Unknown error: " + JSON.stringify(error)); + } +} + + private onSuccessMP(){ + + window.alert('Thank you for posting') + this.form.reset() + } +} + diff --git a/frontend/src/app/navigation/navigation.component.html b/frontend/src/app/navigation/navigation.component.html index d8e22c0..d18cf81 100644 --- a/frontend/src/app/navigation/navigation.component.html +++ b/frontend/src/app/navigation/navigation.component.html @@ -14,7 +14,7 @@ Profile Sign out - Forum + Forum diff --git a/frontend/src/app/post.service.ts b/frontend/src/app/post.service.ts new file mode 100644 index 0000000..b30ad81 --- /dev/null +++ b/frontend/src/app/post.service.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { map, Observable, throwError } from 'rxjs'; +import { Profile } from './profile/profile.service'; +import { Role } from './role'; + + +// these attributes will most likely change +export interface User{ + id: number; + pid: number; + onyen: string; + first_name: string; + last_name: string; + email:string; + pronouns:string; + permissions: []; + } + export interface Post { + id: number; + content: string; + user: User; + votes: User[]; + timestamp: string; +} +@Injectable ({ + providedIn: 'root' +}) + +export class PostService { + + + constructor(private http: HttpClient) {} + + posts: Post[] = []; + + getPost() { + return this.posts; + } + + getPosts(): Observable { + // let table: Observable = this.http.get("/api/forum"); // copied from the getCheckIns + + // let new_table = table + // .pipe( + // map((x: Post[])=> { + // x.forEach(new_post => { + // new_post.timestamp = new Date(new_post.timestamp) + // }); + // return x; + // }) + // ) + // return new_table + return this.http.get("/api/post"); + } + + makePost(id: number, content: string, user: Profile, votes: [], timestamp: string): Observable { + let u: User = {id:2, pid:100000000, onyen:'sol', first_name:"Sol", last_name:"Student", email:"sol@unc.edu", pronouns:"they / them",permissions:[]}; + + let post: Post = {id:id, content: content, user: u, votes: votes, timestamp:timestamp}; + console.log("Made it to api call") + console.log(JSON.stringify(post)) + try{ + return this.http.post("/api/post", post); + }catch (err){ + return throwError(() => new Error("Unable to Post from unregistered user")); + } + + } + + // deletePost(id: number) { + // return this.http.delete("/api/forum/" + id) + // } + +} diff --git a/frontend/src/app/viewforum.service.spec.ts b/frontend/src/app/viewforum.service.spec.ts new file mode 100644 index 0000000..6695f8f --- /dev/null +++ b/frontend/src/app/viewforum.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { viewforumService } from './viewforum.service'; + +describe('makeforumService', () => { + let service: viewforumService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(viewforumService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/viewforum.service.ts b/frontend/src/app/viewforum.service.ts new file mode 100644 index 0000000..cb7acbc --- /dev/null +++ b/frontend/src/app/viewforum.service.ts @@ -0,0 +1,12 @@ + +import { Injectable, PLATFORM_ID } from '@angular/core'; +import { map, Observable, throwError } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; //just added this + +@Injectable({ + providedIn: 'root' +}) +export class viewforumService { + + constructor() { } +} diff --git a/frontend/src/app/viewforum/viewforum.component.css b/frontend/src/app/viewforum/viewforum.component.css new file mode 100644 index 0000000..a02c1ac --- /dev/null +++ b/frontend/src/app/viewforum/viewforum.component.css @@ -0,0 +1,24 @@ +tr:hover {background-color: #D6EEEE;} + +tr:nth-child(even) { + background-color: rgba(150, 212, 212, 0.4); + } + + th:nth-child(even),td:nth-child(even) { + background-color: rgba(150, 212, 212, 0.4); + } + + a { + transition-duration: 0.4s; + border: 2px solid #4CAF50; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + } + + a:hover { + background-color: #4CAF50; /* Green */ + color: white; + } \ No newline at end of file diff --git a/frontend/src/app/viewforum/viewforum.component.html b/frontend/src/app/viewforum/viewforum.component.html new file mode 100644 index 0000000..bf3e777 --- /dev/null +++ b/frontend/src/app/viewforum/viewforum.component.html @@ -0,0 +1,19 @@ +

Forum

+ + + + + + + + + + + + + + + + + Make a Post +
ResponseFirst NameLast NameDate
{{ post.content }}{{ post.user.first_name}}{{ post.user.last_name}}{{ post.timestamp }}
\ No newline at end of file diff --git a/frontend/src/app/viewforum/viewforum.component.spec.ts b/frontend/src/app/viewforum/viewforum.component.spec.ts new file mode 100644 index 0000000..3f99005 --- /dev/null +++ b/frontend/src/app/viewforum/viewforum.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { viewforumComponent } from './viewforum.component'; + +describe('GetforumComponent', () => { + let component: viewforumComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ viewforumComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(viewforumComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/viewforum/viewforum.component.ts b/frontend/src/app/viewforum/viewforum.component.ts new file mode 100644 index 0000000..31fe2c4 --- /dev/null +++ b/frontend/src/app/viewforum/viewforum.component.ts @@ -0,0 +1,21 @@ +import { Component } from '@angular/core'; +import { Post, PostService } from '../post.service'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'app-getforum', + templateUrl: './viewforum.component.html', + styleUrls: ['./viewforum.component.css'] +}) +export class viewforumComponent { + public post$: Observable; + public static Route = { + path: 'viewforum', + component: viewforumComponent + }; + + constructor(private postService: PostService) { + this.post$ = postService.getPosts() + } + +}