Skip to content

Commit ced2ec8

Browse files
committed
fixed experience ordering by start date in both experience section and timeline
1 parent 809bd8e commit ced2ec8

3 files changed

Lines changed: 156 additions & 0 deletions

File tree

public-readonly/index.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,51 @@ <h2 class="section-title">Featured Projects</h2>
226226
document.getElementById('contactBadges').innerHTML = badges.join('');
227227
}
228228

229+
// Parse date string into comparable numeric value for sorting
230+
// Handles formats: "2020", "2020-01", "Jan 2020", etc.
231+
function parseDateForSortLocal(dateStr) {
232+
if (!dateStr) return 0;
233+
234+
// Format: "YYYY" (year only)
235+
if (/^\d{4}$/.test(dateStr)) {
236+
return parseInt(dateStr) * 100;
237+
}
238+
239+
// Format: "YYYY-MM" (ISO month)
240+
if (/^\d{4}-\d{2}$/.test(dateStr)) {
241+
const [year, month] = dateStr.split('-');
242+
return parseInt(year) * 100 + parseInt(month);
243+
}
244+
245+
// Format: "Mon YYYY" (e.g., "Jan 2020")
246+
const monthMatch = dateStr.match(/^([A-Za-z]+)\s+(\d{4})$/);
247+
if (monthMatch) {
248+
const months = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'];
249+
const monthIdx = months.indexOf(monthMatch[1].toLowerCase().substring(0, 3));
250+
const year = parseInt(monthMatch[2]);
251+
return year * 100 + (monthIdx >= 0 ? monthIdx + 1 : 0);
252+
}
253+
254+
// Fallback: try to extract year
255+
const yearMatch = dateStr.match(/(\d{4})/);
256+
if (yearMatch) {
257+
return parseInt(yearMatch[1]) * 100;
258+
}
259+
260+
return 0;
261+
}
262+
229263
function renderTimelineFromData(experiences) {
230264
if (!experiences) return;
231265
const visible = experiences.filter(e => e.visible !== false);
266+
267+
// Sort by start_date ascending (oldest first for timeline - left to right)
268+
visible.sort((a, b) => {
269+
const dateA = parseDateForSortLocal(a.start_date);
270+
const dateB = parseDateForSortLocal(b.start_date);
271+
return dateA - dateB; // ASC: lower dates first
272+
});
273+
232274
const container = document.getElementById('timelineItems');
233275
let lastCountry = null;
234276
container.innerHTML = visible.map((exp, idx) => {
@@ -247,6 +289,14 @@ <h2 class="section-title">Featured Projects</h2>
247289
function renderExperiencesFromData(experiences) {
248290
if (!experiences) return;
249291
const visible = experiences.filter(e => e.visible !== false);
292+
293+
// Sort by start_date descending (newest first)
294+
visible.sort((a, b) => {
295+
const dateA = parseDateForSortLocal(a.start_date);
296+
const dateB = parseDateForSortLocal(b.start_date);
297+
return dateB - dateA; // DESC: higher dates first
298+
});
299+
250300
document.getElementById('experienceList').innerHTML = visible.map(exp => `
251301
<article class="item-card" data-id="${exp.id || ''}" itemscope itemtype="https://schema.org/OrganizationRole">
252302
<div class="item-header">

public/shared/admin.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,40 @@ let currentModal = { type: null, id: null };
55
let sectionVisibility = {};
66
let sectionOrder = [];
77

8+
// Parse date string into comparable numeric value for sorting
9+
// Handles formats: "2020", "2020-01", "Jan 2020", etc.
10+
function parseDateForSort(dateStr) {
11+
if (!dateStr) return 0;
12+
13+
// Format: "YYYY" (year only)
14+
if (/^\d{4}$/.test(dateStr)) {
15+
return parseInt(dateStr) * 100; // e.g., 2020 -> 202000
16+
}
17+
18+
// Format: "YYYY-MM" (ISO month)
19+
if (/^\d{4}-\d{2}$/.test(dateStr)) {
20+
const [year, month] = dateStr.split('-');
21+
return parseInt(year) * 100 + parseInt(month); // e.g., 2020-03 -> 202003
22+
}
23+
24+
// Format: "Mon YYYY" (e.g., "Jan 2020")
25+
const monthMatch = dateStr.match(/^([A-Za-z]+)\s+(\d{4})$/);
26+
if (monthMatch) {
27+
const months = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'];
28+
const monthIdx = months.indexOf(monthMatch[1].toLowerCase().substring(0, 3));
29+
const year = parseInt(monthMatch[2]);
30+
return year * 100 + (monthIdx >= 0 ? monthIdx + 1 : 0);
31+
}
32+
33+
// Fallback: try to extract year
34+
const yearMatch = dateStr.match(/(\d{4})/);
35+
if (yearMatch) {
36+
return parseInt(yearMatch[1]) * 100;
37+
}
38+
39+
return 0;
40+
}
41+
842
// Initialize Admin
943
async function initAdmin() {
1044
sectionOrder = await loadSectionOrder();
@@ -340,8 +374,17 @@ async function loadSectionsAdmin() {
340374
}
341375

342376
// Load Experiences (admin version with edit controls)
377+
// Sorted by start_date DESC (newest first)
343378
async function loadExperiences() {
344379
const experiences = await api('/api/experiences');
380+
381+
// Sort by start_date descending (newest first)
382+
experiences.sort((a, b) => {
383+
const dateA = parseDateForSort(a.start_date);
384+
const dateB = parseDateForSort(b.start_date);
385+
return dateB - dateA; // DESC: higher dates first
386+
});
387+
345388
const container = document.getElementById('experienceList');
346389

347390
container.innerHTML = experiences.map(exp => `

public/shared/scripts.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,40 @@ function formatDateATS(dateStr) {
9696
return dateStr;
9797
}
9898

99+
// Parse date string into comparable numeric value for sorting
100+
// Handles formats: "2020", "2020-01", "Jan 2020", etc.
101+
function parseDateForSort(dateStr) {
102+
if (!dateStr) return 0;
103+
104+
// Format: "YYYY" (year only)
105+
if (/^\d{4}$/.test(dateStr)) {
106+
return parseInt(dateStr) * 100; // e.g., 2020 -> 202000
107+
}
108+
109+
// Format: "YYYY-MM" (ISO month)
110+
if (/^\d{4}-\d{2}$/.test(dateStr)) {
111+
const [year, month] = dateStr.split('-');
112+
return parseInt(year) * 100 + parseInt(month); // e.g., 2020-03 -> 202003
113+
}
114+
115+
// Format: "Mon YYYY" (e.g., "Jan 2020")
116+
const monthMatch = dateStr.match(/^([A-Za-z]+)\s+(\d{4})$/);
117+
if (monthMatch) {
118+
const months = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'];
119+
const monthIdx = months.indexOf(monthMatch[1].toLowerCase().substring(0, 3));
120+
const year = parseInt(monthMatch[2]);
121+
return year * 100 + (monthIdx >= 0 ? monthIdx + 1 : 0);
122+
}
123+
124+
// Fallback: try to extract year
125+
const yearMatch = dateStr.match(/(\d{4})/);
126+
if (yearMatch) {
127+
return parseInt(yearMatch[1]) * 100;
128+
}
129+
130+
return 0;
131+
}
132+
99133
// Load Profile (shared between admin and public)
100134
async function loadProfile(includePrivate = false) {
101135
const p = await api('/api/profile');
@@ -128,8 +162,28 @@ async function loadProfile(includePrivate = false) {
128162
}
129163

130164
// Load Timeline
165+
// Sorted by start_date ASC (oldest first - left to right)
131166
async function loadTimeline() {
132167
const timeline = await api('/api/timeline');
168+
169+
// Sort by start_date ascending (oldest first for timeline)
170+
// Timeline API returns 'period' field like "Jan 2020 - Present", extract start date from it
171+
timeline.sort((a, b) => {
172+
// Extract start date from period string (format: "Jan 2020 - Present" or "2020 - 2022")
173+
const getStartFromPeriod = (item) => {
174+
if (item.start_date) return parseDateForSort(item.start_date);
175+
if (item.period) {
176+
const startPart = item.period.split(' - ')[0];
177+
return parseDateForSort(startPart);
178+
}
179+
return 0;
180+
};
181+
182+
const dateA = getStartFromPeriod(a);
183+
const dateB = getStartFromPeriod(b);
184+
return dateA - dateB; // ASC: lower dates first (oldest on left)
185+
});
186+
133187
const container = document.getElementById('timelineItems');
134188

135189
let lastCountry = null;
@@ -210,8 +264,17 @@ function scrollToExperience(timelineItem) {
210264
}
211265

212266
// Load Experiences (read-only version)
267+
// Sorted by start_date DESC (newest first)
213268
async function loadExperiencesReadOnly() {
214269
const experiences = await api('/api/experiences');
270+
271+
// Sort by start_date descending (newest first)
272+
experiences.sort((a, b) => {
273+
const dateA = parseDateForSort(a.start_date);
274+
const dateB = parseDateForSort(b.start_date);
275+
return dateB - dateA; // DESC: higher dates first
276+
});
277+
215278
const container = document.getElementById('experienceList');
216279

217280
container.innerHTML = experiences.map(exp => `

0 commit comments

Comments
 (0)