Feature: Implement a new custom section type: summary (#2657)

* feat: add summaryItemSchema for custom summary section type

* feat: add CreateSummaryItemDialog and UpdateSummaryItemDialog

* feat: register summary item dialog types in store

* feat: route summary item dialogs in manager

* feat: add SummaryItem render component

* feat: handle summary type in renderItemByType and hide title for summary sections

* feat: handle summary type in sidebar helpers

* feat: add summary to custom section type options

* fix: update type definitions to support CustomSectionType for summary sections

* chore: extract new i18n strings for summary section

* style: apply biome formatting fixes

* chore: remove TODO.md file containing outdated feature specifications
This commit is contained in:
Amruth Pillai
2026-01-31 01:53:27 +01:00
committed by GitHub
parent 3d1c2d1fb6
commit aa12fcbd36
63 changed files with 1090 additions and 26 deletions
@@ -1,5 +1,11 @@
import { match } from "ts-pattern";
import type { CustomSectionItem, SectionItem, SectionType } from "@/schema/resume/data";
import type {
CustomSectionItem,
CustomSectionType,
SectionItem,
SectionType,
SummaryItem as SummaryItemType,
} from "@/schema/resume/data";
import { cn } from "@/utils/style";
import { useResumeStore } from "../store/resume";
import { AwardsItem } from "./items/awards-item";
@@ -13,6 +19,7 @@ import { ProjectsItem } from "./items/projects-item";
import { PublicationsItem } from "./items/publications-item";
import { ReferencesItem } from "./items/references-item";
import { SkillsItem } from "./items/skills-item";
import { SummaryItem } from "./items/summary-item";
import { VolunteerItem } from "./items/volunteer-item";
import { PageSection } from "./page-section";
import { PageSummary } from "./page-summary";
@@ -23,8 +30,9 @@ type SectionComponentProps = {
};
// Helper to render item component based on type
function renderItemByType(type: SectionType, item: CustomSectionItem, itemClassName?: string) {
function renderItemByType(type: CustomSectionType, item: CustomSectionItem, itemClassName?: string) {
return match(type)
.with("summary", () => <SummaryItem {...(item as SummaryItemType)} className={itemClassName} />)
.with("profiles", () => <ProfilesItem {...(item as SectionItem<"profiles">)} className={itemClassName} />)
.with("experience", () => <ExperienceItem {...(item as SectionItem<"experience">)} className={itemClassName} />)
.with("education", () => <EducationItem {...(item as SectionItem<"education">)} className={itemClassName} />)
@@ -163,7 +171,9 @@ export function getSectionComponent(
return (
<section className={cn(`page-section page-section-custom page-section-${id}`, sectionClassName)}>
<h6 className="mb-1.5 text-(--page-primary-color)">{customSection.title}</h6>
{customSection.type !== "summary" && (
<h6 className="mb-1.5 text-(--page-primary-color)">{customSection.title}</h6>
)}
<div
className="section-content grid gap-x-(--page-gap-x) gap-y-(--page-gap-y)"
@@ -0,0 +1,18 @@
import { TiptapContent } from "@/components/input/rich-input";
import type { SummaryItem as SummaryItemType } from "@/schema/resume/data";
import { stripHtml } from "@/utils/string";
import { cn } from "@/utils/style";
type SummaryItemProps = SummaryItemType & {
className?: string;
};
export function SummaryItem({ className, ...item }: SummaryItemProps) {
if (!stripHtml(item.content)) return null;
return (
<div className={cn("summary-item", className)}>
<TiptapContent content={item.content} />
</div>
);
}