fixes #2733: Bug where date range is displayed on separate line (#2862)

This commit is contained in:
Amruth Pillai
2026-04-04 12:03:09 +02:00
committed by GitHub
parent 9332e1e3ff
commit 923f5f6173
68 changed files with 2042 additions and 1704 deletions
@@ -1,6 +1,7 @@
import type { SectionItem } from "@/schema/resume/data";
import { TiptapContent } from "@/components/input/rich-input";
import { filterFieldValues } from "@/utils/field";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
@@ -12,24 +13,44 @@ type AwardsItemProps = SectionItem<"awards"> & {
};
export function AwardsItem({ className, ...item }: AwardsItemProps) {
const headerValues = {
title: item.title,
date: item.date,
awarder: item.awarder,
};
const headerFields = filterFieldValues(
headerValues,
{
key: "title",
content: (
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title awards-item-title"
/>
),
},
{
key: "date",
content: <span className="section-item-metadata awards-item-date">{item.date}</span>,
},
{
key: "awarder",
content: <span className="section-item-metadata awards-item-awarder">{item.awarder}</span>,
},
);
return (
<div className={cn("awards-item", className)}>
{/* Header */}
<div className="section-item-header awards-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title awards-item-title"
/>
<span className="section-item-metadata awards-item-date shrink-0 text-end">{item.date}</span>
</div>
{/* Row 2 */}
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata awards-item-awarder">{item.awarder}</span>
<div className="grid grid-cols-2 items-start gap-x-2">
{headerFields.map((field, index) => (
<div key={field.key} className={cn(index % 2 === 1 && "shrink-0 justify-self-end text-end")}>
{field.content}
</div>
))}
</div>
</div>
@@ -1,6 +1,7 @@
import type { SectionItem } from "@/schema/resume/data";
import { TiptapContent } from "@/components/input/rich-input";
import { filterFieldValues } from "@/utils/field";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
@@ -12,24 +13,44 @@ type CertificationsItemProps = SectionItem<"certifications"> & {
};
export function CertificationsItem({ className, ...item }: CertificationsItemProps) {
const headerValues = {
title: item.title,
date: item.date,
issuer: item.issuer,
};
const headerFields = filterFieldValues(
headerValues,
{
key: "title",
content: (
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title certifications-item-title"
/>
),
},
{
key: "date",
content: <span className="section-item-metadata certifications-item-date">{item.date}</span>,
},
{
key: "issuer",
content: <span className="section-item-metadata certifications-item-issuer">{item.issuer}</span>,
},
);
return (
<div className={cn("certifications-item", className)}>
{/* Header */}
<div className="section-item-header certifications-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title certifications-item-title"
/>
<span className="section-item-metadata certifications-item-date shrink-0 text-end">{item.date}</span>
</div>
{/* Row 2 */}
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata certifications-item-issuer">{item.issuer}</span>
<div className="grid grid-cols-2 items-start gap-x-2">
{headerFields.map((field, index) => (
<div key={field.key} className={cn(index % 2 === 1 && "shrink-0 justify-self-end text-end")}>
{field.content}
</div>
))}
</div>
</div>
@@ -1,6 +1,7 @@
import type { SectionItem } from "@/schema/resume/data";
import { TiptapContent } from "@/components/input/rich-input";
import { filterFieldValues } from "@/utils/field";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
@@ -12,29 +13,51 @@ type EducationItemProps = SectionItem<"education"> & {
};
export function EducationItem({ className, ...item }: EducationItemProps) {
const degreeAndGrade = [item.degree, item.grade].filter(Boolean).join(" • ");
const locationAndPeriod = [item.location, item.period].filter(Boolean).join(" • ");
const headerValues = {
school: item.school,
degreeAndGrade,
area: item.area,
locationAndPeriod,
};
const headerFields = filterFieldValues(
headerValues,
{
key: "school",
content: (
<LinkedTitle
title={item.school}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title education-item-title"
/>
),
},
{
key: "degreeAndGrade",
content: <span className="section-item-metadata education-item-degree-grade">{degreeAndGrade}</span>,
},
{
key: "area",
content: <span className="section-item-metadata education-item-area">{item.area}</span>,
},
{
key: "locationAndPeriod",
content: <span className="section-item-metadata education-item-location-period">{locationAndPeriod}</span>,
},
);
return (
<div className={cn("education-item", className)}>
{/* Header */}
<div className="section-item-header education-item-header mb-2">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<LinkedTitle
title={item.school}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title education-item-title"
/>
<span className="section-item-metadata education-item-degree-grade shrink-0 text-end">
{[item.degree, item.grade].filter(Boolean).join(" • ")}
</span>
</div>
{/* Row 2 */}
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata education-item-area">{item.area}</span>
<span className="section-item-metadata education-item-location-period shrink-0 text-end">
{[item.location, item.period].filter(Boolean).join(" • ")}
</span>
<div className="grid grid-cols-2 items-start gap-x-2">
{headerFields.map((field, index) => (
<div key={field.key} className={cn(index % 2 === 1 && "shrink-0 justify-self-end text-end")}>
{field.content}
</div>
))}
</div>
</div>
@@ -1,6 +1,7 @@
import type { SectionItem } from "@/schema/resume/data";
import { TiptapContent } from "@/components/input/rich-input";
import { filterFieldValues } from "@/utils/field";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
@@ -13,36 +14,50 @@ type ExperienceItemProps = SectionItem<"experience"> & {
export function ExperienceItem({ className, ...item }: ExperienceItemProps) {
const hasRoles = Array.isArray(item.roles) && item.roles.length > 0;
const headerValues = {
company: item.company,
location: item.location,
position: item.position,
period: item.period,
};
const headerFields = filterFieldValues(
headerValues,
{
key: "company",
content: (
<LinkedTitle
title={item.company}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title experience-item-title"
/>
),
},
{
key: "location",
content: <span className="section-item-metadata experience-item-location">{item.location}</span>,
},
{
key: "position",
content: <span className="section-item-metadata experience-item-position">{item.position}</span>,
},
{
key: "period",
content: <span className="section-item-metadata experience-item-period">{item.period}</span>,
},
);
return (
<div className={cn("experience-item", className)}>
{/* Header */}
<div className="section-item-header experience-item-header">
{/* Row 1: Company + Location */}
<div className="flex items-start justify-between gap-x-2">
<LinkedTitle
title={item.company}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title experience-item-title"
/>
<span className="section-item-metadata experience-item-location shrink-0 text-end">{item.location}</span>
<div className="grid grid-cols-2 items-start gap-x-2">
{headerFields.map((field, index) => (
<div key={field.key} className={cn(index % 2 === 1 && "shrink-0 justify-self-end text-end")}>
{field.content}
</div>
))}
</div>
{/* Row 2: Position + Period */}
{(!hasRoles || item.position) && (
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata experience-item-position">{item.position}</span>
<span className="section-item-metadata experience-item-period shrink-0 text-end">{item.period}</span>
</div>
)}
{/* Overall period when hasRoles and no summary position */}
{hasRoles && !item.position && item.period && (
<div className="flex items-start justify-end gap-x-2">
<span className="section-item-metadata experience-item-period shrink-0 text-end">{item.period}</span>
</div>
)}
</div>
{/* Role Progression */}
@@ -52,7 +67,7 @@ export function ExperienceItem({ className, ...item }: ExperienceItemProps) {
<div key={role.id} className="experience-item-role">
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata experience-item-role-position">{role.position}</span>
<span className="section-item-metadata experience-item-role-period shrink-0 text-end">
<span className="section-item-metadata experience-item-role-period shrink-0 justify-self-end text-end">
{role.period}
</span>
</div>
@@ -1,6 +1,7 @@
import type { SectionItem } from "@/schema/resume/data";
import { TiptapContent } from "@/components/input/rich-input";
import { filterFieldValues } from "@/utils/field";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
@@ -12,24 +13,44 @@ type PublicationsItemProps = SectionItem<"publications"> & {
};
export function PublicationsItem({ className, ...item }: PublicationsItemProps) {
const headerValues = {
title: item.title,
date: item.date,
publisher: item.publisher,
};
const headerFields = filterFieldValues(
headerValues,
{
key: "title",
content: (
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title publications-item-title"
/>
),
},
{
key: "date",
content: <span className="section-item-metadata publications-item-date">{item.date}</span>,
},
{
key: "publisher",
content: <span className="section-item-metadata publications-item-publisher">{item.publisher}</span>,
},
);
return (
<div className={cn("publications-item", className)}>
{/* Header */}
<div className="section-item-header publications-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<LinkedTitle
title={item.title}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title publications-item-title"
/>
<span className="section-item-metadata publications-item-date shrink-0 text-end">{item.date}</span>
</div>
{/* Row 2 */}
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata publications-item-publisher">{item.publisher}</span>
<div className="grid grid-cols-2 items-start gap-x-2">
{headerFields.map((field, index) => (
<div key={field.key} className={cn(index % 2 === 1 && "shrink-0 justify-self-end text-end")}>
{field.content}
</div>
))}
</div>
</div>
@@ -1,6 +1,7 @@
import type { SectionItem } from "@/schema/resume/data";
import { TiptapContent } from "@/components/input/rich-input";
import { filterFieldValues } from "@/utils/field";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
@@ -12,23 +13,39 @@ type ReferencesItemProps = SectionItem<"references"> & {
};
export function ReferencesItem({ className, ...item }: ReferencesItemProps) {
const headerValues = {
name: item.name,
position: item.position,
};
const headerFields = filterFieldValues(
headerValues,
{
key: "name",
content: (
<LinkedTitle
title={item.name}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title references-item-name"
/>
),
},
{
key: "position",
content: <span className="section-item-metadata references-item-position">{item.position}</span>,
},
);
return (
<div className={cn("references-item", className)}>
{/* Header */}
<div className="section-item-header references-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<LinkedTitle
title={item.name}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title references-item-name"
/>
</div>
{/* Row 2 */}
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata references-item-position">{item.position}</span>
<div className="grid grid-cols-2 items-start gap-x-2">
{headerFields.map((field, index) => (
<div key={field.key} className={cn(index % 2 === 1 && "shrink-0 justify-self-end text-end")}>
{field.content}
</div>
))}
</div>
</div>
@@ -1,6 +1,7 @@
import type { SectionItem } from "@/schema/resume/data";
import { TiptapContent } from "@/components/input/rich-input";
import { filterFieldValues } from "@/utils/field";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
@@ -12,24 +13,44 @@ type VolunteerItemProps = SectionItem<"volunteer"> & {
};
export function VolunteerItem({ className, ...item }: VolunteerItemProps) {
const headerValues = {
organization: item.organization,
period: item.period,
location: item.location,
};
const headerFields = filterFieldValues(
headerValues,
{
key: "organization",
content: (
<LinkedTitle
title={item.organization}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title volunteer-item-title"
/>
),
},
{
key: "period",
content: <span className="section-item-metadata volunteer-item-period">{item.period}</span>,
},
{
key: "location",
content: <span className="section-item-metadata volunteer-item-location">{item.location}</span>,
},
);
return (
<div className={cn("volunteer-item", className)}>
{/* Header */}
<div className="section-item-header volunteer-item-header">
{/* Row 1 */}
<div className="flex items-start justify-between gap-x-2">
<LinkedTitle
title={item.organization}
website={item.website}
showLinkInTitle={item.options?.showLinkInTitle}
className="section-item-title volunteer-item-title"
/>
<span className="section-item-metadata volunteer-item-period shrink-0 text-end">{item.period}</span>
</div>
{/* Row 2 */}
<div className="flex items-start justify-between gap-x-2">
<span className="section-item-metadata volunteer-item-location">{item.location}</span>
<div className="grid grid-cols-2 items-start gap-x-2">
{headerFields.map((field, index) => (
<div key={field.key} className={cn(index % 2 === 1 && "shrink-0 justify-self-end text-end")}>
{field.content}
</div>
))}
</div>
</div>