Updated Careers page and Projects styling
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect , useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import careersBanner from '../assets/careers-banner.jpg';
|
import careersBanner from '../assets/careers-banner.jpg';
|
||||||
import careerImg from '../assets/career.jpg';
|
import careerImg from '../assets/career.jpg';
|
||||||
@@ -24,7 +24,8 @@ const Careers = () => {
|
|||||||
});
|
});
|
||||||
const [filteredJobs, setFilteredJobs] = useState([]);
|
const [filteredJobs, setFilteredJobs] = useState([]);
|
||||||
const [activeOnly, setActiveOnly] = useState(true);
|
const [activeOnly, setActiveOnly] = useState(true);
|
||||||
|
const [showSuccessMessage, setShowSuccessMessage] = useState(false);
|
||||||
|
const resumeInputRef = useRef(null);
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// const handleShortcut = (e) => {
|
// const handleShortcut = (e) => {
|
||||||
// if (e.altKey && e.key.toLowerCase() === "n") {
|
// if (e.altKey && e.key.toLowerCase() === "n") {
|
||||||
@@ -581,109 +582,170 @@ const Careers = () => {
|
|||||||
</div> */}
|
</div> */}
|
||||||
|
|
||||||
|
|
||||||
<div className="mt-8 bg-white rounded-lg shadow-xl p-6 max-w-2xl mx-auto text-left text-gray-800">
|
<div className="mt-8 bg-white rounded-lg shadow-xl p-6 max-w-2xl mx-auto text-left text-gray-800 relative">
|
||||||
<h3 className="text-2xl font-bold text-blue-900 mb-4">HR Contact Form</h3>
|
{/* Success Popup */}
|
||||||
<form onSubmit={handleSubmit}>
|
{showSuccessMessage && (
|
||||||
<div className="mb-4">
|
<div className="absolute inset-0 bg-black/30 flex items-center justify-center z-50">
|
||||||
<label htmlFor="fullName" className="block text-gray-700 font-medium mb-2">
|
<div className="bg-green-100 text-green-800 px-6 py-4 rounded-md shadow-md text-center max-w-sm">
|
||||||
Full Name <span className="text-red-500">*</span>
|
<p className="font-semibold text-lg">Thank you for connecting!</p>
|
||||||
</label>
|
<p className="text-sm mt-1">We will get back to you soon.</p>
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="fullName"
|
|
||||||
name="fullName"
|
|
||||||
value={formData.fullName}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<label htmlFor="email" className="block text-gray-700 font-medium mb-2">
|
|
||||||
Email <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
value={formData.email}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<label htmlFor="phone" className="block text-gray-700 font-medium mb-2">
|
|
||||||
Phone Number
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phone"
|
|
||||||
name="phone"
|
|
||||||
value={formData.phone}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-6">
|
|
||||||
<label htmlFor="resume" className="block text-gray-700 font-medium mb-2">
|
|
||||||
Upload Resume (PDF) <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="resume"
|
|
||||||
name="resume"
|
|
||||||
onChange={handleFileChange}
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
accept=".pdf,.doc,.docx"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-4">
|
|
||||||
{/* <button
|
|
||||||
type="button"
|
|
||||||
onClick={handleContactClick}
|
|
||||||
className="px-6 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-100 transition"
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button> */}
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
onClick={() => setShowSuccessMessage(false)}
|
||||||
className={`px-6 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition flex items-center justify-center gap-2 ${loading ? 'opacity-50 cursor-not-allowed' : ''}`}
|
className="mt-3 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition"
|
||||||
disabled={loading}
|
|
||||||
>
|
>
|
||||||
{loading && (
|
OK
|
||||||
<svg
|
|
||||||
className="animate-spin h-5 w-5 text-white"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<circle
|
|
||||||
className="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="4"
|
|
||||||
></circle>
|
|
||||||
<path
|
|
||||||
className="opacity-75"
|
|
||||||
fill="currentColor"
|
|
||||||
d="M4 12a8 8 0 018-8v8H4z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
Submit
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
|
<h3 className="text-2xl font-bold text-blue-900 mb-4">Let’s Connect With Us</h3>
|
||||||
|
<form
|
||||||
|
onSubmit={async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!formData.resume) {
|
||||||
|
alert("Please upload your resume.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append("fullName", formData.fullName);
|
||||||
|
data.append("email", formData.email);
|
||||||
|
data.append("phone", formData.phone);
|
||||||
|
data.append("resume", formData.resume);
|
||||||
|
|
||||||
|
const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}/api/careers/contact`, {
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
// Show popup message
|
||||||
|
setShowSuccessMessage(true);
|
||||||
|
|
||||||
|
// Reset the form
|
||||||
|
setFormData({
|
||||||
|
fullName: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
resume: null,
|
||||||
|
});
|
||||||
|
if (resumeInputRef.current) {
|
||||||
|
resumeInputRef.current.value = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert(result.error || "Failed to submit the form. Please try again.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error submitting form:", error);
|
||||||
|
alert("Server error. Please try again later.");
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Full Name */}
|
||||||
|
<div className="mb-4">
|
||||||
|
<label htmlFor="fullName" className="block text-gray-700 font-medium mb-2">
|
||||||
|
Full Name <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="fullName"
|
||||||
|
name="fullName"
|
||||||
|
value={formData.fullName}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Email */}
|
||||||
|
<div className="mb-4">
|
||||||
|
<label htmlFor="email" className="block text-gray-700 font-medium mb-2">
|
||||||
|
Email <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Phone */}
|
||||||
|
<div className="mb-4">
|
||||||
|
<label htmlFor="phone" className="block text-gray-700 font-medium mb-2">
|
||||||
|
Phone Number
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
id="phone"
|
||||||
|
name="phone"
|
||||||
|
value={formData.phone}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Resume */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<label htmlFor="resume" className="block text-gray-700 font-medium mb-2">
|
||||||
|
Upload Resume (PDF) <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="resume"
|
||||||
|
name="resume"
|
||||||
|
ref={resumeInputRef} // <-- add this
|
||||||
|
onChange={handleFileChange}
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
accept=".pdf,.doc,.docx"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-4">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className={`px-6 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition flex items-center justify-center gap-2 ${loading ? 'opacity-50 cursor-not-allowed' : ''}`}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading && (
|
||||||
|
<svg
|
||||||
|
className="animate-spin h-5 w-5 text-white"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
className="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
className="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8v8H4z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,73 +1,57 @@
|
|||||||
/* Projects.css */
|
/* =====================
|
||||||
|
Projects.css
|
||||||
|
===================== */
|
||||||
|
|
||||||
|
/* Force cards to match height */
|
||||||
|
.radar-like-slider .slick-track {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
/* Slider override */
|
|
||||||
.radar-like-slider .slick-slide {
|
.radar-like-slider .slick-slide {
|
||||||
padding: 0 0.75rem;
|
height: inherit !important;
|
||||||
|
display: flex !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Card */
|
.radar-like-slider .slick-slide > div {
|
||||||
.radar-card {
|
width: 100% !important;
|
||||||
text-align: center;
|
height: 100% !important;
|
||||||
cursor: pointer;
|
}
|
||||||
|
/* --- Fix for Overlapping Dots Grid --- */
|
||||||
|
.radar-like-slider .slick-dots {
|
||||||
|
bottom: -40px !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: row !important;
|
||||||
|
flex-wrap: nowrap !important; /* Forces dots into a single horizontal row */
|
||||||
|
justify-content: center !important;
|
||||||
|
list-style: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radar-image {
|
/* --- Mobile View Adjustments --- */
|
||||||
width: 100%;
|
@media (max-width: 768px) {
|
||||||
height: 12rem;
|
/* Force the single card to fill the screen width to stop congestion */
|
||||||
object-fit: cover;
|
.radar-like-slider .slick-slide {
|
||||||
border-radius: 0.75rem;
|
width: 100% !important;
|
||||||
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease;
|
padding: 0 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radar-image:hover {
|
/* Expand image area for the single-card layout */
|
||||||
transform: translateY(-6px) scale(1.05);
|
.radar-image-container {
|
||||||
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
|
aspect-ratio: 16 / 9 !important;
|
||||||
}
|
width: 100%;
|
||||||
|
border-radius: 12px 12px 0 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.radar-title {
|
/* Position arrows so they are easy to use on mobile */
|
||||||
margin-top: 0.5rem;
|
.slick-prev, .slick-next {
|
||||||
font-size: 1rem;
|
z-index: 20 !important;
|
||||||
font-weight: 600;
|
top: 40% !important;
|
||||||
color: #1f2937; /* text-gray-800 */
|
width: 35px !important;
|
||||||
}
|
height: 35px !important;
|
||||||
|
}
|
||||||
/* Filter buttons */
|
|
||||||
.filter-buttons {
|
.slick-prev { left: 5px !important; }
|
||||||
display: flex;
|
.slick-next { right: 5px !important; }
|
||||||
flex-wrap: wrap;
|
}
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sector-btn {
|
|
||||||
background: #2563eb;
|
|
||||||
color: #fff;
|
|
||||||
padding: 0.6rem 1rem;
|
|
||||||
border-radius: 9999px;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sector-btn.selected,
|
|
||||||
.sector-btn:hover {
|
|
||||||
background: #1e40af;
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lightbox */
|
|
||||||
.lightbox {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(0,0,0,0.85);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lightbox img {
|
|
||||||
max-width: 90%;
|
|
||||||
max-height: 90%;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user