feat: Implement drag-and-drop reordering, collapsible forms, and UI refinements
Introduces drag-and-drop functionality for Experience and Education entries in the admin dashboard using SortableJS, along with collapsible forms powered by Alpine.js. Ensures live updates via HTMX. Refines both public site and admin dashboard UI: - Public site: Role/Degree are more prominent, Company/Institution highlight on hover. - Admin dashboard: Headers display Role/Degree as primary and include date ranges. Addresses backend needs by re-introducing 'display_order' to the database schema and updating queries and mutations for proper reordering. Fixes: - Resolved 'invalid_grant' Keycloak error by correcting PKCE code verifier generation. - Corrected database query parameter passing to fix text field clearing on form save. - Fixed JSX parsing errors with Alpine.js attributes by using full syntax and spread operator. - Resolved DOMTokenList whitespace error in SortableJS ghostClass. - Fixed SortableJS initialization and drag events to ensure visual reordering.
This commit is contained in:
@@ -24,9 +24,11 @@ interface ExperienceTranslation {
|
||||
}
|
||||
|
||||
interface Experience extends ExperienceTranslation {
|
||||
id: number; // Add ID for ordering
|
||||
start_date: string;
|
||||
end_date: string | null;
|
||||
company_url: string | null;
|
||||
display_order: number; // Re-added
|
||||
}
|
||||
|
||||
interface EducationTranslation {
|
||||
@@ -36,10 +38,11 @@ interface EducationTranslation {
|
||||
}
|
||||
|
||||
interface Education extends EducationTranslation {
|
||||
id: number; // Add ID for ordering
|
||||
start_date: string;
|
||||
end_date: string | null;
|
||||
institution_url: string | null;
|
||||
display_order: number;
|
||||
display_order: number; // Re-added
|
||||
}
|
||||
|
||||
interface SkillTranslation {
|
||||
@@ -66,24 +69,24 @@ export function getProfile(lang: string): Profile | null {
|
||||
|
||||
export function getExperience(lang: string): Experience[] {
|
||||
const experience = db.query(`
|
||||
SELECT e.start_date, e.end_date, e.company_url, e.display_order,
|
||||
SELECT e.id, e.start_date, e.end_date, e.company_url, e.display_order,
|
||||
et.company_name, et.role, et.description, et.location
|
||||
FROM experience e
|
||||
JOIN experience_translations et ON e.id = et.experience_id
|
||||
WHERE et.language_code = $lang
|
||||
ORDER BY e.display_order ASC, e.start_date DESC
|
||||
ORDER BY e.display_order ASC
|
||||
`).all({ $lang: lang }) as Experience[];
|
||||
return experience;
|
||||
}
|
||||
|
||||
export function getEducation(lang: string): Education[] {
|
||||
const education = db.query(`
|
||||
SELECT e.start_date, e.end_date, e.institution_url, e.display_order,
|
||||
SELECT e.id, e.start_date, e.end_date, e.institution_url, e.display_order,
|
||||
et.institution, et.degree, et.description
|
||||
FROM education e
|
||||
JOIN education_translations et ON e.id = et.education_id
|
||||
WHERE et.language_code = $lang
|
||||
ORDER BY e.display_order ASC, e.start_date DESC
|
||||
ORDER BY e.display_order ASC
|
||||
`).all({ $lang: lang }) as Education[];
|
||||
return education;
|
||||
}
|
||||
@@ -125,9 +128,9 @@ export function getAdminProfile() {
|
||||
}
|
||||
|
||||
export function getAdminExperience() {
|
||||
const exps = db.query(`SELECT * FROM experience ORDER BY display_order ASC, start_date DESC`).all() as any[];
|
||||
const exps = db.query(`SELECT e.*, MAX(e.display_order) OVER () AS max_order FROM experience e ORDER BY e.display_order ASC`).all() as any[];
|
||||
return exps.map(e => {
|
||||
const trans = db.query(`SELECT * FROM experience_translations WHERE experience_id = $id`, { $id: e.id }).all() as any[];
|
||||
const trans = db.query(`SELECT * FROM experience_translations WHERE experience_id = $id`).all({ $id: e.id }) as any[];
|
||||
trans.forEach(t => {
|
||||
e[`company_name_${t.language_code}`] = t.company_name;
|
||||
e[`role_${t.language_code}`] = t.role;
|
||||
@@ -153,9 +156,9 @@ export function getAdminExperienceById(id: number) {
|
||||
}
|
||||
|
||||
export function getAdminEducation() {
|
||||
const edus = db.query(`SELECT * FROM education ORDER BY display_order ASC, start_date DESC`).all() as any[];
|
||||
const edus = db.query(`SELECT e.*, MAX(e.display_order) OVER () AS max_order FROM education e ORDER BY e.display_order ASC`).all() as any[];
|
||||
return edus.map(e => {
|
||||
const trans = db.query(`SELECT * FROM education_translations WHERE education_id = $id`, { $id: e.id }).all() as any[];
|
||||
const trans = db.query(`SELECT * FROM education_translations WHERE education_id = $id`).all({ $id: e.id }) as any[];
|
||||
trans.forEach(t => {
|
||||
e[`institution_${t.language_code}`] = t.institution;
|
||||
e[`degree_${t.language_code}`] = t.degree;
|
||||
@@ -177,5 +180,3 @@ export function getAdminEducationById(id: number) {
|
||||
});
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user