From fbb00cdecc51178f7894472c093fd0c9c1d5d68f Mon Sep 17 00:00:00 2001 From: Solomon Laing <solomonlaing@pm.me> Date: Sun, 18 Apr 2021 21:07:04 +0930 Subject: [PATCH 1/5] started implementation of projects page. currently need to work out cors --- src/app/app-routing.module.ts | 4 +- src/app/app.module.ts | 6 +- .../project-card/project-card.component.html | 1 + .../project-card/project-card.component.scss | 0 .../project-card.component.spec.ts | 25 ++++++++ .../project-card/project-card.component.ts | 15 +++++ .../projects/project/project.component.html | 1 + .../projects/project/project.component.scss | 0 .../project/project.component.spec.ts | 25 ++++++++ src/app/projects/project/project.component.ts | 15 +++++ src/app/projects/projects-routing.module.ts | 21 +++++++ src/app/projects/projects.component.html | 6 +- src/app/projects/projects.component.ts | 25 +++++++- src/app/projects/projects.module.ts | 16 +++++ src/app/projects/projects.service.spec.ts | 16 +++++ src/app/projects/projects.service.ts | 60 +++++++++++++++++++ src/app/shared/models/project.model.ts | 6 ++ 17 files changed, 233 insertions(+), 9 deletions(-) create mode 100644 src/app/projects/project-card/project-card.component.html create mode 100644 src/app/projects/project-card/project-card.component.scss create mode 100644 src/app/projects/project-card/project-card.component.spec.ts create mode 100644 src/app/projects/project-card/project-card.component.ts create mode 100644 src/app/projects/project/project.component.html create mode 100644 src/app/projects/project/project.component.scss create mode 100644 src/app/projects/project/project.component.spec.ts create mode 100644 src/app/projects/project/project.component.ts create mode 100644 src/app/projects/projects-routing.module.ts create mode 100644 src/app/projects/projects.module.ts create mode 100644 src/app/projects/projects.service.spec.ts create mode 100644 src/app/projects/projects.service.ts create mode 100644 src/app/shared/models/project.model.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 65dc93b..63bd139 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -8,8 +8,8 @@ import { Pages } from './shared/models/pages.model'; const routes: Routes = [ { path: Pages.HOME, component: HomeComponent }, - { path: Pages.PROJECTS, component: ProjectsComponent }, - // { path: AppRoutes.CORE, loadChildren: () => import('./galler/gallery.module').then((m) => m.GalleryModule) }, // for future use + { path: Pages.PROJECTS, loadChildren: () => import('./projects/projects.module').then((m) => m.ProjectsModule) }, + // { path: AppRoutes.CORE, loadChildren: () => import('./galler/gallery.module').then((m) => m.GalleryModule) }, // for future use maybe { path: Pages.LINKS, component: LinksComponent }, { path: Pages.PET, component: PetComponent }, { path: '**', redirectTo: Pages.HOME, pathMatch: 'full' }]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dfadf08..ab0f8ae 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,8 +9,8 @@ import { FooterComponent } from './footer/footer.component'; import { SharedModule } from './shared/shared.module'; import { HomeComponent } from './home/home.component'; import { LinksComponent } from './links/links.component'; -import { ProjectsComponent } from './projects/projects.component'; import { PetComponent } from './pet/pet.component'; +import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ @@ -20,13 +20,13 @@ import { PetComponent } from './pet/pet.component'; FooterComponent, HomeComponent, LinksComponent, - ProjectsComponent, PetComponent ], imports: [ SharedModule, BrowserModule, - AppRoutingModule + AppRoutingModule, + HttpClientModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/projects/project-card/project-card.component.html b/src/app/projects/project-card/project-card.component.html new file mode 100644 index 0000000..98ff4ff --- /dev/null +++ b/src/app/projects/project-card/project-card.component.html @@ -0,0 +1 @@ +<p>project-card works!</p> diff --git a/src/app/projects/project-card/project-card.component.scss b/src/app/projects/project-card/project-card.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/projects/project-card/project-card.component.spec.ts b/src/app/projects/project-card/project-card.component.spec.ts new file mode 100644 index 0000000..8815d7d --- /dev/null +++ b/src/app/projects/project-card/project-card.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProjectCardComponent } from './project-card.component'; + +describe('ProjectCardComponent', () => { + let component: ProjectCardComponent; + let fixture: ComponentFixture<ProjectCardComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProjectCardComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ProjectCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/projects/project-card/project-card.component.ts b/src/app/projects/project-card/project-card.component.ts new file mode 100644 index 0000000..06ce8d9 --- /dev/null +++ b/src/app/projects/project-card/project-card.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-project-card', + templateUrl: './project-card.component.html', + styleUrls: ['./project-card.component.scss'] +}) +export class ProjectCardComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/projects/project/project.component.html b/src/app/projects/project/project.component.html new file mode 100644 index 0000000..8ea00ea --- /dev/null +++ b/src/app/projects/project/project.component.html @@ -0,0 +1 @@ +<p>project works!</p> diff --git a/src/app/projects/project/project.component.scss b/src/app/projects/project/project.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/projects/project/project.component.spec.ts b/src/app/projects/project/project.component.spec.ts new file mode 100644 index 0000000..e0f0d74 --- /dev/null +++ b/src/app/projects/project/project.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProjectComponent } from './project.component'; + +describe('ProjectComponent', () => { + let component: ProjectComponent; + let fixture: ComponentFixture<ProjectComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProjectComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ProjectComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/projects/project/project.component.ts b/src/app/projects/project/project.component.ts new file mode 100644 index 0000000..e4e21ec --- /dev/null +++ b/src/app/projects/project/project.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-project', + templateUrl: './project.component.html', + styleUrls: ['./project.component.scss'] +}) +export class ProjectComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/projects/projects-routing.module.ts b/src/app/projects/projects-routing.module.ts new file mode 100644 index 0000000..74cf277 --- /dev/null +++ b/src/app/projects/projects-routing.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { ProjectComponent } from './project/project.component'; +import { ProjectsComponent } from './projects.component'; + +const routes: Routes = [ + { + path: '', + component: ProjectsComponent + }, + { + path: ':id', + component: ProjectComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ProjectsRoutingModule {} diff --git a/src/app/projects/projects.component.html b/src/app/projects/projects.component.html index 885859a..b8607a9 100644 --- a/src/app/projects/projects.component.html +++ b/src/app/projects/projects.component.html @@ -1 +1,5 @@ -<p>projects works!</p> +<h2>Projects on git</h2> +<p>These are my projects as can be found on my <a href="https://gitlab.inkletblot.com/explore">gitlab</a>. Mainly my programming projects that are quality enough for me to be happy for the world to see them. There are more in the works but they are currently private.</p> + +<h2>Projects of a personal nature</h2> +<p>This is (will be) a summary of my personal projects be they my homelab, general electronics projects, or anything else.</p> \ No newline at end of file diff --git a/src/app/projects/projects.component.ts b/src/app/projects/projects.component.ts index 1172d9c..17af914 100644 --- a/src/app/projects/projects.component.ts +++ b/src/app/projects/projects.component.ts @@ -1,18 +1,37 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Subject } from 'rxjs/internal/Subject'; +import { takeUntil } from 'rxjs/operators'; import { NavService } from '../nav/nav.service'; import { Pages } from '../shared/models/pages.model'; +import { Project } from '../shared/models/project.model'; +import { ProjectsService } from './projects.service'; @Component({ selector: 'app-projects', templateUrl: './projects.component.html', styleUrls: ['./projects.component.scss'] }) -export class ProjectsComponent implements OnInit { +export class ProjectsComponent implements OnInit, OnDestroy { + _unsubscribe$: Subject<boolean> = new Subject(); - constructor(private navService:NavService) { } + uncategorizedProjects: Array<Project>; + + constructor(private navService: NavService, private projectsService: ProjectsService) { } ngOnInit(): void { this.navService.setPageTitle(Pages.PROJECTS); + + this.projectsService.allProjects$ + .pipe(takeUntil(this._unsubscribe$)) + .subscribe((result: Project[]) => { + this.uncategorizedProjects = result; + }); + this.projectsService.getInvoicesToApprove(); + } + + ngOnDestroy(): void { + this._unsubscribe$.next(false); + this._unsubscribe$.complete(); } } diff --git a/src/app/projects/projects.module.ts b/src/app/projects/projects.module.ts new file mode 100644 index 0000000..bd6f109 --- /dev/null +++ b/src/app/projects/projects.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ProjectCardComponent } from './project-card/project-card.component'; +import { ProjectComponent } from './project/project.component'; +import { ProjectsComponent } from './projects.component'; +import { ProjectsRoutingModule } from './projects-routing.module'; + +@NgModule({ + declarations: [ProjectsComponent, ProjectCardComponent, ProjectComponent], + imports: [ + CommonModule, + ProjectsRoutingModule + ], + providers: [] +}) +export class ProjectsModule { } diff --git a/src/app/projects/projects.service.spec.ts b/src/app/projects/projects.service.spec.ts new file mode 100644 index 0000000..96c6dcc --- /dev/null +++ b/src/app/projects/projects.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ProjectsService } from './projects.service'; + +describe('ProjectsService', () => { + let service: ProjectsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ProjectsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/projects/projects.service.ts b/src/app/projects/projects.service.ts new file mode 100644 index 0000000..384962d --- /dev/null +++ b/src/app/projects/projects.service.ts @@ -0,0 +1,60 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { Project } from '../shared/models/project.model'; + +@Injectable({ + providedIn: 'root' +}) +export class ProjectsService { + + private _allProjects = new BehaviorSubject<Array<Project>>([]); + readonly allProjects$ = this._allProjects.asObservable(); + + private set allProjects(projects: Array<Project>) { + this._allProjects.next(projects); + } + private get allProjects(): Array<Project> { + return this._allProjects.getValue(); + } + + private _apiErrored = new BehaviorSubject<boolean>(false); + readonly apiErrored$ = this._apiErrored.asObservable(); + + private set apiErrored(errored: boolean) { + this._apiErrored.next(errored); + } + private get apiErrored(): boolean { + return this._apiErrored.getValue(); + } + + /** + * I am actively avoiding any kind of environment management, this is a very basic site and currently this is the only api call. + */ + + private PROJECT_API_URL = "https://cms.inkletblot.com/" + + constructor(private http: HttpClient) { } + + httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + + getInvoicesToApprove(): void { + this.http + .get(`${this.PROJECT_API_URL}`, this.httpOptions) + .subscribe( + (response: Project[]) => { + if (response) { + this.allProjects = response; + // filter out different categories here currently only one + } + }, + (error) => { + this.apiErrored = true; + } + ); + } +} diff --git a/src/app/shared/models/project.model.ts b/src/app/shared/models/project.model.ts new file mode 100644 index 0000000..f5b9aab --- /dev/null +++ b/src/app/shared/models/project.model.ts @@ -0,0 +1,6 @@ +export interface Project { + title: string, + description: string, + ct: string, + body: string +} \ No newline at end of file -- GitLab From 036f48b3f08ad9991e963d0101e13c2170bac69a Mon Sep 17 00:00:00 2001 From: Solomon Laing <solomonlaing@pm.me> Date: Tue, 20 Apr 2021 19:30:30 +0930 Subject: [PATCH 2/5] Got api request working. I added the necessary header to the api server and then changed the content type of the request to text/plain to disabled the preflight OPTIONS request which the server was erroring on. Not sure about the security of all this but it's a very basic website and I don't think it'll be an issue. --- src/app/projects/projects.service.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/app/projects/projects.service.ts b/src/app/projects/projects.service.ts index 384962d..a0e5e72 100644 --- a/src/app/projects/projects.service.ts +++ b/src/app/projects/projects.service.ts @@ -32,13 +32,13 @@ export class ProjectsService { * I am actively avoiding any kind of environment management, this is a very basic site and currently this is the only api call. */ - private PROJECT_API_URL = "https://cms.inkletblot.com/" + private PROJECT_API_URL = "https://cms.inkletblot.com/api/json" constructor(private http: HttpClient) { } httpOptions = { headers: new HttpHeaders({ - 'Content-Type': 'application/json' + 'Content-Type': 'text/plain' }) }; @@ -48,7 +48,16 @@ export class ProjectsService { .subscribe( (response: Project[]) => { if (response) { - this.allProjects = response; + // using text/plain so as to not get the preflight options request + // as such : Project[] does not work as expected and objects must be parsed. + for (let project of response) { + this.allProjects.push({ + title : project.title, + description : project.description, + ct : project.ct, + body : project.body + }); + } // filter out different categories here currently only one } }, -- GitLab From 17e74a18a35e6940e537ea47f74d9bbb2d933b9c Mon Sep 17 00:00:00 2001 From: Solomon Laing <solomonlaing@pm.me> Date: Mon, 26 Apr 2021 20:05:45 +0930 Subject: [PATCH 3/5] got basic implementation of project cards and the suppporting services/routing working. need to style project page now and will have first pass done --- src/app/app-routing.module.ts | 2 + src/app/app.module.ts | 4 +- src/app/page404/page404.component.html | 2 + src/app/page404/page404.component.scss | 5 +++ src/app/page404/page404.component.spec.ts | 25 +++++++++++ src/app/page404/page404.component.ts | 15 +++++++ .../project-card/project-card.component.html | 5 ++- .../project-card/project-card.component.scss | 22 ++++++++++ .../project-card/project-card.component.ts | 6 ++- .../projects/project/project.component.html | 1 + src/app/projects/project/project.component.ts | 43 +++++++++++++++++-- src/app/projects/projects.component.html | 5 ++- src/app/projects/projects.service.ts | 42 +++++++++++++++++- src/app/shared/models/pages.model.ts | 3 +- src/app/shared/models/project.model.ts | 1 + 15 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 src/app/page404/page404.component.html create mode 100644 src/app/page404/page404.component.scss create mode 100644 src/app/page404/page404.component.spec.ts create mode 100644 src/app/page404/page404.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 63bd139..6001582 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { LinksComponent } from './links/links.component'; +import { Page404Component } from './page404/page404.component'; import { PetComponent } from './pet/pet.component'; import { ProjectsComponent } from './projects/projects.component'; import { Pages } from './shared/models/pages.model'; @@ -12,6 +13,7 @@ const routes: Routes = [ // { path: AppRoutes.CORE, loadChildren: () => import('./galler/gallery.module').then((m) => m.GalleryModule) }, // for future use maybe { path: Pages.LINKS, component: LinksComponent }, { path: Pages.PET, component: PetComponent }, + { path: Pages.PAGE_404, component: Page404Component }, { path: '**', redirectTo: Pages.HOME, pathMatch: 'full' }]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ab0f8ae..1464527 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -11,6 +11,7 @@ import { HomeComponent } from './home/home.component'; import { LinksComponent } from './links/links.component'; import { PetComponent } from './pet/pet.component'; import { HttpClientModule } from '@angular/common/http'; +import { Page404Component } from './page404/page404.component'; @NgModule({ declarations: [ @@ -20,7 +21,8 @@ import { HttpClientModule } from '@angular/common/http'; FooterComponent, HomeComponent, LinksComponent, - PetComponent + PetComponent, + Page404Component ], imports: [ SharedModule, diff --git a/src/app/page404/page404.component.html b/src/app/page404/page404.component.html new file mode 100644 index 0000000..e5abac8 --- /dev/null +++ b/src/app/page404/page404.component.html @@ -0,0 +1,2 @@ +<h1>Page Not Found?</h1> +<h3>Sorry.</h3> \ No newline at end of file diff --git a/src/app/page404/page404.component.scss b/src/app/page404/page404.component.scss new file mode 100644 index 0000000..e026e1c --- /dev/null +++ b/src/app/page404/page404.component.scss @@ -0,0 +1,5 @@ +:host { + display: flex; + flex-direction: column; + text-align: center; +} \ No newline at end of file diff --git a/src/app/page404/page404.component.spec.ts b/src/app/page404/page404.component.spec.ts new file mode 100644 index 0000000..38b4072 --- /dev/null +++ b/src/app/page404/page404.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Page404Component } from './page404.component'; + +describe('Page404Component', () => { + let component: Page404Component; + let fixture: ComponentFixture<Page404Component>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Page404Component ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Page404Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/page404/page404.component.ts b/src/app/page404/page404.component.ts new file mode 100644 index 0000000..ca07c7a --- /dev/null +++ b/src/app/page404/page404.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-page404', + templateUrl: './page404.component.html', + styleUrls: ['./page404.component.scss'] +}) +export class Page404Component implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/projects/project-card/project-card.component.html b/src/app/projects/project-card/project-card.component.html index 98ff4ff..6cc1921 100644 --- a/src/app/projects/project-card/project-card.component.html +++ b/src/app/projects/project-card/project-card.component.html @@ -1 +1,4 @@ -<p>project-card works!</p> +<a routerLink="{{project.slug}}"> + <h2>{{project.title}}</h2> + <p>{{project.description}}</p> +</a> \ No newline at end of file diff --git a/src/app/projects/project-card/project-card.component.scss b/src/app/projects/project-card/project-card.component.scss index e69de29..d902910 100644 --- a/src/app/projects/project-card/project-card.component.scss +++ b/src/app/projects/project-card/project-card.component.scss @@ -0,0 +1,22 @@ +@use 'src/app/shared/styles/_variables.color' as colors; + +:host { + flex-direction: column; + display: flex; + background-color: colors.$inklets-color-bg; + margin-bottom: 20px; + + a { + color: colors.$inklets-color-white; + border: colors.$inklets-color-highlight-bg 2px dashed; + background-color: colors.$inklets-color-fg; + text-decoration: none; + padding: 0 20px 0 20px; + } + + a:hover { + background-color: colors.$inklets-color-highlight-fg; + color: colors.$inklets-color-black; + } + +} \ No newline at end of file diff --git a/src/app/projects/project-card/project-card.component.ts b/src/app/projects/project-card/project-card.component.ts index 06ce8d9..fcc7491 100644 --- a/src/app/projects/project-card/project-card.component.ts +++ b/src/app/projects/project-card/project-card.component.ts @@ -1,4 +1,5 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { Project } from 'src/app/shared/models/project.model'; @Component({ selector: 'app-project-card', @@ -7,6 +8,9 @@ import { Component, OnInit } from '@angular/core'; }) export class ProjectCardComponent implements OnInit { + @Input() + project: Project; + constructor() { } ngOnInit(): void { diff --git a/src/app/projects/project/project.component.html b/src/app/projects/project/project.component.html index 8ea00ea..dc66179 100644 --- a/src/app/projects/project/project.component.html +++ b/src/app/projects/project/project.component.html @@ -1 +1,2 @@ <p>project works!</p> +{{project.title}} diff --git a/src/app/projects/project/project.component.ts b/src/app/projects/project/project.component.ts index e4e21ec..ce93a3e 100644 --- a/src/app/projects/project/project.component.ts +++ b/src/app/projects/project/project.component.ts @@ -1,15 +1,52 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { Project } from 'src/app/shared/models/project.model'; +import { ProjectsService } from '../projects.service'; @Component({ selector: 'app-project', templateUrl: './project.component.html', styleUrls: ['./project.component.scss'] }) -export class ProjectComponent implements OnInit { +export class ProjectComponent implements OnInit, OnDestroy { + _unsubscribe$: Subject<boolean> = new Subject(); - constructor() { } + project: Project = { + title: '', + description: '', + slug: '', + ct: '', + body: '', + }; + + constructor( + private route: ActivatedRoute, + private projectsService: ProjectsService + ) { } ngOnInit(): void { + let id: string; + this.route.paramMap.subscribe((params) => { + id = params.get('id'); + }); + + this.projectsService.getProject(id); + + this.projectsService.currentProject$ + .pipe(takeUntil(this._unsubscribe$)) + .subscribe((result: Project) => { + if (!!result) { + this.project = result; + } + }); + + } + + ngOnDestroy(): void { + this._unsubscribe$.next(false); + this._unsubscribe$.complete(); } } diff --git a/src/app/projects/projects.component.html b/src/app/projects/projects.component.html index b8607a9..4a04f43 100644 --- a/src/app/projects/projects.component.html +++ b/src/app/projects/projects.component.html @@ -2,4 +2,7 @@ <p>These are my projects as can be found on my <a href="https://gitlab.inkletblot.com/explore">gitlab</a>. Mainly my programming projects that are quality enough for me to be happy for the world to see them. There are more in the works but they are currently private.</p> <h2>Projects of a personal nature</h2> -<p>This is (will be) a summary of my personal projects be they my homelab, general electronics projects, or anything else.</p> \ No newline at end of file +<p>This is (will be) a summary of my personal projects be they my homelab, general electronics projects, or anything else.</p> + +<app-project-card *ngFor="let project of uncategorizedProjects" [project]="project"> +</app-project-card> \ No newline at end of file diff --git a/src/app/projects/projects.service.ts b/src/app/projects/projects.service.ts index a0e5e72..fa29dfb 100644 --- a/src/app/projects/projects.service.ts +++ b/src/app/projects/projects.service.ts @@ -1,5 +1,6 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; import { Project } from '../shared/models/project.model'; @@ -18,6 +19,16 @@ export class ProjectsService { return this._allProjects.getValue(); } + private _currentProject = new BehaviorSubject<Project>(null); + readonly currentProject$ = this._currentProject.asObservable(); + + private set currentProject(project: Project) { + this._currentProject.next(project); + } + private get currentProject(): Project { + return this._currentProject.getValue(); + } + private _apiErrored = new BehaviorSubject<boolean>(false); readonly apiErrored$ = this._apiErrored.asObservable(); @@ -34,7 +45,7 @@ export class ProjectsService { private PROJECT_API_URL = "https://cms.inkletblot.com/api/json" - constructor(private http: HttpClient) { } + constructor(private http: HttpClient, private router: Router) { } httpOptions = { headers: new HttpHeaders({ @@ -50,11 +61,13 @@ export class ProjectsService { if (response) { // using text/plain so as to not get the preflight options request // as such : Project[] does not work as expected and objects must be parsed. + this.allProjects = [] for (let project of response) { this.allProjects.push({ title : project.title, description : project.description, ct : project.ct, + slug : this.generateSlug(project.title), body : project.body }); } @@ -66,4 +79,31 @@ export class ProjectsService { } ); } + + /** + * This is not very efficient code but I'm not planning on having that many projects and (if this turns into a blog) blog posts + * If this does become a general blog, then I will make this a bit more efficient. + * @param id slug of project to get + */ + getProject(id: string): void { + let found = false; + this.allProjects.forEach((project) => { + if (project.slug === id) { + found = true; + this.currentProject = project; + } + }) + if (!found) { + this.router.navigate(['not-found']) + } + } + + /** + * Currently this is very basic and assumes that generated slug will be unique, it may be updated in future. + * @param input string to use for generating the slug + * @returns a slug representation of the input string + */ + generateSlug(input: string): string { + return input.toLocaleLowerCase().split(" ").join("-"); + } } diff --git a/src/app/shared/models/pages.model.ts b/src/app/shared/models/pages.model.ts index da77482..0619761 100644 --- a/src/app/shared/models/pages.model.ts +++ b/src/app/shared/models/pages.model.ts @@ -6,5 +6,6 @@ export enum Pages { PROJECTS = 'projects', GALLERY = 'gallery', LINKS = 'links', - PET = 'pet' + PET = 'pet', + PAGE_404 = 'not-found' } \ No newline at end of file diff --git a/src/app/shared/models/project.model.ts b/src/app/shared/models/project.model.ts index f5b9aab..28dec90 100644 --- a/src/app/shared/models/project.model.ts +++ b/src/app/shared/models/project.model.ts @@ -2,5 +2,6 @@ export interface Project { title: string, description: string, ct: string, + slug: string, body: string } \ No newline at end of file -- GitLab From 5d754396da0c4e79ced37356da7b3a637f061e25 Mon Sep 17 00:00:00 2001 From: Solomon Laing <solomonlaing@pm.me> Date: Sun, 6 Jun 2021 12:07:29 +0930 Subject: [PATCH 4/5] updated code (started to) for new api, npm install is busted due to out of date deps I think, moving to different computer --- src/app/projects/projects.service.ts | 22 ++-------------------- src/app/shared/models/project.model.ts | 7 +++---- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/app/projects/projects.service.ts b/src/app/projects/projects.service.ts index fa29dfb..c77cffc 100644 --- a/src/app/projects/projects.service.ts +++ b/src/app/projects/projects.service.ts @@ -43,7 +43,7 @@ export class ProjectsService { * I am actively avoiding any kind of environment management, this is a very basic site and currently this is the only api call. */ - private PROJECT_API_URL = "https://cms.inkletblot.com/api/json" + private PROJECT_API_URL = "https://cms.inkletblot.com/" constructor(private http: HttpClient, private router: Router) { } @@ -61,16 +61,7 @@ export class ProjectsService { if (response) { // using text/plain so as to not get the preflight options request // as such : Project[] does not work as expected and objects must be parsed. - this.allProjects = [] - for (let project of response) { - this.allProjects.push({ - title : project.title, - description : project.description, - ct : project.ct, - slug : this.generateSlug(project.title), - body : project.body - }); - } + this.allProjects = response; // filter out different categories here currently only one } }, @@ -97,13 +88,4 @@ export class ProjectsService { this.router.navigate(['not-found']) } } - - /** - * Currently this is very basic and assumes that generated slug will be unique, it may be updated in future. - * @param input string to use for generating the slug - * @returns a slug representation of the input string - */ - generateSlug(input: string): string { - return input.toLocaleLowerCase().split(" ").join("-"); - } } diff --git a/src/app/shared/models/project.model.ts b/src/app/shared/models/project.model.ts index 28dec90..196064d 100644 --- a/src/app/shared/models/project.model.ts +++ b/src/app/shared/models/project.model.ts @@ -1,7 +1,6 @@ export interface Project { - title: string, - description: string, - ct: string, slug: string, - body: string + date: string, + category: string, + content: string } \ No newline at end of file -- GitLab From ac430a46d1edc4f0be24d0f53f4f0a2b307e786e Mon Sep 17 00:00:00 2001 From: Solomon Laing <solomonlaing@pm.me> Date: Sun, 6 Jun 2021 13:27:11 +0930 Subject: [PATCH 5/5] first release of projects ready --- .../project-card/project-card.component.html | 3 +- .../project-card/project-card.component.scss | 2 +- .../projects/project/project.component.html | 3 +- .../projects/project/project.component.scss | 10 +++ src/app/projects/project/project.component.ts | 11 +-- src/app/projects/projects.component.ts | 2 +- src/app/projects/projects.service.ts | 27 +++++- src/app/shared/models/project.model.ts | 1 + src/styles.scss | 90 +++++++++++++++++++ 9 files changed, 134 insertions(+), 15 deletions(-) diff --git a/src/app/projects/project-card/project-card.component.html b/src/app/projects/project-card/project-card.component.html index 6cc1921..b257702 100644 --- a/src/app/projects/project-card/project-card.component.html +++ b/src/app/projects/project-card/project-card.component.html @@ -1,4 +1,3 @@ <a routerLink="{{project.slug}}"> - <h2>{{project.title}}</h2> - <p>{{project.description}}</p> + {{project.title + " - " + project.date}} </a> \ No newline at end of file diff --git a/src/app/projects/project-card/project-card.component.scss b/src/app/projects/project-card/project-card.component.scss index d902910..11d9562 100644 --- a/src/app/projects/project-card/project-card.component.scss +++ b/src/app/projects/project-card/project-card.component.scss @@ -11,7 +11,7 @@ border: colors.$inklets-color-highlight-bg 2px dashed; background-color: colors.$inklets-color-fg; text-decoration: none; - padding: 0 20px 0 20px; + padding: 5px 20px 5px 20px; } a:hover { diff --git a/src/app/projects/project/project.component.html b/src/app/projects/project/project.component.html index dc66179..c6545ee 100644 --- a/src/app/projects/project/project.component.html +++ b/src/app/projects/project/project.component.html @@ -1,2 +1 @@ -<p>project works!</p> -{{project.title}} +<span [innerHTML]="project.content"></span> diff --git a/src/app/projects/project/project.component.scss b/src/app/projects/project/project.component.scss index e69de29..7bfa459 100644 --- a/src/app/projects/project/project.component.scss +++ b/src/app/projects/project/project.component.scss @@ -0,0 +1,10 @@ +@use 'src/app/shared/styles/_variables.color' as colors; + +:host { + display: flex; + flex-direction: column; + flex-grow: 1; + overflow-y: auto; + padding: 0 20px 0 20px; + margin: 0 140px 10px 140px; +} \ No newline at end of file diff --git a/src/app/projects/project/project.component.ts b/src/app/projects/project/project.component.ts index ce93a3e..9325d51 100644 --- a/src/app/projects/project/project.component.ts +++ b/src/app/projects/project/project.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -8,17 +8,18 @@ import { ProjectsService } from '../projects.service'; @Component({ selector: 'app-project', templateUrl: './project.component.html', + // encapsulation: ViewEncapsulation.None, styleUrls: ['./project.component.scss'] }) export class ProjectComponent implements OnInit, OnDestroy { _unsubscribe$: Subject<boolean> = new Subject(); project: Project = { - title: '', - description: '', slug: '', - ct: '', - body: '', + title: '', + date: '', + category: '', + content: '', }; constructor( diff --git a/src/app/projects/projects.component.ts b/src/app/projects/projects.component.ts index 17af914..6da1e8b 100644 --- a/src/app/projects/projects.component.ts +++ b/src/app/projects/projects.component.ts @@ -26,7 +26,7 @@ export class ProjectsComponent implements OnInit, OnDestroy { .subscribe((result: Project[]) => { this.uncategorizedProjects = result; }); - this.projectsService.getInvoicesToApprove(); + this.projectsService.getProjects(); } ngOnDestroy(): void { diff --git a/src/app/projects/projects.service.ts b/src/app/projects/projects.service.ts index c77cffc..fc374fb 100644 --- a/src/app/projects/projects.service.ts +++ b/src/app/projects/projects.service.ts @@ -43,7 +43,7 @@ export class ProjectsService { * I am actively avoiding any kind of environment management, this is a very basic site and currently this is the only api call. */ - private PROJECT_API_URL = "https://cms.inkletblot.com/" + private PROJECT_API_URL = "https://cms.inkletblot.com" constructor(private http: HttpClient, private router: Router) { } @@ -53,9 +53,9 @@ export class ProjectsService { }) }; - getInvoicesToApprove(): void { + getProjects(): void { this.http - .get(`${this.PROJECT_API_URL}`, this.httpOptions) + .get(`${this.PROJECT_API_URL}/posts`, this.httpOptions) .subscribe( (response: Project[]) => { if (response) { @@ -71,6 +71,25 @@ export class ProjectsService { ); } + getSingleProject(id: string): void { + this.http + .get(`${this.PROJECT_API_URL}/post/${id}`, this.httpOptions) + .subscribe( + (response: Project) => { + if (response) { + // using text/plain so as to not get the preflight options request + // as such : Project[] does not work as expected and objects must be parsed. + this.currentProject = response; + // filter out different categories here currently only one + } + }, + (error) => { + this.apiErrored = true; + this.router.navigate(['not-found']) + } + ); + } + /** * This is not very efficient code but I'm not planning on having that many projects and (if this turns into a blog) blog posts * If this does become a general blog, then I will make this a bit more efficient. @@ -85,7 +104,7 @@ export class ProjectsService { } }) if (!found) { - this.router.navigate(['not-found']) + this.getSingleProject(id) } } } diff --git a/src/app/shared/models/project.model.ts b/src/app/shared/models/project.model.ts index 196064d..522b454 100644 --- a/src/app/shared/models/project.model.ts +++ b/src/app/shared/models/project.model.ts @@ -1,5 +1,6 @@ export interface Project { slug: string, + title: string, date: string, category: string, content: string diff --git a/src/styles.scss b/src/styles.scss index 9e71b2c..e5950b8 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -10,4 +10,94 @@ body { font-family: Roboto, 'Helvetica Neue', sans-serif; background-color: colors.$inklets-color-black; color: colors.$inklets-color-white; +} + +/* + + Codehilite styles for api content + +*/ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #979797; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #979797; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.codehilite .hll { background-color: #ffffcc } +.codehilite { background: #000000; padding: 5px; } +.codehilite .c { color: #408080; font-style: italic } /* Comment */ +.codehilite .err { border: 1px solid #FF0000 } /* Error */ +.codehilite .k { color: #008000; font-weight: bold } /* Keyword */ +.codehilite .o { color: #666666 } /* Operator */ +.codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */ +.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */ +.codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */ +.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */ +.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */ +.codehilite .gd { color: #A00000 } /* Generic.Deleted */ +.codehilite .ge { font-style: italic } /* Generic.Emph */ +.codehilite .gr { color: #FF0000 } /* Generic.Error */ +.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.codehilite .gi { color: #00A000 } /* Generic.Inserted */ +.codehilite .go { color: #888888 } /* Generic.Output */ +.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.codehilite .gs { font-weight: bold } /* Generic.Strong */ +.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.codehilite .gt { color: #0044DD } /* Generic.Traceback */ +.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.codehilite .kp { color: #008000 } /* Keyword.Pseudo */ +.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.codehilite .kt { color: #B00040 } /* Keyword.Type */ +.codehilite .m { color: #666666 } /* Literal.Number */ +.codehilite .s { color: #BA2121 } /* Literal.String */ +.codehilite .na { color: #7D9029 } /* Name.Attribute */ +.codehilite .nb { color: #008000 } /* Name.Builtin */ +.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.codehilite .no { color: #880000 } /* Name.Constant */ +.codehilite .nd { color: #AA22FF } /* Name.Decorator */ +.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.codehilite .nf { color: #0000FF } /* Name.Function */ +.codehilite .nl { color: #A0A000 } /* Name.Label */ +.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.codehilite .nv { color: #19177C } /* Name.Variable */ +.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.codehilite .w { color: #bbbbbb } /* Text.Whitespace */ +.codehilite .mb { color: #666666 } /* Literal.Number.Bin */ +.codehilite .mf { color: #666666 } /* Literal.Number.Float */ +.codehilite .mh { color: #666666 } /* Literal.Number.Hex */ +.codehilite .mi { color: #666666 } /* Literal.Number.Integer */ +.codehilite .mo { color: #666666 } /* Literal.Number.Oct */ +.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */ +.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */ +.codehilite .sc { color: #BA2121 } /* Literal.String.Char */ +.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */ +.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.codehilite .sx { color: #008000 } /* Literal.String.Other */ +.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */ +.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */ +.codehilite .ss { color: #19177C } /* Literal.String.Symbol */ +.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.codehilite .fm { color: #0000FF } /* Name.Function.Magic */ +.codehilite .vc { color: #19177C } /* Name.Variable.Class */ +.codehilite .vg { color: #19177C } /* Name.Variable.Global */ +.codehilite .vi { color: #19177C } /* Name.Variable.Instance */ +.codehilite .vm { color: #19177C } /* Name.Variable.Magic */ +.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ + + +* a { + text-decoration: none; + color: colors.$inklets-color-fg; +} + +* a:hover { + color: colors.$inklets-color-highlight-fg; } \ No newline at end of file -- GitLab