s3 fix
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Tag } from './Tag';
|
||||
|
||||
interface ExperienceData {
|
||||
@@ -14,7 +15,25 @@ interface ExperienceSectionProps {
|
||||
experience: ExperienceData;
|
||||
}
|
||||
|
||||
const INITIAL_DISPLAY_COUNT = 6;
|
||||
const EXPERTISE_INITIAL_COUNT = 4;
|
||||
|
||||
export function ExperienceSection({ experience }: ExperienceSectionProps) {
|
||||
const [showAllLicensing, setShowAllLicensing] = useState(false);
|
||||
const [showAllExpertise, setShowAllExpertise] = useState(false);
|
||||
|
||||
// Calculate how many more items are hidden
|
||||
const licensingRemaining = experience.licensingAreas.length - INITIAL_DISPLAY_COUNT;
|
||||
const expertiseRemaining = experience.expertiseYears.length - EXPERTISE_INITIAL_COUNT;
|
||||
|
||||
// Get displayed items
|
||||
const displayedLicensing = showAllLicensing
|
||||
? experience.licensingAreas
|
||||
: experience.licensingAreas.slice(0, INITIAL_DISPLAY_COUNT);
|
||||
const displayedExpertise = showAllExpertise
|
||||
? experience.expertiseYears
|
||||
: experience.expertiseYears.slice(0, EXPERTISE_INITIAL_COUNT);
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-[20px] p-4 lg:p-5">
|
||||
<h2 className="text-[20px] font-bold text-[#00293D] font-fractul leading-[24px] mb-4 text-center lg:text-left">Experience</h2>
|
||||
@@ -24,26 +43,51 @@ export function ExperienceSection({ experience }: ExperienceSectionProps) {
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">Years in Experience</p>
|
||||
<ul className="list-disc list-inside">
|
||||
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">{experience.years}</li>
|
||||
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">
|
||||
{experience.years !== '-' ? experience.years : <span className="text-[#00293D]/40">Not specified</span>}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* Horizontal divider */}
|
||||
<div className="w-full h-0 border-t-[0.5px] border-solid border-[#00293D]/20" />
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">Number of contract closed</p>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">Number of contracts closed</p>
|
||||
<ul className="list-disc list-inside">
|
||||
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">{experience.contracts}</li>
|
||||
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">
|
||||
{experience.contracts !== '-' ? experience.contracts : <span className="text-[#00293D]/40">Not specified</span>}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-3">Licensing & Areas</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{experience.licensingAreas.map((area, idx) => (
|
||||
<Tag key={idx} variant="light">
|
||||
{area}
|
||||
</Tag>
|
||||
))}
|
||||
<span className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer">+3 More</span>
|
||||
{experience.licensingAreas.length > 0 ? (
|
||||
<>
|
||||
{displayedLicensing.map((area, idx) => (
|
||||
<Tag key={idx} variant="light">
|
||||
{area}
|
||||
</Tag>
|
||||
))}
|
||||
{!showAllLicensing && licensingRemaining > 0 && (
|
||||
<button
|
||||
onClick={() => setShowAllLicensing(true)}
|
||||
className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
+{licensingRemaining} More
|
||||
</button>
|
||||
)}
|
||||
{showAllLicensing && experience.licensingAreas.length > INITIAL_DISPLAY_COUNT && (
|
||||
<button
|
||||
onClick={() => setShowAllLicensing(false)}
|
||||
className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
Show Less
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<span className="text-[14px] font-serif text-[#00293D]/40">No licensed areas added</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,24 +101,51 @@ export function ExperienceSection({ experience }: ExperienceSectionProps) {
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-3">Areas in expertise & Years</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{experience.expertiseYears.slice(0, 4).map((item, idx) => (
|
||||
<Tag key={idx} variant="light">
|
||||
{item.area} – {item.years}
|
||||
</Tag>
|
||||
))}
|
||||
<span className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer">+3 More</span>
|
||||
{experience.expertiseYears.length > 0 ? (
|
||||
<>
|
||||
{displayedExpertise.map((item, idx) => (
|
||||
<Tag key={idx} variant="light">
|
||||
{item.years ? `${item.area} – ${item.years}` : item.area}
|
||||
</Tag>
|
||||
))}
|
||||
{!showAllExpertise && expertiseRemaining > 0 && (
|
||||
<button
|
||||
onClick={() => setShowAllExpertise(true)}
|
||||
className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
+{expertiseRemaining} More
|
||||
</button>
|
||||
)}
|
||||
{showAllExpertise && experience.expertiseYears.length > EXPERTISE_INITIAL_COUNT && (
|
||||
<button
|
||||
onClick={() => setShowAllExpertise(false)}
|
||||
className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
Show Less
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<span className="text-[14px] font-serif text-[#00293D]/40">No expertise areas added</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-3">Certifications</p>
|
||||
<ul className="space-y-3">
|
||||
{experience.certifications.map((cert, idx) => (
|
||||
<li key={idx}>
|
||||
<span className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D]">{cert.name}</span>
|
||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] opacity-50 ml-3">{cert.org}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{experience.certifications.length > 0 ? (
|
||||
<ul className="space-y-3">
|
||||
{experience.certifications.map((cert, idx) => (
|
||||
<li key={idx}>
|
||||
<span className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D]">{cert.name}</span>
|
||||
{cert.org && (
|
||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] opacity-50 ml-3">{cert.org}</p>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<span className="text-[14px] font-serif text-[#00293D]/40">No certifications added</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,9 +8,10 @@ interface PhotoUploadModalProps {
|
||||
onClose: () => void;
|
||||
onUpload: (file: File) => Promise<void>;
|
||||
currentPhoto?: string | null;
|
||||
currentPhotoUrl?: string | null; // Presigned URL for display
|
||||
}
|
||||
|
||||
export function PhotoUploadModal({ isOpen, onClose, onUpload, currentPhoto }: PhotoUploadModalProps) {
|
||||
export function PhotoUploadModal({ isOpen, onClose, onUpload, currentPhoto, currentPhotoUrl }: PhotoUploadModalProps) {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
@@ -170,20 +171,14 @@ export function PhotoUploadModal({ isOpen, onClose, onUpload, currentPhoto }: Ph
|
||||
}`}
|
||||
>
|
||||
{/* Current Photo Preview */}
|
||||
{currentPhoto && (
|
||||
{(currentPhotoUrl || currentPhoto) && (
|
||||
<div className="flex justify-center mb-4">
|
||||
<div className="relative w-[100px] h-[100px] rounded-[10px] overflow-hidden opacity-50">
|
||||
<Image
|
||||
src={
|
||||
currentPhoto.startsWith('http')
|
||||
? currentPhoto
|
||||
: currentPhoto.startsWith('/uploads')
|
||||
? `${process.env.NEXT_PUBLIC_API_URL?.replace('/api/v1', '')}${currentPhoto}`
|
||||
: currentPhoto
|
||||
}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={currentPhotoUrl || currentPhoto || ''}
|
||||
alt="Current photo"
|
||||
fill
|
||||
className="object-cover"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user