Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing post frontend #19

Merged
merged 15 commits into from
Sep 30, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ public class PostService {

@Transactional(readOnly = true)
public List<PostResponseDTO> getAllPosts() {
return postRepository.findAll().stream().map(this::convertToDTO).collect(Collectors.toList());
return postRepository.findAll().stream()
.map(this::convertToDTO)
.collect(
Collectors.collectingAndThen(
Collectors.toList(),
list -> {
java.util.Collections.reverse(list);
return list;
}));
}

@Transactional(readOnly = true)
Expand Down
6 changes: 3 additions & 3 deletions postrify-frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ApiService } from './services/api.service';
import { HeaderComponent } from './header/header.component';
import { ToastComponent } from './toast/toast.component';
import { FooterComponent } from './footer/footer.component';
import { HeaderComponent } from './components/header/header.component';
import { ToastComponent } from './components/toast/toast.component';
import { FooterComponent } from './components/footer/footer.component';

@Component({
selector: 'app-root',
Expand Down
14 changes: 10 additions & 4 deletions postrify-frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { HomeComponent } from './home/home.component';
import { PageNotFoundComponentComponent } from './page-not-found-component/page-not-found-component.component';
import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/register/register.component';
import { HomeComponent } from './components/home/home.component';
import { PageNotFoundComponentComponent } from './components/page-not-found-component/page-not-found-component.component';
import { PostFormComponent } from './components/post-form/post-form.component';
import { PostDetailComponent } from './components/post-detail/post-detail.component';
import { PostUpdateComponent } from './components/post-update/post-update.component';

export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'create', component: PostFormComponent },
{ path: 'post/:id', component: PostDetailComponent },
{ path: 'edit/:id', component: PostUpdateComponent },
{ path: '**', component: PageNotFoundComponentComponent },
];
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { Component } from '@angular/core';
import packageJson from '../../../package.json';
import packageJson from '../../../../package.json';

@Component({
selector: 'app-footer',
standalone: true,
template: `
<footer class="footer">
<div class="version-date">
<span>Version: {{ version }}</span> -
<span>{{ currentDate }}</span>
<p>v{{ version }} &copy; {{ currentYear }} Postrify</p>
<p>
Licensed under
<a href="https://github.com/jorbush/postrify/blob/main/LICENSE"
>Apache 2.0</a
>
</p>
</div>
<div class="social-icons">
<a href="mailto:[email protected]" aria-label="Email">
Expand Down Expand Up @@ -162,10 +167,14 @@ import packageJson from '../../../package.json';
transform: scale(1.2);
stroke: var(--primary-color-hover);
}

a {
text-decoration: none;
}
`,
],
})
export class FooterComponent {
version: string = packageJson.version;
currentDate: string = new Date().toLocaleDateString();
currentYear: string = new Date().getFullYear().toString();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { RouterLink } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { AuthService } from '../../services/auth.service';

@Component({
selector: 'app-header',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { HomeComponent } from './home.component';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { provideHttpClient } from '@angular/common/http';

describe('HomeComponent', () => {
let component: HomeComponent;
Expand All @@ -9,6 +11,7 @@ describe('HomeComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HomeComponent],
providers: [provideHttpClient(), provideHttpClientTesting()],
}).compileComponents();

fixture = TestBed.createComponent(HomeComponent);
Expand Down
134 changes: 134 additions & 0 deletions postrify-frontend/src/app/components/home/home.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Component, OnInit } from '@angular/core';
import { PostResponseDTO } from '../../models/post-response.model';
import { PostService } from '../../services/post.service';
import { Router } from '@angular/router';
import { AuthService } from '../../services/auth.service';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-home',
standalone: true,
imports: [CommonModule],
template: `
<div class="home-container">
<div class="posts-grid">
@for (post of posts; track post.id) {
<div
class="post-card"
(click)="viewPost(post.id)"
role="button"
tabindex="0"
(keyup.enter)="viewPost(post.id)"
(keyup.space)="viewPost(post.id)"
>
<h3>{{ post.title }}</h3>
<p>
{{ post.content | slice: 0 : 100
}}{{ post.content.length > 100 ? '...' : '' }}
</p>
<div class="post-meta">
<span>By: {{ post.user.username }}</span>
<span>{{ post.createdAt | date: 'short' }}</span>
</div>
</div>
}
</div>
@if (isLogged) {
<button
class="floating-button"
(click)="createPost()"
aria-label="Create new post"
>
+
</button>
}
</div>
`,
styles: `
.home-container {
padding: 20px;
position: relative;
}

.posts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}

.post-card {
background-color: var(--header-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 15px;
cursor: pointer;
transition: box-shadow 0.3s;
}

.post-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.post-meta {
display: flex;
justify-content: space-between;
font-size: 0.9em;
color: var(--secondary-text-color);
margin-top: 10px;
}

.floating-button {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 50%;
font-size: 2em;
cursor: pointer;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: background-color 0.3s;
}

.floating-button:hover {
background-color: var(--primary-color-hover);
}
`,
})
export class HomeComponent implements OnInit {
posts: PostResponseDTO[] = [];
isLogged = false;

constructor(
private postService: PostService,
private router: Router,
private authService: AuthService,
) {}

ngOnInit(): void {
this.fetchPosts();
this.isLogged = this.authService.isAuthenticated();
}

fetchPosts(): void {
this.postService.getAllPosts().subscribe({
next: (posts) => {
this.posts = posts;
},
error: (err) => {
console.error(err);
},
});
}

viewPost(id: number): void {
this.router.navigate(['/post', id]);
}

createPost(): void {
this.router.navigate(['/create']);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
import { AuthService } from '../services/auth.service';
import { AuthService } from '../../services/auth.service';
import { of, throwError } from 'rxjs';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Component } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { AuthService } from '../../services/auth.service';
import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ToastService } from '../services/toast.service';
import { ToastService } from '../../services/toast.service';
import { Router } from '@angular/router';

@Component({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { PostDetailComponent } from './post-detail.component';
import { ActivatedRoute } from '@angular/router';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { provideHttpClient } from '@angular/common/http';

describe('PostDetailComponent', () => {
let component: PostDetailComponent;
let fixture: ComponentFixture<PostDetailComponent>;
let mockActivatedRoute;

beforeEach(async () => {
mockActivatedRoute = {
snapshot: {
paramMap: {
get: () => '1',
},
},
};
await TestBed.configureTestingModule({
imports: [PostDetailComponent],
providers: [
provideHttpClient(),
provideHttpClientTesting(),
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
],
}).compileComponents();

fixture = TestBed.createComponent(PostDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading
Loading