From e08c2d40139d91f9cdbccdafa656eb8e5386b17e Mon Sep 17 00:00:00 2001 From: seccodesmith Date: Mon, 26 May 2025 22:42:05 +0200 Subject: [PATCH 1/5] Fix list in project modal --- src/components/ProjectModal.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/ProjectModal.tsx b/src/components/ProjectModal.tsx index 79f3483..d52084b 100644 --- a/src/components/ProjectModal.tsx +++ b/src/components/ProjectModal.tsx @@ -68,14 +68,15 @@ export const ProjectModal: React.FC = ({ project }) => { project.projectDetails?.keyFeatures && project.projectDetails.keyFeatures.length > 0 && ( <>

Key Features

+ ) } -- 2.52.0 From 805bd07eb45b62d6b5ce0152ba7e604a2c5b7a58 Mon Sep 17 00:00:00 2001 From: seccodesmith Date: Mon, 26 May 2025 23:45:39 +0200 Subject: [PATCH 2/5] feat: add contact data and styling for the Contact page --- src/assets/styles/Contact.module.scss | 0 src/data/contactData.ts | 20 ++++ src/pages/Contact.tsx | 133 +++++++++----------------- src/untils/ContactProps.ts | 19 ++++ 4 files changed, 84 insertions(+), 88 deletions(-) create mode 100644 src/assets/styles/Contact.module.scss create mode 100644 src/data/contactData.ts create mode 100644 src/untils/ContactProps.ts diff --git a/src/assets/styles/Contact.module.scss b/src/assets/styles/Contact.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/data/contactData.ts b/src/data/contactData.ts new file mode 100644 index 0000000..f628a00 --- /dev/null +++ b/src/data/contactData.ts @@ -0,0 +1,20 @@ +import type {ContactProps} from '../untils/ContactProps' + +export const contactData: ContactProps = { + email: 'forge@seccodesmith.pl', + businessEmail: 'business@seccodesmith.pl', + socialLinks: [ + { platform: 'LinkedIn', url: 'https://www.linkedin.com/in/jakub-berechowski', icon: 'fab fa-linkedin' }, + { platform: 'GitHub', url: 'https://github.com/seccodesmith', icon: 'fab fa-github' }, + { platform: 'Facebook', url: 'https://www.facebook.com/people/Seccodesmith/61575058536717/', icon: 'fab fa-facebook' }, + ], + questionsAndAnswers: [ + { question: 'What is your typical project timeline?', answer: 'Project timelines vary based on complexity and scope. Small projects typically take 2-4 weeks, medium-sized projects 1-2 months, and larger enterprise solutions 3-6 months. I provide detailed timeline estimates during the initial consultation, and maintain transparent communication about progress throughout the development cycle.' }, + { question: 'How does your collaboration process work?', answer: 'My collaboration process consists of five phases: initial consultation, requirements gathering, design and planning, implementation, and testing/deployment. I use agile methodologies with regular check-ins to ensure your vision is being realized. Communication is maintained through your preferred channels (email, Slack, etc.), and you\'ll have access to development milestones through shared repositories and project management tools.' }, + { question: 'What are your payment terms and methods?', answer: 'I typically work with a milestone-based payment structure: 30% upfront to begin work, 30% at the midpoint after key deliverables are approved, and 40% upon project completion. For longer projects, we can establish additional milestones. I accept bank transfers, PayPal, and cryptocurrency payments. All payment terms are clearly outlined in the project contract before work begins.'}, + { question: 'Who owns the code and deliverables?', answer: 'Upon final payment, you receive full ownership rights to all custom code and deliverables created specifically for your project. I maintain no proprietary claim to custom solutions developed for clients. For projects that incorporate open-source components, those elements remain under their original licenses, and I provide clear documentation of all third-party code used in your project.' }, + { question: 'Do you offer support and maintenance?', answer: 'Yes, I offer a 30-day warranty period for all projects, during which bug fixes and minor adjustments are covered at no additional cost. For ongoing support, I provide maintenance packages that include regular updates, security patches, and priority response times. These can be tailored to your specific needs and budget, whether you require occasional support or dedicated monthly maintenance.'} + ], + MapIframeUrl: 'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d82077.2903513271!2d21.9433946!3d50.0411861!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x473cfae3cc14d449%3A0xd2240d31b33eb2ed!2zUnplc3rDs3c!5e0!3m2!1sen!2spl!4v1651813309336!5m2!1sen!2spl', + phone: '', +}; diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx index ff27463..b888149 100644 --- a/src/pages/Contact.tsx +++ b/src/pages/Contact.tsx @@ -1,14 +1,20 @@ import { PageHeader } from '../components/PageHeader'; import { ContactForm } from '../components/ContactForm'; +import style from '@styles/Contact.module.scss'; + +import { contactData } from '../data/contactData' +import type { ContactProps } from '../untils/ContactProps'; export const Contact = () => { + const data: ContactProps = contactData; + return ( <> - - +
@@ -16,7 +22,7 @@ export const Contact = () => {
- + {/* Contact Info Column */}
{/* Email Info */} @@ -44,7 +50,7 @@ export const Contact = () => {
- + {/* Connect Through Socials */}
@@ -89,7 +95,7 @@ export const Contact = () => {
- + {/* FAQ Section */}
@@ -102,89 +108,40 @@ export const Contact = () => {
- {/* FAQ Item 1 */} -
-

- -

-
-
- Project timelines vary based on complexity and scope. Small projects typically take 2-4 weeks, medium-sized projects 1-2 months, and larger enterprise solutions 3-6 months. I provide detailed timeline estimates during the initial consultation, and maintain transparent communication about progress throughout the development cycle. + { + data.questionsAndAnswers.map((item, index) => ( +
+

+ +

+
+
+ {item.answer} +
+
-
-
- - {/* FAQ Item 2 */} -
-

- -

-
-
- My collaboration process consists of five phases: initial consultation, requirements gathering, design and planning, implementation, and testing/deployment. I use agile methodologies with regular check-ins to ensure your vision is being realized. Communication is maintained through your preferred channels (email, Slack, etc.), and you'll have access to development milestones through shared repositories and project management tools. -
-
-
- - {/* FAQ Item 3 */} -
-

- -

-
-
- I typically work with a milestone-based payment structure: 30% upfront to begin work, 30% at the midpoint after key deliverables are approved, and 40% upon project completion. For longer projects, we can establish additional milestones. I accept bank transfers, PayPal, and cryptocurrency payments. All payment terms are clearly outlined in the project contract before work begins. -
-
-
+ )) + }
- + {/* Map Section */}
@@ -197,11 +154,11 @@ export const Contact = () => {
-
diff --git a/src/untils/ContactProps.ts b/src/untils/ContactProps.ts new file mode 100644 index 0000000..378d549 --- /dev/null +++ b/src/untils/ContactProps.ts @@ -0,0 +1,19 @@ +export interface ContactProps { + email: string; + businessEmail: string; + socialLinks: SocialLink[]; + questionsAndAnswers: QuestionAndAnswer[]; + MapIframeUrl: string; + phone: string; +} + +export interface SocialLink { + platform: string; + url: string; + icon: string; +} + +export interface QuestionAndAnswer { + question: string; + answer: string; +} \ No newline at end of file -- 2.52.0 From 6b9522153f8857952f59ec081c26075c67b4f5c5 Mon Sep 17 00:00:00 2001 From: seccodesmith Date: Tue, 27 May 2025 12:30:22 +0200 Subject: [PATCH 3/5] feat: update Contact page to use dynamic email and social links --- src/assets/styles/_variables.scss | 1 + src/pages/Contact.tsx | 80 +++++++++++++------------------ 2 files changed, 35 insertions(+), 46 deletions(-) diff --git a/src/assets/styles/_variables.scss b/src/assets/styles/_variables.scss index b147616..b2d30d2 100644 --- a/src/assets/styles/_variables.scss +++ b/src/assets/styles/_variables.scss @@ -9,6 +9,7 @@ $text-ash: #aaaaaa; $card-bg: #141414; $card-border: #2a2a2a; $code-bg: #1e1e1e; +$font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; // Effects $glow-effect: 0 0 10px $accent-fire, 0 0 20px rgba(255, 100, 0, 0.4); diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx index b888149..75bcde9 100644 --- a/src/pages/Contact.tsx +++ b/src/pages/Contact.tsx @@ -36,14 +36,14 @@ export const Contact = () => {

For project inquiries or questions:

- - forge@seccodesmtih.com + + {data.email}

For business collaborations:

- - business@seccodesmtih.com + + {data.businessEmail}

@@ -51,53 +51,41 @@ export const Contact = () => {
- {/* Connect Through Socials */} -
-
-
-
- -
-

Connect Through the Ether

-
-

You can also reach me through these digital pathways:

-
- - - GitHub - - - - LinkedIn - - - - Twitter - - - - Discord - - - - Telegram - - - - Stack Overflow - + { + data.socialLinks.length > 0 && ( +
+
+
+
+ +
+

Connect Through the Ether

+
+

You can also reach me through these digital pathways:

+
+ {data.socialLinks.map((link, index) => ( + + + + {link.platform} + + + ))} +
+
-
-
+ ) + } +
-
+ {/* FAQ Section */} -
+ < section className="py-5 bg-dark" >
@@ -140,10 +128,10 @@ export const Contact = () => {
-
+ {/* Map Section */} -
+ < section className="py-5" >
@@ -165,7 +153,7 @@ export const Contact = () => {
-
+ ); }; \ No newline at end of file -- 2.52.0 From cd684dbaf7c1e6464b26e1735a4b78345ed7698f Mon Sep 17 00:00:00 2001 From: seccodesmith Date: Tue, 27 May 2025 13:41:26 +0200 Subject: [PATCH 4/5] feat: implement styling for Contact page and form components --- src/assets/styles/Contact.module.scss | 270 ++++++++++++++++++++++++++ src/assets/styles/FormControl.scss | 95 +++++++++ src/assets/styles/card.scss | 35 ++++ src/components/ContactForm.tsx | 10 +- src/components/styles.css | 135 ------------- src/pages/Contact.tsx | 48 ++--- 6 files changed, 430 insertions(+), 163 deletions(-) create mode 100644 src/assets/styles/FormControl.scss create mode 100644 src/assets/styles/card.scss diff --git a/src/assets/styles/Contact.module.scss b/src/assets/styles/Contact.module.scss index e69de29..c5695e9 100644 --- a/src/assets/styles/Contact.module.scss +++ b/src/assets/styles/Contact.module.scss @@ -0,0 +1,270 @@ +@use './variables' as *; +@use './card.scss' as card; + +@include card.card; + +/* Card styling */ +.contactCard { + position: relative; + border-left: 3px solid $accent-fire; + padding-left: 1.5rem; +} + +.contactIcon { + width: 50px; + height: 50px; + border-radius: 50%; + background-color: rgba(255, 100, 0, 0.1); + display: flex; + align-items: center; + justify-content: center; + color: $accent-fire; + font-size: 1.5rem; + margin-bottom: 1rem; +} + +.contactTitle { + font-size: 1.3rem; + color: $text-light; + margin-bottom: 1rem; +} + +.contactContent { + color: $text-ash; + line-height: 1.6; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +.contactLink { + color: $accent-fire; + text-decoration: none; + transition: all 0.3s ease; +} + +.contactLink:hover { + text-decoration: underline; + color: $accent-ember; +} + +/* Button styling */ +.btnPrimary { + background: linear-gradient(45deg, $accent-dragon, $accent-fire); + border: none; + padding: 0.75rem 2rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + box-shadow: 0 4px 15px rgba(139, 0, 0, 0.3); + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.btnPrimary:hover { + transform: translateY(-3px); + box-shadow: 0 7px 25px rgba(139, 0, 0, 0.5); + background-color: $accent-fire; + background-image: none; +} + +.btnPrimary:focus { + box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.4); +} + +.btnPrimary::before { + content: ""; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: 0.6s; +} + +.btnPrimary:hover::before { + left: 100%; +} + +.btnPrimary:disabled { + background: linear-gradient(45deg, rgba(139, 0, 0, 0.5), rgba(255, 100, 0, 0.5)); + transform: none; +} + +/* Social links styling */ +.socialGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); + gap: 15px; + margin-top: 1.5rem; +} + +.socialItem { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 10px; + padding: 15px; + border-radius: 8px; + background-color: rgba(255, 255, 255, 0.05); + transition: all 0.3s ease; + text-decoration: none; +} + +.socialItem:hover { + background-color: rgba(255, 100, 0, 0.1); + transform: translateY(-5px); +} + +.socialIconLg { + font-size: 1.8rem; + color: $accent-fire; +} + +.socialName { + color: $text-ash; + font-size: 0.9rem; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +/* Map styling */ +.mapContainer { + position: relative; + overflow: hidden; + border-radius: 8px; + border: 1px solid $card-border; + margin-bottom: 2rem; +} + +.mapContainer::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 5px; + background: linear-gradient(to right, $accent-dragon, $accent-fire); + z-index: 1; +} + +.mapFrame { + width: 100%; + height: 400px; + border: none; + background-color: $card-bg; + filter: grayscale(90%) invert(92%) hue-rotate(180deg); +} + +/* Accordion styling */ +.accordionButton { + background-color: $card-bg !important; + color: $text-light !important; + border-color: $card-border !important; + box-shadow: none !important; + padding: 1rem 1.25rem; + font-family: 'Cinzel', serif; +} + +.accordionButton:not(.collapsed) { + color: $accent-fire !important; +} + +.accordionButton:focus { + border-color: $accent-fire !important; + box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25) !important; +} + +.accordionButton::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23ff6400'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") !important; +} + +.accordionItem { + background-color: transparent; + border: none; + margin-bottom: 1rem; +} + +.accordionBody { + background-color: $card-bg; + border: 1px solid $card-border; + border-top: none; + color: $text-ash; + padding: 1.25rem; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + border-bottom-left-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; +} + +/* Binary background effect */ +.binaryLine { + position: absolute; + font-family: 'Fira Code', monospace; + color: $accent-fire; + font-size: 0.8rem; + opacity: 0.15; + pointer-events: none; +} + +/* Ember effect */ +.emberParticle { + position: absolute; + width: 3px; + height: 3px; + background-color: $accent-fire; + border-radius: 50%; + opacity: 0.6; + pointer-events: none; + z-index: 2; + box-shadow: 0 0 5px $accent-fire; + animation: float 3s linear infinite; +} + +@keyframes float { + 0% { + transform: translateY(0) rotate(0deg); + opacity: 0.6; + } + + 100% { + transform: translateY(-100px) rotate(360deg); + opacity: 0; + } +} + +/* Table styling */ +.table { + color: $text-ash; + margin-bottom: 0; +} + +.table td { + padding: 0.5rem 0.25rem; + border-color: rgba(255, 255, 255, 0.05); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +@media (max-width: 768px) { + .schedule-container { + overflow-x: auto; + } + + .social-grid { + grid-template-columns: repeat(3, 1fr); + } + + .map-frame { + height: 300px; + } +} + +@media (max-width: 576px) { + .social-grid { + grid-template-columns: repeat(2, 1fr); + gap: 10px; + } + + .form-floating>label { + padding: 0.75rem 0.75rem; + } +} \ No newline at end of file diff --git a/src/assets/styles/FormControl.scss b/src/assets/styles/FormControl.scss new file mode 100644 index 0000000..4595d48 --- /dev/null +++ b/src/assets/styles/FormControl.scss @@ -0,0 +1,95 @@ +@use './variables.scss' as *; + +/* Form styling */ +.form-control { + background-color: rgba(255, 255, 255, 0.05); + border: 1px solid var(--card-border); + color: var(--text-light); + padding: 0.75rem 0.9375rem; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +.form-control:focus { + background-color: rgba(255, 255, 255, 0.08); + border-color: var(--accent-fire); + box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25); + color: var(--text-light); +} + +.form-control::placeholder { + color: rgba(255, 255, 255, 0.3); +} + +textarea.form-control { + min-height: 120px; +} + +.form-label { + color: var(--text-ash); + font-weight: 600; + margin-bottom: 0.5rem; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +.form-text { + color: rgba(255, 255, 255, 0.5); +} + +.form-select { + background-color: rgba(255, 255, 255, 0.05); + border: 1px solid var(--card-border); + color: var(--text-light); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ff6400' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +.form-select:focus { + background-color: rgba(255, 255, 255, 0.08); + border-color: var(--accent-fire); + box-shadow: 0 0 0 0.25rem rgba(255, 100, 0, 0.25); + color: var(--text-light); +} + +.form-select option { + background-color: var(--card-bg); + color: var(--text-light); +} + +/* Alert styling */ +.alert-success { + background-color: rgba(25, 135, 84, 0.1); + border-color: rgba(25, 135, 84, 0.3); + color: #75b798; +} + +.alert-danger { + background-color: rgba(220, 53, 69, 0.1); + border-color: rgba(220, 53, 69, 0.3); + color: #ea868f; +} + +/* Form validation styling */ +.was-validated .form-control:invalid, +.form-control.is-invalid { + border-color: #dc3545; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .form-control:valid, +.form-control.is-valid { + border-color: #198754; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.invalid-feedback { + color: #dc3545; + font-size: 0.875em; + margin-top: 0.25rem; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} \ No newline at end of file diff --git a/src/assets/styles/card.scss b/src/assets/styles/card.scss new file mode 100644 index 0000000..999ad18 --- /dev/null +++ b/src/assets/styles/card.scss @@ -0,0 +1,35 @@ +@use './variables.scss' as *; + +/* Card styling */ +@mixin card { + .card { + background-color: $card-bg; + border: 1px solid $card-border; + border-radius: 8px; + transition: all 0.3s ease; + overflow: hidden; + } + + .card:hover { + transform: translateY(-7px); + border-color: rgba(255, 100, 0, 0.3); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4); + } + + .card::before { + content: ""; + position: absolute; + width: 100%; + height: 5px; + top: 0; + left: 0; + background: linear-gradient(to right, $accent-dragon, $accent-fire); + z-index: 1; + } + + @media (max-width: 576px) { + .card-body { + padding: 1.25rem; + } + } +} \ No newline at end of file diff --git a/src/components/ContactForm.tsx b/src/components/ContactForm.tsx index 792eb1c..0209f3e 100644 --- a/src/components/ContactForm.tsx +++ b/src/components/ContactForm.tsx @@ -1,4 +1,6 @@ import { useState, type FormEvent } from 'react'; +import '@styles/FormControl.scss' +import styles from '@styles/Contact.module.scss' export const ContactForm = () => { const [formData, setFormData] = useState({ @@ -47,11 +49,11 @@ export const ContactForm = () => { }; return ( -
-
+
+

Send a Message to the Forge

-
+
{/* Success message */} {showSuccess && (
@@ -176,7 +178,7 @@ export const ContactForm = () => {
-
+