diff --git a/src/app/certifications/certifications.component.css b/src/app/certifications/certifications.component.css new file mode 100644 index 0000000..6cbd774 --- /dev/null +++ b/src/app/certifications/certifications.component.css @@ -0,0 +1,47 @@ +img { + width: 100%; +} + +.desc { + max-width: 1200px; + margin: auto; + font-size: 1.5rem; +} + +.certs { + display: grid; + grid-template-columns: repeat(3, 1fr); + row-gap: 10px; + width: 100%; + max-width: 1200px; + justify-items: center; + margin: auto; +} + +.cert { + width: 300px; +} + +.title { + font-size: 1.5rem; + font-weight: bold; + height: 4rem; + border: 2px solid var(--text); +} + +.issued { + font-size: 1.25rem; + border: 2px solid var(--text); +} + +@media screen and (max-width: 950px) { + .certs { + grid-template-columns: repeat(2, 1fr); + } +} + +@media screen and (max-width: 650px) { + .certs { + grid-template-columns: repeat(1, 1fr); + } +} diff --git a/src/app/certifications/certifications.component.html b/src/app/certifications/certifications.component.html new file mode 100644 index 0000000..534b525 --- /dev/null +++ b/src/app/certifications/certifications.component.html @@ -0,0 +1,19 @@ +

Certifications

+

+ All of the professional certifications I have obtained throughout my journey. +

+
+
+

{{ cert.name }}

+ +

+ {{ cert.issuer }} - + {{ + cert.date.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + }) + }} +

+
+
diff --git a/src/app/certifications/certifications.component.spec.ts b/src/app/certifications/certifications.component.spec.ts new file mode 100644 index 0000000..32e882e --- /dev/null +++ b/src/app/certifications/certifications.component.spec.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { Certifications } from "../config/Certifications"; + +import { CertificationsComponent } from "./certifications.component"; + +describe("CertificationsComponent", () => { + let component: CertificationsComponent; + let fixture: ComponentFixture; + let compiled: HTMLElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CertificationsComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(CertificationsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + compiled = fixture.nativeElement; + }); + + it("should load the certifications", () => { + expect(component.certs).toEqual( + Certifications.sort((a, b) => b.date.getTime() - a.date.getTime()) + ); + }); + + it("should render the certifications correctly", () => { + const sorted = Certifications.sort( + (a, b) => b.date.getTime() - a.date.getTime() + ); + const certs = compiled.querySelectorAll(".cert"); + for (let i = 0; i < certs.length; i++) { + const cert = certs[i]; + const title = cert.querySelector(".title"); + expect(title?.textContent?.trim()).toBe(sorted[i].name); + const img = cert.querySelector("img"); + expect(img?.src).toContain(`/assets/img/certs/${sorted[i].fileName}`); + const issued = cert.querySelector(".issued"); + expect(issued?.textContent?.trim()).toBe( + `${sorted[i].issuer} - ${sorted[i].date.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })}` + ); + } + }); +}); diff --git a/src/app/certifications/certifications.component.ts b/src/app/certifications/certifications.component.ts new file mode 100644 index 0000000..f3b8d60 --- /dev/null +++ b/src/app/certifications/certifications.component.ts @@ -0,0 +1,20 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; + +import { Certifications } from "../config/Certifications"; + +/** + * + */ +@Component({ + selector: "app-certifications", + standalone: true, + imports: [CommonModule], + templateUrl: "./certifications.component.html", + styleUrl: "./certifications.component.css" +}) +export class CertificationsComponent { + public certs = Certifications.sort( + (a, b) => b.date.getTime() - a.date.getTime() + ); +} diff --git a/src/app/config/Certifications.ts b/src/app/config/Certifications.ts new file mode 100644 index 0000000..20858bf --- /dev/null +++ b/src/app/config/Certifications.ts @@ -0,0 +1,97 @@ +export const Certifications: { + name: string; + fileName: string; + issuer: string; + date: Date; +}[] = [ + { + name: "Responsive Web Design", + fileName: "responsive-web-design.png", + issuer: "freeCodeCamp", + date: new Date("April 17 2020") + }, + { + name: "JavaScript Algorithms and Data Structures", + fileName: "javascript.png", + issuer: "freeCodeCamp", + date: new Date("April 24, 2020") + }, + { + name: "Front End Libraries", + fileName: "front-end-libs.png", + issuer: "freeCodeCamp", + date: new Date("April 28, 2020") + }, + { + name: "Information Security and Quality Assurance", + fileName: "infosec.png", + issuer: "freeCodeCamp", + date: new Date("May 19, 2020") + }, + { + name: "Full Stack Developer", + fileName: "legacy-full-stack.png", + issuer: "freeCodeCamp", + date: new Date("May 25, 2020") + }, + { + name: "Data Visualisation", + fileName: "data-visualisation.png", + issuer: "freeCodeCamp", + date: new Date("May 25, 2020") + }, + { + name: "Back End Development and APIs", + fileName: "back-end-dev.png", + issuer: "freeCodeCamp", + date: new Date("May 8 2020") + }, + { + name: "Quality Assurance", + fileName: "quality-assurance.png", + issuer: "freeCodeCamp", + date: new Date("July 3 2020") + }, + { + name: "Legacy Front End", + fileName: "legacy-front-end.png", + issuer: "freeCodeCamp", + date: new Date("August 14 2020") + }, + { + name: "Scientific Computing with Python", + fileName: "scientific-computing.png", + issuer: "freeCodeCamp", + date: new Date("July 3, 2020") + }, + { + name: "Node.js Developer", + fileName: "mongo-nodejs.jpg", + issuer: "MongoDB", + date: new Date("May 9 2024") + }, + { + name: "Self-Managed Database Admin", + fileName: "mongo-self-admin.jpg", + issuer: "MongoDB", + date: new Date("May 9 2024") + }, + { + name: "Data Modelling", + fileName: "mongo-data-model.jpg", + issuer: "MongoDB", + date: new Date("May 9 2024") + }, + { + name: "Atlas Database Admin", + fileName: "mongo-atlas-admin.jpg", + issuer: "MongoDB", + date: new Date("May 9 2024") + }, + { + name: "E-Commerce Modernisation and Personalisation", + fileName: "ecommerce-retail.jpg", + issuer: "MongoDB", + date: new Date("May 9 2024") + } +]; diff --git a/src/app/config/Employment.ts b/src/app/config/Employment.ts index 41b1e02..81a11d3 100644 --- a/src/app/config/Employment.ts +++ b/src/app/config/Employment.ts @@ -17,7 +17,7 @@ export const Employment: { /** * File name of logo. */ - logo?: string; + logo: string; }[] = [ { title: "Consultant", @@ -65,6 +65,7 @@ Additionally, I guided and mentored other developers in the design and coding of end: null, link: "https://discord.gg/kYpjgEB", type: "volunteer", + logo: "angel.png", description: `As a Discord Moderator at AngelRose, I played a key role in maintaining a safe, respectful, and engaging environment for community members. I monitored conversations, enforcing community guidelines to prevent disruptive behavior and protect users. Additionally, I addressed and resolved conflicts, providing support and guidance to members to promote a positive experience. My contributions helped uphold the integrity of the community and foster an inclusive and welcoming space for all participants.` }, { @@ -90,6 +91,7 @@ Through these innovative features, my work has improved community engagement and end: new Date("January 5 2024"), link: "https://linktr.ee/bigbadbeaver", type: "project", + logo: "beaver.png", description: `As a Twitch Integration Engineer at Big Bad Beaver Productions, I developed "PrivateTwigs," a custom Twitch chat bot that enhanced stream management and engagement. The bot includes comprehensive logging for all chat messages and stream events, enabling detailed analysis and strategic insights. Additionally, I implemented custom redemption rewards within the bot, providing viewers with unique opportunities to interact with the stream and engage with content. My work as a Twitch Integration Engineer significantly improved the streaming experience for both streamers and viewers, contributing to a more interactive and dynamic community.` @@ -115,6 +117,7 @@ Moreover, I developed a Discord bot to query projects listed on the Open Source end: new Date("November 5 2022"), link: "https://discord.com/invite/ns5x8bTz25", type: "fixed", + logo: "4c.png", description: `As Community Manager at 4C, I played a pivotal role in shaping and nurturing the Discord community, driving significant growth and member engagement. Under my management, the community expanded from 1,100 to 3,350 members, fostering an active and thriving space for all participants. My contributions have been instrumental in creating a welcoming and dynamic environment, promoting meaningful interactions and enhancing overall community satisfaction.` }, { @@ -124,6 +127,7 @@ Moreover, I developed a Discord bot to query projects listed on the Open Source end: new Date("May 5 2023"), link: "https://tweetshift.com", type: "fixed", + logo: "tweetshift.png", description: `As the Community Manager for the TweetShift Discord bot, I provided comprehensive support to a community that utilizes the bot across over 230,000 servers. My focus was on delivering high-quality assistance and ensuring the bot's seamless functionality for users. I also played a key role in moderating the community, maintaining a safe and inclusive environment for all members. Through these efforts, I have fostered a welcoming space and contributed to the overall success and engagement of the TweetShift community.` }, { @@ -171,6 +175,7 @@ Additionally, I contributed to driving engagement for the Summer League 2021 and end: null, link: "https://discord.gg/infinite", type: "volunteer", + logo: "caylus.png", description: `As the Discord Administrator and Platform Engineering Manager at Caylus Crew, I developed custom bots that enhanced the community experience and streamlined operations. One such bot posted daily messages wishing happy birthday to members, fostering a sense of connection and camaraderie. I also created a bot to manage sponsor perks, ensuring efficient and accurate distribution of benefits to eligible members. In addition to technical contributions, I coached and trained moderators, providing quarterly staff reviews to support their professional growth and improve their performance. My work played a pivotal role in maintaining a vibrant, well-managed community and ensuring an exceptional experience for all participants.` @@ -182,6 +187,7 @@ In addition to technical contributions, I coached and trained moderators, provid end: new Date("July 5 2023"), link: "http://discord.gg/U3jQVYNbJt", type: "volunteer", + logo: "xcentric.jpg", description: `As an Integrations Engineer at Xcentric Collective, I developed a custom Discord bot that incorporated a unique Matchmaking Rating (MMR) system to calculate Rocket League proficiency. This system enabled users to track and monitor their skill levels accurately. The bot also featured the ability to define teams and find matches with opponents of similar skill levels, facilitating balanced and fair gameplay. Additionally, the bot allowed users to schedule matches seamlessly, providing a streamlined and efficient experience for the community. My work enhanced the overall gaming experience and engagement within the Xcentric Collective community.As an Integrations Engineer at Xcentric Collective, I developed a custom Discord bot that incorporated a unique Matchmaking Rating (MMR) system to calculate Rocket League proficiency. This system enabled users to track and monitor their skill levels accurately. The bot also featured the ability to define teams and find matches with opponents of similar skill levels, facilitating balanced and fair gameplay. Additionally, the bot allowed users to schedule matches seamlessly, providing a streamlined and efficient experience for the community. My work enhanced the overall gaming experience and engagement within the Xcentric Collective community.` @@ -241,6 +247,7 @@ To enhance community security, I built a robust verification system that signifi end: null, link: "https://discord.com/invite/GDYNGnrGUs", type: "volunteer", + logo: "troopy.png", description: `As a Discord Moderator for Virtual Insanity, an adult-only community, I was instrumental in cultivating a secure, respectful, and interactive atmosphere for our diverse members. My responsibilities encompassed vigilant monitoring of discussions, ensuring compliance with established guidelines to deter any disruptive conduct and safeguard our users. Alongside conflict resolution, I provided empathetic support and guidance, fostering a harmonious environment conducive to positive interactions. Moreover, I actively assisted in verifying identification documents, reinforcing our commitment to maintaining a safe and authentic community experience. Through these efforts, I contributed to upholding the community's integrity and nurturing an inclusive space for all participants to thrive.` }, { @@ -250,6 +257,7 @@ To enhance community security, I built a robust verification system that signifi end: new Date("April 5 2024"), link: "https://discord.com/invite/XNSy8PMvyy", type: "volunteer", + logo: "azuliah.jpg", description: `As a Discord Administrator and Integrations Engineer at Azuliah, I established custom integrations to streamline moderation efforts and enhance community management. These integrations supported the moderation team in maintaining a safe and welcoming space for all members. In addition to technical contributions, I trained the owner and moderation team on best practices for running a successful community. My guidance included techniques for efficient moderation, conflict resolution, and fostering positive interactions among members. Through my efforts, I played a key role in ensuring the community's smooth operation and promoting a vibrant, supportive environment.` @@ -261,6 +269,7 @@ In addition to technical contributions, I trained the owner and moderation team end: new Date("Jan 5 2024"), link: "https://discord.com", type: "volunteer", + logo: "rion.jpg", description: `As a Discord Moderator for Rion Kuroko, I constructed the server almost entirely from scratch to tailor it to the specific needs of the community. This involved designing and implementing structures, channels, and rules that supported a smooth and organized environment. I also provided guidance to the owner on the technical aspects of Discord moderation, sharing best practices and offering solutions to effectively manage the server. My efforts played a key role in establishing a functional, user-friendly community space and empowering the owner with the knowledge needed to maintain and grow the server.` @@ -284,6 +293,7 @@ Additionally, I guided and supported moderators, helping them develop their skil end: new Date("Jan 5 2022"), link: "https://discord.com/invite/zdfQhjc", type: "volunteer", + logo: "tweetshift.png", description: `As Technical Support Staff for TweetShift, I responded promptly to user queries, addressing issues and bugs within the bot to ensure a smooth user experience. I provided clear guidance on how to use the bot's features effectively, helping users maximize its capabilities. My role involved troubleshooting technical challenges and offering solutions to enhance user satisfaction. By delivering efficient support and sharing helpful tips, I contributed to the overall success and reliability of the bot for the TweetShift community.` @@ -295,6 +305,7 @@ My role involved troubleshooting technical challenges and offering solutions to end: new Date("Feb 5 2022"), link: "https://discord.com/invite/rythm", type: "volunteer", + logo: "rythm.jpeg", description: `As a Discord Moderator for Rythm, I played a central role in maintaining a safe, respectful, and welcoming environment for community members. I monitored conversations to ensure compliance with community guidelines and addressed any disruptive behavior promptly. My efforts helped foster a positive and inclusive space for all members, contributing to the overall health and vibrancy of the Rythm community.` @@ -322,5 +333,15 @@ Acting as a liaison between users and developers, I facilitated clear communicat description: `As a Community Moderator for freeCodeCamp, I provided vital support to users (campers) as they navigated the freeCodeCamp curriculum. My role included assisting users in debugging their code and answering questions, ensuring they received the guidance they needed to progress in their learning journey. I engaged with and moderated the community on the forum and Discord server, fostering a positive, supportive, and inclusive environment for all members. Additionally, I assisted with issue triage and pull request review on GitHub, contributing to the ongoing improvement and development of freeCodeCamp's open-source projects. My efforts played a key role in maintaining the quality of the community and empowering users to achieve their learning goals.` + }, + { + title: "Community Moderator", + company: "FruitPursuits", + start: new Date("March 5 2024"), + end: null, + link: "https://discord.gg/xcy2fRsC5K", + type: "volunteer", + logo: "fruit.png", + description: `As a Discord Moderator for FruitPursuits, I provide crucial support to members, ensuring they have a seamless experience within our community. I troubleshoot technical issues, enforce community guidelines, and foster engagement among members. Collaborating with fellow moderators, I contribute to the ongoing improvement of our Discord server, empowering users to fully enjoy their fruit-loving journey.` } ]; diff --git a/src/app/config/Stats.ts b/src/app/config/Stats.ts new file mode 100644 index 0000000..4e26a80 --- /dev/null +++ b/src/app/config/Stats.ts @@ -0,0 +1,77 @@ +import { Certifications } from "./Certifications"; +import { Employment } from "./Employment"; +import { Testimonials } from "./Testimonials"; + +const getTimeBetween = (start: Date, end: Date) => { + const months = + (end.getFullYear() - start.getFullYear()) * 12 - + start.getMonth() + + end.getMonth(); + return Math.floor(months / 12) > 0 + ? `${Math.floor(months / 12)} years, ${months % 12} months` + : `${months} months`; +}; + +export const Stats: { + title: string; + value: string | number; +}[] = [ + { + title: "Current Positions Held", + value: Employment.filter((el) => !el.end).length + }, + { + title: "Total Positions Held", + value: Employment.length + }, + { + title: "Paid Positions Held", + value: Employment.filter( + (el) => el.type !== "hypothetical" && el.type !== "volunteer" + ).length + }, + { + title: "Pro-Bono Positons Held", + value: Employment.filter((el) => el.type === "volunteer").length + }, + { + title: "Current Longest Position Held", + value: (() => { + const longest = Employment.filter( + (el) => !el.end && el.type !== "hypothetical" + ).sort( + (a, b) => + (b.end ?? new Date(Date.now())).getTime() - + b.start.getTime() - + ((a.end ?? new Date(Date.now())).getTime() - a.start.getTime()) + )[0]; + return `${longest.title} - ${getTimeBetween(longest.start, longest.end ?? new Date(Date.now()))}`; + })() + }, + { + title: "All-time Longest Position Held", + value: (() => { + const longest = Employment.filter( + (el) => el.type !== "hypothetical" + ).sort( + (a, b) => + (b.end ?? new Date(Date.now())).getTime() - + b.start.getTime() - + ((a.end ?? new Date(Date.now())).getTime() - a.start.getTime()) + )[0]; + return `${longest.title} - ${getTimeBetween(longest.start, longest.end ?? new Date(Date.now()))}`; + })() + }, + { + title: "Clients Served", + value: new Set(Employment.map((el) => el.company)).size + }, + { + title: "Reviews Received", + value: Testimonials.length + }, + { + title: "Certifications Earned", + value: Certifications.length + } +]; diff --git a/src/app/home/home.component.css b/src/app/home/home.component.css index 28bfce4..ed33adc 100644 --- a/src/app/home/home.component.css +++ b/src/app/home/home.component.css @@ -32,6 +32,7 @@ button:disabled { display: flex; justify-content: space-evenly; margin: auto; + flex-wrap: wrap; } .smaller { diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 75d8d0e..c7dc3e4 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -43,4 +43,6 @@
+ +
diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 5b48c40..3b1c0c5 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,11 +1,13 @@ import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; +import { CertificationsComponent } from "../certifications/certifications.component"; import { Logos } from "../config/Logos"; import { Socials } from "../config/Socials"; import { JobComponent } from "../job/job.component"; import { LogoComponent } from "../logo/logo.component"; import { SocialComponent } from "../social/social.component"; +import { StatsComponent } from "../stats/stats.component"; import { TestimonialsComponent } from "../testimonials/testimonials.component"; import { TimelineComponent } from "../timeline/timeline.component"; @@ -21,7 +23,9 @@ import { TimelineComponent } from "../timeline/timeline.component"; JobComponent, LogoComponent, TimelineComponent, - TestimonialsComponent + TestimonialsComponent, + CertificationsComponent, + StatsComponent ], templateUrl: "./home.component.html", styleUrl: "./home.component.css" @@ -30,7 +34,7 @@ export class HomeComponent { public socials = Socials; public logos = Logos; public view: "resume" | "testimonials" | "certifications" | "stats" = - "testimonials"; + "resume"; /** * @param {string} view The view to load. diff --git a/src/app/job/job.component.spec.ts b/src/app/job/job.component.spec.ts index ff93def..6142d7d 100644 --- a/src/app/job/job.component.spec.ts +++ b/src/app/job/job.component.spec.ts @@ -42,8 +42,17 @@ describe("JobComponent", () => { expect(component.description).toEqual(expected.description.split(/\n+/)); expect(component.date).toBe( expected.end - ? `${expected.start.toLocaleDateString()} - ${expected.end.toLocaleDateString()}` - : expected.start.toLocaleDateString() + ? `${expected.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })} - ${expected.end.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })}` + : expected.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + }) ); }); @@ -94,8 +103,17 @@ describe("JobComponent", () => { const date = compiled.querySelector(".date"); expect(date?.textContent?.trim()).toBe( expected.end - ? `${expected.start.toLocaleDateString()} - ${expected.end.toLocaleDateString()}` - : expected.start.toLocaleDateString() + ? `${expected.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })} - ${expected.end.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })}` + : expected.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + }) ); /* Test the stuff that's hidden */ @@ -159,8 +177,17 @@ describe("JobComponent", () => { const date = compiled.querySelector(".date"); expect(date?.textContent?.trim()).toBe( expected.end - ? `${expected.start.toLocaleDateString()} - ${expected.end.toLocaleDateString()}` - : expected.start.toLocaleDateString() + ? `${expected.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })} - ${expected.end.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })}` + : expected.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + }) ); /* Hidden stuff should now be visible */ diff --git a/src/app/job/job.component.ts b/src/app/job/job.component.ts index 6b8c2bd..5b0777d 100644 --- a/src/app/job/job.component.ts +++ b/src/app/job/job.component.ts @@ -35,11 +35,20 @@ export class JobComponent implements OnInit { this.iconDescription = JobTypeDescriptions[this.job.type]; if (this.job.end) { this.type = "former"; - this.date = `${this.job.start.toLocaleDateString()} - ${this.job.end.toLocaleDateString()}`; + this.date = `${this.job.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })} - ${this.job.end.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + })}`; return; } this.type = this.job.type === "hypothetical" ? "hypothetical" : "current"; - this.date = this.job.start.toLocaleDateString(); + this.date = this.job.start.toLocaleDateString("en-GB", { + month: "long", + year: "numeric" + }); } /** */ diff --git a/src/app/stats/stats.component.css b/src/app/stats/stats.component.css new file mode 100644 index 0000000..413e764 --- /dev/null +++ b/src/app/stats/stats.component.css @@ -0,0 +1,8 @@ +p { + max-width: 1200px; + margin: auto; +} + +.bold { + font-weight: bold; +} diff --git a/src/app/stats/stats.component.html b/src/app/stats/stats.component.html new file mode 100644 index 0000000..fd401df --- /dev/null +++ b/src/app/stats/stats.component.html @@ -0,0 +1,5 @@ +

Profile Stats

+

+ {{ stat.title }}: {{ stat.value }} +

diff --git a/src/app/stats/stats.component.spec.ts b/src/app/stats/stats.component.spec.ts new file mode 100644 index 0000000..41632e3 --- /dev/null +++ b/src/app/stats/stats.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { StatsComponent } from "./stats.component"; + +describe("StatsComponent", () => { + let component: StatsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [StatsComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(StatsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/stats/stats.component.ts b/src/app/stats/stats.component.ts new file mode 100644 index 0000000..80182ec --- /dev/null +++ b/src/app/stats/stats.component.ts @@ -0,0 +1,18 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; + +import { Stats } from "../config/Stats"; + +/** + * + */ +@Component({ + selector: "app-stats", + standalone: true, + imports: [CommonModule], + templateUrl: "./stats.component.html", + styleUrl: "./stats.component.css" +}) +export class StatsComponent { + public stats = Stats; +} diff --git a/src/app/timeline/timeline.component.html b/src/app/timeline/timeline.component.html index c63527b..7106b2f 100644 --- a/src/app/timeline/timeline.component.html +++ b/src/app/timeline/timeline.component.html @@ -1,4 +1,4 @@ -

All Work

+

Resume

Here you can see a timeline of all of my client work. Items without an end date (green items) are roles I am still in. diff --git a/src/assets/img/certs/back-end-dev.png b/src/assets/img/certs/back-end-dev.png new file mode 100644 index 0000000..80130c3 Binary files /dev/null and b/src/assets/img/certs/back-end-dev.png differ diff --git a/src/assets/img/certs/data-visualisation.png b/src/assets/img/certs/data-visualisation.png new file mode 100644 index 0000000..9e5e3da Binary files /dev/null and b/src/assets/img/certs/data-visualisation.png differ diff --git a/src/assets/img/certs/ecommerce-retail.jpg b/src/assets/img/certs/ecommerce-retail.jpg new file mode 100644 index 0000000..20f8a38 Binary files /dev/null and b/src/assets/img/certs/ecommerce-retail.jpg differ diff --git a/src/assets/img/certs/front-end-libs.png b/src/assets/img/certs/front-end-libs.png new file mode 100644 index 0000000..afda9d1 Binary files /dev/null and b/src/assets/img/certs/front-end-libs.png differ diff --git a/src/assets/img/certs/infosec.png b/src/assets/img/certs/infosec.png new file mode 100644 index 0000000..4abc0a4 Binary files /dev/null and b/src/assets/img/certs/infosec.png differ diff --git a/src/assets/img/certs/javascript.png b/src/assets/img/certs/javascript.png new file mode 100644 index 0000000..a1bcfd4 Binary files /dev/null and b/src/assets/img/certs/javascript.png differ diff --git a/src/assets/img/certs/legacy-front-end.png b/src/assets/img/certs/legacy-front-end.png new file mode 100644 index 0000000..189cbd2 Binary files /dev/null and b/src/assets/img/certs/legacy-front-end.png differ diff --git a/src/assets/img/certs/legacy-full-stack.png b/src/assets/img/certs/legacy-full-stack.png new file mode 100644 index 0000000..a8cde7b Binary files /dev/null and b/src/assets/img/certs/legacy-full-stack.png differ diff --git a/src/assets/img/certs/mongo-atlas-admin.jpg b/src/assets/img/certs/mongo-atlas-admin.jpg new file mode 100644 index 0000000..a336dca Binary files /dev/null and b/src/assets/img/certs/mongo-atlas-admin.jpg differ diff --git a/src/assets/img/certs/mongo-data-model.jpg b/src/assets/img/certs/mongo-data-model.jpg new file mode 100644 index 0000000..9f16ae1 Binary files /dev/null and b/src/assets/img/certs/mongo-data-model.jpg differ diff --git a/src/assets/img/certs/mongo-nodejs.jpg b/src/assets/img/certs/mongo-nodejs.jpg new file mode 100644 index 0000000..717255f Binary files /dev/null and b/src/assets/img/certs/mongo-nodejs.jpg differ diff --git a/src/assets/img/certs/mongo-self-admin.jpg b/src/assets/img/certs/mongo-self-admin.jpg new file mode 100644 index 0000000..65645fc Binary files /dev/null and b/src/assets/img/certs/mongo-self-admin.jpg differ diff --git a/src/assets/img/certs/quality-assurance.png b/src/assets/img/certs/quality-assurance.png new file mode 100644 index 0000000..a9ef862 Binary files /dev/null and b/src/assets/img/certs/quality-assurance.png differ diff --git a/src/assets/img/certs/responsive-web-design.png b/src/assets/img/certs/responsive-web-design.png new file mode 100644 index 0000000..9fcb3b9 Binary files /dev/null and b/src/assets/img/certs/responsive-web-design.png differ diff --git a/src/assets/img/certs/scientific-computing.png b/src/assets/img/certs/scientific-computing.png new file mode 100644 index 0000000..5afb079 Binary files /dev/null and b/src/assets/img/certs/scientific-computing.png differ diff --git a/src/assets/img/icons/4c.png b/src/assets/img/icons/4c.png new file mode 100644 index 0000000..11dcee1 Binary files /dev/null and b/src/assets/img/icons/4c.png differ diff --git a/src/assets/img/icons/angel.png b/src/assets/img/icons/angel.png new file mode 100644 index 0000000..02b27f5 Binary files /dev/null and b/src/assets/img/icons/angel.png differ diff --git a/src/assets/img/icons/azuliah.jpg b/src/assets/img/icons/azuliah.jpg new file mode 100644 index 0000000..2eb83fb Binary files /dev/null and b/src/assets/img/icons/azuliah.jpg differ diff --git a/src/assets/img/icons/beaver.png b/src/assets/img/icons/beaver.png new file mode 100644 index 0000000..4c0b329 Binary files /dev/null and b/src/assets/img/icons/beaver.png differ diff --git a/src/assets/img/icons/caylus.png b/src/assets/img/icons/caylus.png new file mode 100644 index 0000000..15ec5e4 Binary files /dev/null and b/src/assets/img/icons/caylus.png differ diff --git a/src/assets/img/icons/fruit.png b/src/assets/img/icons/fruit.png new file mode 100644 index 0000000..a97e20c Binary files /dev/null and b/src/assets/img/icons/fruit.png differ diff --git a/src/assets/img/icons/rion.jpg b/src/assets/img/icons/rion.jpg new file mode 100644 index 0000000..811d7bd Binary files /dev/null and b/src/assets/img/icons/rion.jpg differ diff --git a/src/assets/img/icons/troopy.png b/src/assets/img/icons/troopy.png new file mode 100644 index 0000000..3c7c325 Binary files /dev/null and b/src/assets/img/icons/troopy.png differ diff --git a/src/assets/img/icons/tweetshift.png b/src/assets/img/icons/tweetshift.png new file mode 100644 index 0000000..d6ce54d Binary files /dev/null and b/src/assets/img/icons/tweetshift.png differ diff --git a/src/assets/img/icons/xcentric.jpg b/src/assets/img/icons/xcentric.jpg new file mode 100644 index 0000000..c3cdc46 Binary files /dev/null and b/src/assets/img/icons/xcentric.jpg differ