diff --git a/README.md b/README.md index dd25fb0..f02f142 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/eslint.config.js b/eslint.config.js index 31d1fa8..7c7f663 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,7 +6,7 @@ const angular = require('angular-eslint'); module.exports = defineConfig([ { - files: ['**/*.ts'], + files: ['src/**/*.ts'], extends: [ eslint.configs.recommended, tseslint.configs.recommended, @@ -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], + }, ]); diff --git a/public/assets/contact-footer/contact-footer-texture.png b/public/assets/contact-footer/contact-footer-texture.png deleted file mode 100644 index c051f3e..0000000 Binary files a/public/assets/contact-footer/contact-footer-texture.png and /dev/null differ diff --git a/public/assets/contact-footer/contact-footer-texture.webp b/public/assets/contact-footer/contact-footer-texture.webp new file mode 100644 index 0000000..79c7d23 Binary files /dev/null and b/public/assets/contact-footer/contact-footer-texture.webp differ diff --git a/public/assets/contact-footer/contact-stamp.png b/public/assets/contact-footer/contact-stamp.png new file mode 100644 index 0000000..568dd01 Binary files /dev/null and b/public/assets/contact-footer/contact-stamp.png differ diff --git a/public/assets/contact-footer/contact-stamp.svg b/public/assets/contact-footer/contact-stamp.svg deleted file mode 100644 index 03b7cda..0000000 --- a/public/assets/contact-footer/contact-stamp.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/public/assets/legal-notice/legal-notice-header-bg.png b/public/assets/legal-notice/legal-notice-header-bg.png deleted file mode 100644 index 8f102fe..0000000 Binary files a/public/assets/legal-notice/legal-notice-header-bg.png and /dev/null differ diff --git a/public/assets/legal-notice/legal-notice-header-bg.webp b/public/assets/legal-notice/legal-notice-header-bg.webp new file mode 100644 index 0000000..7de61a7 Binary files /dev/null and b/public/assets/legal-notice/legal-notice-header-bg.webp differ diff --git a/public/assets/project-detail/project-detail-bg-mobile.png b/public/assets/project-detail/project-detail-bg-mobile.png deleted file mode 100644 index 7f6d380..0000000 Binary files a/public/assets/project-detail/project-detail-bg-mobile.png and /dev/null differ diff --git a/public/assets/project-detail/project-detail-bg-mobile.webp b/public/assets/project-detail/project-detail-bg-mobile.webp new file mode 100644 index 0000000..9144609 Binary files /dev/null and b/public/assets/project-detail/project-detail-bg-mobile.webp differ diff --git a/public/assets/project-detail/project-detail-header-bg..png b/public/assets/project-detail/project-detail-header-bg..png deleted file mode 100644 index e705260..0000000 Binary files a/public/assets/project-detail/project-detail-header-bg..png and /dev/null differ diff --git a/public/assets/project-detail/project-detail-header-bg.webp b/public/assets/project-detail/project-detail-header-bg.webp new file mode 100644 index 0000000..615b6ce Binary files /dev/null and b/public/assets/project-detail/project-detail-header-bg.webp differ diff --git a/public/assets/project-detail/title-underline.svg b/public/assets/project-detail/title-underline.svg index 6de3fea..7ca2628 100644 --- a/public/assets/project-detail/title-underline.svg +++ b/public/assets/project-detail/title-underline.svg @@ -1,4 +1,4 @@ - + diff --git a/public/assets/projects/projects-title-underline.png b/public/assets/projects/projects-title-underline.png new file mode 100644 index 0000000..2d55ae9 Binary files /dev/null and b/public/assets/projects/projects-title-underline.png differ diff --git a/public/assets/projects/projects-title-underline.webp b/public/assets/projects/projects-title-underline.webp deleted file mode 100644 index 37d8434..0000000 Binary files a/public/assets/projects/projects-title-underline.webp and /dev/null differ diff --git a/public/assets/skills/icons/Next.svg b/public/assets/skills/icons/Next.svg index 3a380f8..a6daec2 100644 --- a/public/assets/skills/icons/Next.svg +++ b/public/assets/skills/icons/Next.svg @@ -9,12 +9,12 @@ - - + + - - + + diff --git a/public/assets/testimonials/testimonial-paper-01-mobile.webp b/public/assets/testimonials/testimonial-paper-01-mobile.webp deleted file mode 100644 index 68f94d7..0000000 Binary files a/public/assets/testimonials/testimonial-paper-01-mobile.webp and /dev/null differ diff --git a/public/assets/testimonials/testimonials-title-arrow.png b/public/assets/testimonials/testimonials-title-arrow.png new file mode 100644 index 0000000..d1dc84e Binary files /dev/null and b/public/assets/testimonials/testimonials-title-arrow.png differ diff --git a/public/assets/testimonials/testimonials-title-arrow.webp b/public/assets/testimonials/testimonials-title-arrow.webp deleted file mode 100644 index 4a90a21..0000000 Binary files a/public/assets/testimonials/testimonials-title-arrow.webp and /dev/null differ diff --git a/public/i18n/de.json b/public/i18n/de.json index 5678113..0263d4e 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -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", @@ -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", @@ -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", @@ -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": { diff --git a/public/i18n/en.json b/public/i18n/en.json index 544eb4e..7c2af70 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -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", @@ -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", @@ -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", @@ -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": { diff --git a/server/package.json b/server/package.json index 08e6166..05429f1 100644 --- a/server/package.json +++ b/server/package.json @@ -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", diff --git a/server/src/routes/contact.route.ts b/server/src/routes/contact.route.ts index 3d54092..c9f4f59 100644 --- a/server/src/routes/contact.route.ts +++ b/server/src/routes/contact.route.ts @@ -63,5 +63,3 @@ export function createContactRouter(emailSender: ContactEmailSender = sendContac return router; } - -export const contactRouter = createContactRouter(); diff --git a/server/src/validators/contact.validator.ts b/server/src/validators/contact.validator.ts index f60947d..175968b 100644 --- a/server/src/validators/contact.validator.ts +++ b/server/src/validators/contact.validator.ts @@ -25,5 +25,3 @@ export const contactRequestSchema = z company: z.string().max(200).optional().default(''), }) .strict(); - -export type ContactRequest = z.infer; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 667cfa8..d3fb8c8 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -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', diff --git a/src/app/features/projects/projects.data.ts b/src/app/features/projects/projects.data.ts index edcc9b7..22679b8 100644 --- a/src/app/features/projects/projects.data.ts +++ b/src/app/features/projects/projects.data.ts @@ -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', }, { diff --git a/src/app/layout/footer/footer.css b/src/app/layout/footer/footer.css index 9466893..1bddb7b 100644 --- a/src/app/layout/footer/footer.css +++ b/src/app/layout/footer/footer.css @@ -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; @@ -167,6 +167,7 @@ } .site-footer__inner { + max-width: 1200px; min-height: 132px; grid-template-columns: repeat(4, 1fr); grid-template-rows: auto 1fr; diff --git a/src/app/layout/header/header.css b/src/app/layout/header/header.css index 248e461..29ade53 100644 --- a/src/app/layout/header/header.css +++ b/src/app/layout/header/header.css @@ -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))) { diff --git a/src/app/layout/header/header.html b/src/app/layout/header/header.html index d46c473..dde6a33 100644 --- a/src/app/layout/header/header.html +++ b/src/app/layout/header/header.html @@ -5,7 +5,7 @@ >