Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 21 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,42 @@
# Portfolio

This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.19.
Angular 22 frontend with an Express backend for the contact form.

## Development server
## Frontend

To start a local development server, run:
Install dependencies and start the development server:

```bash
ng serve
npm install
npm start
```

Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
The frontend is available at `http://localhost:4200/` and proxies `/api` requests to
`http://localhost:3000`.

## Code scaffolding

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:

```bash
ng generate component component-name
```

For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
Available checks:

```bash
ng generate --help
npm run build
npm test -- --watch=false
npm run lint
```

## Building
## Backend

To build the project run:
Create `server/.env` from `server/.env.example`, then install dependencies and start the
development server:

```bash
ng build
cd server
npm install
npm run dev
```

This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.

## Running unit tests

To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
Available checks:

```bash
ng test
npm run build
npm test
npm run lint
```

## Running end-to-end tests

For end-to-end (e2e) testing, run:

```bash
ng e2e
```

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.

## Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
8 changes: 6 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const angular = require('angular-eslint');

module.exports = defineConfig([
{
files: ['**/*.ts'],
files: ['src/**/*.ts'],
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
Expand Down Expand Up @@ -37,8 +37,12 @@ module.exports = defineConfig([
},
},
{
files: ['**/*.html'],
files: ['src/**/*.html'],
extends: [angular.configs.templateRecommended, angular.configs.templateAccessibility],
rules: {},
},
{
files: ['server/{src,test}/**/*.ts'],
extends: [eslint.configs.recommended, tseslint.configs.recommended],
},
]);
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/contact-footer/contact-stamp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 0 additions & 9 deletions public/assets/contact-footer/contact-stamp.svg

This file was deleted.

Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/assets/project-detail/title-underline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/assets/projects/projects-title-underline.webp
Binary file not shown.
8 changes: 4 additions & 4 deletions public/assets/skills/icons/Next.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
14 changes: 8 additions & 6 deletions public/i18n/de.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"pageTitles": {
"notFound": "Seite nicht gefunden | KamyCoding"
"notFound": "Seite nicht gefunden | KamyCoding",
"legalNotice": "Impressum | KamyCoding",
"privacyPolicy": "Datenschutzerklärung | KamyCoding"
},
"nav": {
"about": "Über mich",
Expand Down Expand Up @@ -47,8 +49,8 @@
"eyebrow": "Wer ist Kamyar?",
"heading": "Über mich",
"paragraphs": {
"introduction": "Hi, ich bin Kamyar, Full-Stack Developer, und verwandle Ideen gerne in barrierearme, wartbare digitale Produkte.",
"values": "Mir sind durchdachte Problemlösungen, sauberer Code und Anwendungen wichtig, die echten Menschen einen Mehrwert bieten."
"introduction": "Ich bin Kamyar und arbeite als Full-Stack-Entwickler in der Nähe von Köln. Durch meinen Design-Hintergrund betrachte ich Software nicht nur aus technischer Sicht, sondern auch aus der Perspektive der Menschen, die sie nutzen.",
"values": "Mein besonderes Interesse gilt moderner Softwarearchitektur und sinnvollen Einsatzmöglichkeiten von KI in realen Anwendungen."
},
"cta": "Lass uns sprechen",
"availabilityAria": "Verfügbarkeit",
Expand All @@ -61,7 +63,7 @@
"skills": {
"eyebrow": "Mein Stack",
"heading": "Skillset",
"description": "Ich entwickle wartbare Webanwendungen mit Angular und TypeScript und erweitere meinen Stack durch praktische Projekte, durchdachte Problemlösungen und kontinuierliches Lernen.",
"description": "Ich arbeite mit Angular, React, TypeScript und Node.js und entwickle damit moderne, wartbare Webanwendungen. Dabei arbeite ich gerne im gesamten Stack, löse praxisnahe Probleme und erweitere meine Fähigkeiten kontinuierlich durch reale Projekte.",
"listAria": "Technologien und Werkzeuge",
"peel": {
"revealAria": "Technologien anzeigen, die ich lernen möchte",
Expand Down Expand Up @@ -135,8 +137,8 @@
"eyebrow": "Kontakt",
"heading": "Bereit für eine\nZusammenarbeit?",
"paragraphs": {
"projects": "Ich interessiere mich für durchdachte Projekte, bei denen saubere Entwicklung, verlässliche Zusammenarbeit und eine starke User Experience zählen.",
"invitation": "Wenn du ein Projekt, eine Position oder eine Idee hast, bei der meine Full-Stack-Erfahrung helfen kann, freue ich mich darauf, mehr darüber zu erfahren."
"projects": "Ich bin immer offen für spannende Projekte, neue Möglichkeiten und Ideen, bei denen gute technische Umsetzung und eine starke User Experience zusammenkommen.",
"invitation": "Wenn du denkst, dass meine Full-Stack-Erfahrung gut zu deinem Projekt oder Team passt, freue ich mich, von dir zu hören."
},
"fields": {
"name": {
Expand Down
14 changes: 8 additions & 6 deletions public/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"pageTitles": {
"notFound": "Page Not Found | KamyCoding"
"notFound": "Page Not Found | KamyCoding",
"legalNotice": "Legal Notice | KamyCoding",
"privacyPolicy": "Privacy Policy | KamyCoding"
},
"nav": {
"about": "About me",
Expand Down Expand Up @@ -47,8 +49,8 @@
"eyebrow": "Who's Kamyar?",
"heading": "About me",
"paragraphs": {
"introduction": "Hi, I’m Kamyar, a full-stack developer who enjoys turning ideas into accessible, maintainable digital products.",
"values": "I care about thoughtful problem-solving, clean code, and building experiences that are useful for real people."
"introduction": "I’m Kamyar, a full-stack developer based near Cologne. With a background in design, I approach software from both a technical and a user perspective.",
"values": "I’m interested in building products that are not only well engineered, but also intuitive to use. My main interests are modern software architecture and practical ways to integrate AI into real-world applications."
},
"cta": "Let’s talk",
"availabilityAria": "Work availability",
Expand All @@ -61,7 +63,7 @@
"skills": {
"eyebrow": "My stack",
"heading": "Skill set",
"description": "I build maintainable web applications with Angular and TypeScript, and I enjoy expanding my stack through hands-on projects, thoughtful problem-solving, and continuous learning.",
"description": "I work with Angular, React, TypeScript, and Node.js to build modern, maintainable web applications. I enjoy working across the stack, solving real-world problems, and continuously improving through hands-on projects.",
"listAria": "Technologies and tools",
"peel": {
"revealAria": "Reveal technologies I am interested in learning",
Expand Down Expand Up @@ -135,8 +137,8 @@
"eyebrow": "Contact me",
"heading": "Ready to work\ntogether?",
"paragraphs": {
"projects": "I’m interested in thoughtful projects where clean engineering, reliable collaboration, and a strong user experience matter.",
"invitation": "If you have a project, role, or idea where my full-stack experience could help, I’d be happy to hear more about it."
"projects": "I’m always open to thoughtful projects, new opportunities, and ideas where good engineering and strong user experience come together.",
"invitation": "If you think my full-stack experience could be a good fit, I’d be happy to hear from you."
},
"fields": {
"name": {
Expand Down
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "",
"scripts": {
"lint": "npm --prefix .. exec -- eslint \"{src,test}/**/*.ts\"",
"test": "tsx --test test/contact.integration.test.ts",
"dev": "tsx watch src/index.ts",
"build": "tsc",
Expand Down
2 changes: 0 additions & 2 deletions server/src/routes/contact.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,3 @@ export function createContactRouter(emailSender: ContactEmailSender = sendContac

return router;
}

export const contactRouter = createContactRouter();
2 changes: 0 additions & 2 deletions server/src/validators/contact.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,3 @@ export const contactRequestSchema = z
company: z.string().max(200).optional().default(''),
})
.strict();

export type ContactRequest = z.infer<typeof contactRequestSchema>;
4 changes: 2 additions & 2 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export const routes: Routes = [
{
path: 'legal-notice',
component: LegalNotice,
title: 'Legal Notice | KamyCoding',
data: { titleKey: 'pageTitles.legalNotice' },
},
{
path: 'privacy-policy',
component: PrivacyPolicy,
title: 'Privacy Policy | KamyCoding',
data: { titleKey: 'pageTitles.privacyPolicy' },
},
{
path: 'projects/:slug',
Expand Down
2 changes: 2 additions & 0 deletions src/app/features/projects/projects.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export const PORTFOLIO_PROJECTS = [
implementationDetailsKey: 'projects.items.elPolloLoco.implementation',
durationKey: 'projects.items.elPolloLoco.duration',
technologies: [TECHNOLOGIES.javascript, TECHNOLOGIES.html, TECHNOLOGIES.css],
githubUrl: 'https://github.com/kamycoding/El-Pollo-Loco',
liveUrl: 'https://kamycoding.github.io/El-Pollo-Loco/',
variant: 'standard',
},
{
Expand Down
3 changes: 2 additions & 1 deletion src/app/layout/footer/footer.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
.site-footer__inner {
display: grid;
width: 100%;
max-width: 1200px;
max-width: 42rem;
margin-inline: auto;
align-items: start;
row-gap: 12px;
Expand Down Expand Up @@ -167,6 +167,7 @@
}

.site-footer__inner {
max-width: 1200px;
min-height: 132px;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto 1fr;
Expand Down
14 changes: 7 additions & 7 deletions src/app/layout/header/header.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
}

.site-header {
border-bottom: 1px solid transparent;
background-color: transparent;
box-shadow: none;
backdrop-filter: blur(0) saturate(100%);
-webkit-backdrop-filter: blur(0) saturate(100%);
transition:
background-color 260ms ease-out,
border-color 260ms ease-out,
box-shadow 260ms ease-out,
backdrop-filter 260ms ease-out;
}

.site-header--scrolled {
border-bottom-color: color-mix(in srgb, var(--color-foreground) 12%, transparent);
background-color: color-mix(in srgb, var(--color-canvas) 68%, transparent);
box-shadow: 0 10px 32px rgba(0, 0, 0, 0.18);
backdrop-filter: blur(16px) saturate(130%);
-webkit-backdrop-filter: blur(16px) saturate(130%);
background-color: color-mix(in srgb, var(--color-canvas) 64%, transparent);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.16);
backdrop-filter: blur(20px) saturate(150%);
-webkit-backdrop-filter: blur(20px) saturate(150%);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
Expand Down
4 changes: 2 additions & 2 deletions src/app/layout/header/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
></span>

<header
class="site-header fixed inset-x-0 top-0 z-50 flex h-16 items-center bg-[linear-gradient(to_bottom,rgba(0,0,0,0.3)_0%,rgba(0,0,0,0)_70%)] px-4 lg:h-20 lg:px-18"
class="site-header fixed inset-x-0 top-0 z-50 flex h-16 items-center px-4 lg:h-20 lg:px-18"
[class.site-header--scrolled]="isScrolled()"
>
<!-- Mobile header -->
Expand Down Expand Up @@ -49,7 +49,7 @@

<!-- Desktop header -->
<div
class="mx-auto hidden h-10.5 w-full max-w-324 grid-cols-[auto_repeat(4,auto)_auto] items-center justify-between lg:grid"
class="site-header__desktop mx-auto hidden h-10.5 w-full max-w-324 grid-cols-[auto_repeat(4,auto)_auto] items-center justify-between lg:grid"
>
<!-- Logo -->
<a
Expand Down
29 changes: 23 additions & 6 deletions src/app/pages/home/sections/about/about.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@

.about__cta {
--cta-font-size: 16px;

width: 110px;
--cta-min-width: 110px;
}

@media (min-width: 22.5rem) {
Expand All @@ -76,8 +75,7 @@

.about__cta {
--cta-font-size: 18px;

width: 126px;
--cta-min-width: 126px;
}
}

Expand All @@ -90,11 +88,30 @@
transform: translate(-50%, -50%) rotate(4deg);
}

@media (min-width: 40rem) and (max-width: 63.999rem) {
.availability-list {
max-width: 44rem;
margin-inline: auto;
}

.availability-card__content {
gap: clamp(16px, 2.5vw, 24px);
}

.availability-card__content img {
width: clamp(40px, 5vw, 48px);
height: clamp(40px, 5vw, 48px);
}

.availability-card__content span {
font-size: clamp(20px, 2.75vw, 28px);
}
}

@media (min-width: 64rem) {
.about__cta {
--cta-font-size: 20px;

width: 156px;
--cta-min-width: 156px;
}

.availability-card[data-variant='yellow'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
min-height: 900px;
overflow: hidden;
background-color: var(--color-foreground);
background-image: url('/assets/contact-footer/contact-footer-texture.png');
background-image: url('/assets/contact-footer/contact-footer-texture.webp');
background-repeat: no-repeat;
background-position: top center;
background-size: cover;
Expand Down
4 changes: 0 additions & 4 deletions src/app/pages/home/sections/contact/contact.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
display: none;
}

.contact__arrow--mobile {
width: 100%;
}

.contact-field {
min-height: 114px;
padding: 12px 24px 8px;
Expand Down
Loading