Skip to content
Open
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
29 changes: 29 additions & 0 deletions src/actions/collaborationAdsActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import axios from 'axios';
import { toast } from 'react-toastify';
import { ENDPOINTS } from '../utils/URL';

export const CREATE_COLLABORATION_ADS_REQUEST = 'CREATE_COLLABORATION_ADS_REQUEST';
export const CREATE_COLLABORATION_ADS_SUCCESS = 'CREATE_COLLABORATION_ADS_SUCCESS';
export const CREATE_COLLABORATION_ADS_FAIL = 'CREATE_COLLABORATION_ADS_FAIL';

// eslint-disable-next-line import/prefer-default-export
export const createCollaborationAds = formData => async dispatch => {
try {
dispatch({ type: CREATE_COLLABORATION_ADS_REQUEST });
const response = await axios.post(`${ENDPOINTS.JOBS}`, formData);

dispatch({
type: CREATE_COLLABORATION_ADS_SUCCESS,
payload: response.data,
});

if (response.status === 200 || response.status === 201) {
toast.success('Collaboration Ads created successfully!');
}
} catch (error) {
dispatch({ type: CREATE_COLLABORATION_ADS_FAIL });
toast.error(
error.response?.data?.message || 'Error updating the details. Please try again.',
);
}
};
74 changes: 69 additions & 5 deletions src/components/Collaboration/Collaboration.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,55 @@ function dedupeJobsByTitle(jobs) {
});
}

/**
* Backend parseCategory() splits on commas, which breaks names like
* "Architecture, Landscape & Environment". Sending a JSON array keeps the
* full category string intact (API already supports JSON.parse first).
*/
function encodeJobsCategoryParam(category) {
const trimmed = String(category || '').trim();
if (!trimmed) return '';
return JSON.stringify([trimmed]);
}

/** Dropbox share links need raw=1 (or dl=1) to render as <img> src. */
function normalizeJobImageUrl(url) {
const trimmed = String(url || '').trim();
if (!trimmed) return '';
try {
const parsed = new URL(trimmed);
const host = parsed.hostname.replace(/^www\./, '');
if (host === 'dropbox.com' || host === 'dl.dropboxusercontent.com') {
parsed.searchParams.delete('dl');
parsed.searchParams.set('raw', '1');
return parsed.toString();
}
return trimmed;
} catch {
return trimmed;
}
}

function isUsableJobImageUrl(url) {
if (!url || typeof url !== 'string') return false;
const trimmed = url.trim();
if (!trimmed) return false;
try {
const parsed = new URL(trimmed);
if (!['http:', 'https:'].includes(parsed.protocol)) return false;
if (
['example.com', 'www.example.com', 'placeholder.com', 'via.placeholder.com'].includes(
parsed.hostname,
)
) {
return false;
}
return true;
} catch {
return false;
}
}

function Collaboration() {
const [searchTerm, setSearchTerm] = useState('');
const [submittedSearchTerm, setSubmittedSearchTerm] = useState('');
Expand Down Expand Up @@ -175,7 +224,7 @@ function Collaboration() {
const response = await fetch(
`${ApiEndpoint}/jobs?page=${page}&limit=${adsPerPage}` +
`&search=${encodeURIComponent(search)}` +
`&category=${encodeURIComponent(category)}`,
`&category=${encodeURIComponent(encodeJobsCategoryParam(category))}`,
requestOptions,
);

Expand Down Expand Up @@ -307,7 +356,7 @@ function Collaboration() {
setActiveTab('jobPostings');
const response = await fetch(
`${ApiEndpoint}/jobs/summaries?search=${encodeURIComponent(searchTerm)}` +
`&category=${encodeURIComponent(selectedCategory)}`,
`&category=${encodeURIComponent(encodeJobsCategoryParam(selectedCategory))}`,
{ method: 'GET' },
);

Expand Down Expand Up @@ -354,9 +403,22 @@ function Collaboration() {
}
};

const getJobImageSrc = (ad, jobCategory) => {
if (isUsableJobImageUrl(ad?.imageUrl)) {
return normalizeJobImageUrl(ad.imageUrl);
}
return getCategoryImage(jobCategory);
};

const handleImageError = event => {
event.currentTarget.onerror = null;
event.currentTarget.src =
const img = event.currentTarget;
img.onerror = null;
const fallback = img.dataset.fallback;
if (fallback && img.src !== fallback) {
img.src = fallback;
return;
}
img.src =
'https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=640&h=480&fit=crop&q=80';
};

Expand All @@ -380,6 +442,7 @@ function Collaboration() {
if (!ad?._id) return null;
const jobTitle = ad.title || 'Untitled Position';
const jobCategory = ad.category || 'General';
const categoryFallback = getCategoryImage(jobCategory);

return (
<button
Expand All @@ -392,7 +455,8 @@ function Collaboration() {
onClick={handleJobAdClick}
>
<img
src={getCategoryImage(jobCategory)}
src={getJobImageSrc(ad, jobCategory)}
data-fallback={categoryFallback}
alt={jobTitle}
loading="lazy"
onError={handleImageError}
Expand Down
14 changes: 13 additions & 1 deletion src/components/Collaboration/Collaboration.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: auto;
min-width: fit-content;
height: 38px;
padding: 0 16px;
background-color: #0958a8;
Expand All @@ -124,6 +127,7 @@
font-size: 16px;
font-weight: 600;
white-space: nowrap;
overflow: visible;
line-height: normal;
vertical-align: middle;
transition: filter 0.15s ease, transform 0.02s ease;
Expand Down Expand Up @@ -603,8 +607,16 @@
color: #fff !important;
border: none !important;
border-radius: 8px;
padding: 6px 12px;
padding: 0 16px;
font-weight: 600;
flex: 0 0 auto;
width: auto;
min-width: fit-content;
white-space: nowrap;
}

.jobLandingDark .showSummaries {
padding: 0 22px;
}

.jobLandingDark .pagination button:hover {
Expand Down
Loading
Loading