This commit is contained in:
Iliyan Angelov
2025-11-24 08:18:18 +02:00
parent 366f28677a
commit 136f75a859
133 changed files with 14977 additions and 3350 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useState, FormEvent, ChangeEvent } from "react";
import { useState, FormEvent, ChangeEvent, useEffect, useRef } from "react";
import { JobPosition, careerService } from "@/lib/api/careerService";
interface JobApplicationFormProps {
@@ -62,6 +62,71 @@ const JobApplicationForm = ({ job, onClose }: JobApplicationFormProps) => {
type: null,
text: "",
});
// Refs for scrolling to messages
const messageRef = useRef<HTMLDivElement>(null);
const formRef = useRef<HTMLFormElement>(null);
const scrollableRef = useRef<HTMLDivElement>(null);
// State for scroll buttons visibility
const [showScrollUp, setShowScrollUp] = useState(false);
const [showScrollDown, setShowScrollDown] = useState(false);
// Check scroll position and update button visibility
const checkScrollPosition = () => {
if (scrollableRef.current) {
const { scrollTop, scrollHeight, clientHeight } = scrollableRef.current;
setShowScrollUp(scrollTop > 100);
setShowScrollDown(scrollTop < scrollHeight - clientHeight - 100);
}
};
// Scroll functions
const scrollUp = () => {
if (scrollableRef.current) {
scrollableRef.current.scrollBy({
top: -300,
behavior: 'smooth'
});
}
};
const scrollDown = () => {
if (scrollableRef.current) {
scrollableRef.current.scrollBy({
top: 300,
behavior: 'smooth'
});
}
};
// Scroll to message when it appears
useEffect(() => {
if (message.type && messageRef.current) {
setTimeout(() => {
messageRef.current?.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}, 100);
}
}, [message]);
// Add scroll listener
useEffect(() => {
const scrollable = scrollableRef.current;
if (scrollable) {
checkScrollPosition();
scrollable.addEventListener('scroll', checkScrollPosition);
// Check on resize
window.addEventListener('resize', checkScrollPosition);
return () => {
scrollable.removeEventListener('scroll', checkScrollPosition);
window.removeEventListener('resize', checkScrollPosition);
};
}
}, []);
const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value, type } = e.target;
@@ -243,7 +308,7 @@ const JobApplicationForm = ({ job, onClose }: JobApplicationFormProps) => {
{/* Message */}
{message.type && (
<div className={`alert alert-${message.type}`}>
<div ref={messageRef} className={`alert alert-${message.type}`}>
<div className="alert-icon">
{message.type === "success" ? (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -262,8 +327,8 @@ const JobApplicationForm = ({ job, onClose }: JobApplicationFormProps) => {
)}
{/* Form */}
<form onSubmit={handleSubmit} className="application-form">
<div className="form-scrollable">
<form ref={formRef} onSubmit={handleSubmit} className="application-form">
<div className="form-scrollable" ref={scrollableRef}>
{/* Required Fields Section */}
<div className="form-section">
<div className="section-header">
@@ -622,6 +687,33 @@ const JobApplicationForm = ({ job, onClose }: JobApplicationFormProps) => {
</p>
</div>
</form>
{/* Scroll Navigation Buttons */}
{showScrollUp && (
<button
type="button"
onClick={scrollUp}
className="scroll-button scroll-button-up"
aria-label="Scroll up"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 15l-6-6-6 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
)}
{showScrollDown && (
<button
type="button"
onClick={scrollDown}
className="scroll-button scroll-button-down"
aria-label="Scroll down"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
)}
</div>
<style jsx>{`
@@ -1120,6 +1212,68 @@ const JobApplicationForm = ({ job, onClose }: JobApplicationFormProps) => {
.form-scrollable::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* Scroll Navigation Buttons */
.scroll-button {
position: absolute;
right: 24px;
width: 48px;
height: 48px;
background: #667eea;
color: white;
border: none;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
transition: all 0.3s ease;
z-index: 10;
opacity: 0;
animation: fadeInButton 0.3s ease forwards;
}
@keyframes fadeInButton {
to {
opacity: 1;
}
}
.scroll-button:hover {
background: #5a6fd8;
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);
}
.scroll-button:active {
transform: translateY(0);
}
.scroll-button-up {
top: 120px;
}
.scroll-button-down {
bottom: 120px;
}
/* Responsive adjustments for scroll buttons */
@media (max-width: 768px) {
.scroll-button {
width: 44px;
height: 44px;
right: 16px;
}
.scroll-button-up {
top: 100px;
}
.scroll-button-down {
bottom: 100px;
}
}
`}</style>
</div>
);