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
195 changes: 195 additions & 0 deletions frontend/src/pages/Contact.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { useState, useEffect } from 'react';
import { useAddMessageMutation, useGetPageContentQuery } from '../store/apiSlice';

export default function Contact() {
const { data: pageContent, isLoading: isLoadingContent } = useGetPageContentQuery();
const [formData, setFormData] = useState({ user_name: '', user_email: '', title_header: '', message_body: '' });
const [status, setStatus] = useState('idle'); // idle, success, error
const [cooldown, setCooldown] = useState(0);

const [addMessage, { isLoading }] = useAddMessageMutation();

useEffect(() => {
let timer;
if (cooldown > 0) {
timer = setInterval(() => setCooldown(c => c - 1), 1000);
}
return () => clearInterval(timer);
}, [cooldown]);

const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};

const handleSubmit = async (e) => {
e.preventDefault();
if (cooldown > 0) return;

try {
const payload = {
...formData,
timestamp: new Date().toISOString(),
status: 'unread'
};

await addMessage(payload).unwrap();

setStatus('success');
setFormData({ user_name: '', user_email: '', title_header: '', message_body: '' });
setCooldown(60); // 60 seconds cooldown to prevent spam

setTimeout(() => setStatus('idle'), 5000);
} catch (err) {
setStatus('error');
}
};

if (isLoadingContent) {
return (
<main className="flex-grow pt-[120px] pb-24 px-6 md:px-12 w-full max-w-[1000px] mx-auto">
<div className="font-label-mono text-primary-container"><span className="cursor-blink">Loading...</span></div>
</main>
);
}

return (
<main className="flex-grow pt-[120px] pb-24 px-6 md:px-12 w-full max-w-container-max mx-auto flex flex-col items-center justify-center">
<div className="w-full max-w-3xl border border-surface-container-highest bg-surface/80 backdrop-blur-md">
{/* Terminal Header */}
<div className="flex items-center px-4 py-2 bg-surface-container border-b border-surface-container-highest">
<div className="flex gap-2">
<div className="w-3 h-3 rounded-full bg-error"></div>
<div className="w-3 h-3 rounded-full bg-[#f1c40f]"></div>
<div className="w-3 h-3 rounded-full bg-primary-container"></div>
</div>
<div className="mx-auto font-label-mono text-label-mono text-on-surface-variant opacity-70">
user@backend-architect: ~/contact
</div>
</div>

{/* Terminal Body */}
<div className="p-8 md:p-12 space-y-12">
<div>
<h1 className="font-headline-xl text-headline-xl text-on-background mb-4">
{pageContent?.contact?.title || 'INITIATE_CONTACT'}
</h1>
<p className="font-body-base text-body-base text-on-surface-variant max-w-xl">
{pageContent?.contact?.subtitle || 'Awaiting input. Provide valid parameters to establish a secure connection or transmit a payload directly via the form interface below.'}
</p>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
{/* Form Section */}
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2">
<label className="font-label-mono text-label-mono text-primary-container flex items-center gap-2">
<span className="material-symbols-outlined text-[16px]">terminal</span>
Input: user_name
</label>
<div className="relative">
<span className="absolute left-3 top-1/2 -translate-y-1/2 font-code-snippet text-code-snippet text-on-surface-variant">&gt;</span>
<input required name="user_name" value={formData.user_name} onChange={handleChange} className="w-full bg-surface-container-low border border-surface-container-highest focus:border-primary-container focus:ring-0 text-on-background font-code-snippet text-code-snippet pl-8 py-3 outline-none transition-colors" placeholder="Enter string..." type="text" />
</div>
</div>
<div className="space-y-2">
<label className="font-label-mono text-label-mono text-primary-container flex items-center gap-2">
<span className="material-symbols-outlined text-[16px]">terminal</span>
Input: user_email
</label>
<div className="relative">
<span className="absolute left-3 top-1/2 -translate-y-1/2 font-code-snippet text-code-snippet text-on-surface-variant">&gt;</span>
<input required name="user_email" value={formData.user_email} onChange={handleChange} className="w-full bg-surface-container-low border border-surface-container-highest focus:border-primary-container focus:ring-0 text-on-background font-code-snippet text-code-snippet pl-8 py-3 outline-none transition-colors" placeholder="user@domain.tld" type="email" />
</div>
</div>
<div className="space-y-2">
<label className="font-label-mono text-label-mono text-primary-container flex items-center gap-2">
<span className="material-symbols-outlined text-[16px]">terminal</span>
Input: title_header
</label>
<div className="relative">
<span className="absolute left-3 top-1/2 -translate-y-1/2 font-code-snippet text-code-snippet text-on-surface-variant">&gt;</span>
<input required name="title_header" value={formData.title_header} onChange={handleChange} className="w-full bg-surface-container-low border border-surface-container-highest focus:border-primary-container focus:ring-0 text-on-background font-code-snippet text-code-snippet pl-8 py-3 outline-none transition-colors" placeholder="Enter metadata..." type="text" />
</div>
</div>
<div className="space-y-2">
<label className="font-label-mono text-label-mono text-primary-container flex items-center gap-2">
<span className="material-symbols-outlined text-[16px]">terminal</span>
Input: message_body
</label>
<div className="relative">
<span className="absolute left-3 top-4 font-code-snippet text-code-snippet text-on-surface-variant">&gt;</span>
<textarea required name="message_body" value={formData.message_body} onChange={handleChange} className="w-full bg-surface-container-low border border-surface-container-highest focus:border-primary-container focus:ring-0 text-on-background font-code-snippet text-code-snippet pl-8 py-3 outline-none transition-colors resize-none" placeholder="Enter payload data..." rows="5"></textarea>
</div>
</div>

{status === 'success' && (
<div className="p-3 border border-primary-container/50 bg-primary-container/10 text-primary-container font-code-snippet text-sm glow-border-sm">
&gt; Payload transmitted successfully. Connection closed.
</div>
)}
{status === 'error' && (
<div className="p-3 border border-error/50 bg-error/10 text-error font-code-snippet text-sm">
&gt; Transmission failed. Check connection logs.
</div>
)}

<div className="pt-4">
<button disabled={isLoading || cooldown > 0} className="w-full font-label-mono text-label-mono border border-surface-container-highest text-on-surface hover:text-surface-container-lowest hover:bg-primary-container hover:border-primary-container py-4 transition-all duration-200 flex justify-center items-center gap-2 uppercase tracking-widest group disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-on-surface disabled:hover:border-surface-container-highest" type="submit">
{isLoading ? '[ Transmitting... ]' : cooldown > 0 ? `[ Rate_Limit: ${cooldown}s ]` : '[ Execute_Transmission ]'}
{!isLoading && cooldown === 0 && <span className="material-symbols-outlined group-hover:translate-x-1 transition-transform">send</span>}
</button>
</div>
</form>

{/* Direct Links Section */}
<div className="flex flex-col gap-6">
<div className="font-label-mono text-label-mono text-on-surface-variant border-b border-surface-container-highest pb-2 mb-2">
// DIRECT_LINKS
</div>
<a className="group flex items-center justify-between p-4 border border-surface-container-highest bg-surface-container-low hover:border-secondary-container transition-colors" href="#">
<div className="flex items-center gap-4">
<span className="material-symbols-outlined text-secondary-container">code</span>
<div>
<div className="font-label-mono text-label-mono text-on-background">GitHub</div>
<div className="font-code-snippet text-code-snippet text-on-surface-variant">github.com/backend_arch</div>
</div>
</div>
<span className="material-symbols-outlined text-on-surface-variant group-hover:text-secondary-container transition-colors">arrow_outward</span>
</a>
<a className="group flex items-center justify-between p-4 border border-surface-container-highest bg-surface-container-low hover:border-secondary-container transition-colors" href="#">
<div className="flex items-center gap-4">
<span className="material-symbols-outlined text-secondary-container">work</span>
<div>
<div className="font-label-mono text-label-mono text-on-background">LinkedIn</div>
<div className="font-code-snippet text-code-snippet text-on-surface-variant">linkedin.com/in/backend_arch</div>
</div>
</div>
<span className="material-symbols-outlined text-on-surface-variant group-hover:text-secondary-container transition-colors">arrow_outward</span>
</a>
<a className="group flex items-center justify-between p-4 border border-surface-container-highest bg-surface-container-low hover:border-secondary-container transition-colors" href="mailto:hello@example.com">
<div className="flex items-center gap-4">
<span className="material-symbols-outlined text-secondary-container">mail</span>
<div>
<div className="font-label-mono text-label-mono text-on-background">Email</div>
<div className="font-code-snippet text-code-snippet text-on-surface-variant">sysadmin@backend.local</div>
</div>
</div>
<span className="material-symbols-outlined text-on-surface-variant group-hover:text-secondary-container transition-colors">arrow_outward</span>
</a>

{/* Terminal Output Mockup */}
<div className="mt-auto pt-8">
<div className="font-code-snippet text-code-snippet text-on-surface-variant bg-surface-container-lowest p-4 border border-surface-container-highest">
<span className="text-primary-container">sys@admin</span>:~$ status check<br />
&gt; System online.<br />
&gt; Listening on port 443.<br />
&gt; Ready for input <span className="cursor-blink inline-block w-2 h-4 bg-primary-container align-middle ml-1"></span>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
);
}
88 changes: 88 additions & 0 deletions frontend/src/pages/Education.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useGetEducationQuery, useGetPageContentQuery } from '../store/apiSlice';

export default function Education() {
const { data: education, isLoading: isLoadingEdu } = useGetEducationQuery();
const { data: pageContent, isLoading: isLoadingContent } = useGetPageContentQuery();

if (isLoadingEdu || isLoadingContent) {
return (
<main className="flex-grow pt-[120px] pb-24 px-6 md:px-12 w-full max-w-[1000px] mx-auto">
<div className="font-label-mono text-primary-container"><span className="cursor-blink">Loading...</span></div>
</main>
);
}

if (!education) return null;

return (
<main className="flex-grow pt-[120px] pb-24 px-6 md:px-12 w-full max-w-[1000px] mx-auto">
<h1 className="font-headline-xl text-[40px] md:text-[64px] text-on-surface tracking-tighter font-black mb-4">
{pageContent?.education?.title || 'ACADEMIC_RECORD'}
</h1>
<p className="font-body-lg text-body-lg text-on-surface-variant max-w-2xl mb-16">
{pageContent?.education?.subtitle || 'Degrees, certifications, and formal training.'}
</p>

<div className="space-y-12">
{/* Degrees Section */}
<section>
<div className="font-label-mono text-label-mono text-on-surface-variant border-b border-surface-container-highest pb-2 mb-8 flex items-center gap-2">
<span className="material-symbols-outlined text-primary-container">school</span>
ACADEMIC_DEGREES
</div>
<div className="space-y-6">
{education.filter(e => e.typeEducation === 'DEGREE').map(deg => (
<article key={deg.id} className="border border-surface-container-highest bg-surface-container-lowest p-6 md:p-8 flex flex-col md:flex-row gap-6 md:gap-12 relative overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-primary-container"></div>
<div className="md:w-1/3">
<div className="font-label-mono text-label-mono text-surface-variant mb-2">{deg.period}</div>
<h2 className="font-headline-sm text-headline-sm text-on-surface mb-1">{deg.degree}</h2>
<div className="font-code-snippet text-code-snippet text-primary-container">{deg.title}</div>
</div>
<div className="md:w-2/3">
<h3 className="font-headline-sm text-headline-sm text-on-surface mb-4">{deg.institution}</h3>
<div className="space-y-2 mb-6">
<div>
<div className="font-label-mono text-label-mono text-surface-variant mb-3">SPECIALIZATION:</div>
<div className="font-body-base text-body-base text-on-surface">{deg.specialization}</div>
</div>
</div>
<div>
<div className="font-label-mono text-label-mono text-surface-variant mb-3">CORE_ARCHITECTURES:</div>
<div className="flex flex-wrap gap-2">
{deg.architectures.map(arch => (
<span key={arch} className="font-label-mono text-[10px] text-on-surface bg-surface-container px-2 py-1 uppercase tracking-wider border border-surface-container-highest">
{arch}
</span>
))}
</div>
</div>
</div>
</article>
))}
</div>
</section>

{/* Certifications Section */}
<section>
<div className="font-label-mono text-label-mono text-on-surface-variant border-b border-surface-container-highest pb-2 mb-8 flex items-center gap-2 mt-16">
<span className="material-symbols-outlined text-secondary-container">verified</span>
CERTIFICATIONS_&_MODULES
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{education.filter(e => e.typeEducation === 'CERTIFICATION').map(cert => (
<div key={cert.id} className="border border-surface-container-highest bg-surface-container p-6 group hover:border-secondary-container transition-colors duration-300">
<div className="flex justify-between items-start mb-4">
<span className="material-symbols-outlined text-3xl text-secondary-container group-hover:glow-border-sm transition-all">{cert.icon}</span>
<div className="font-code-snippet text-[10px] text-surface-variant uppercase tracking-widest">{cert.issued}</div>
</div>
<h3 className="font-headline-sm text-headline-sm text-on-surface mb-2">{cert.title}</h3>
<p className="font-body-sm text-body-sm text-on-surface-variant">{cert.description}</p>
</div>
))}
</div>
</section>
</div>
</main>
);
}
Loading