Skip to content

Feat: Add labeled lists support for custom resume sections#765

Open
ahsanirfan961 wants to merge 3 commits into
srbhr:mainfrom
ahsanirfan961:feat/labeled-lists-section
Open

Feat: Add labeled lists support for custom resume sections#765
ahsanirfan961 wants to merge 3 commits into
srbhr:mainfrom
ahsanirfan961:feat/labeled-lists-section

Conversation

@ahsanirfan961
Copy link
Copy Markdown

@ahsanirfan961 ahsanirfan961 commented Apr 25, 2026

Description

Currently, custom resume sections only allow users to enter a single block of text (e.g., "Skills: Python, React, Docker"). This limits how users can structure grouped content like "Technical Skills" vs "Soft Skills" or "English" vs "Spanish".

This PR adds a new Labeled Lists custom section type that lets users create multiple titled subsections, each containing a list of items. This enables more structured, professional presentations of grouped content.

What this PR does

  • Adds a new labeledLists section type in the resume data model (backend validation and frontend types)
  • Provides UI components (GenericLabeledListForm) for creating, editing, removing, and reordering subsections
  • Renders labeled lists in a professional two-column format across all 4 resume templates
  • Includes complete i18n translations for 5 languages (English, Spanish, Chinese, Japanese, Portuguese Brazilian)
  • Updates documentation with data structure and usage examples
  • Example
image

Type

  • Feature Enhancement
  • Bug Fix
  • Documentation Update
  • Code Refactoring
  • Other (please specify):

Proposed Changes

Backend (Python/Pydantic):

  • Added LabeledListItem model with id, label, and items fields
  • Updated SectionType enum to include LABELED_LISTS = "labeledLists"
  • Extended CustomSection model with namedLists field (optional list of LabeledListItem)
  • Implemented field validators for automatic ID assignment and data normalization

Frontend (TypeScript/React):

  • Updated SectionType union type to include 'labeledLists'
  • Created LabeledListItem TypeScript interface matching backend schema
  • Built GenericLabeledListForm component with:
    • Add subsection button
    • Remove subsection (trash icon)
    • Reorder subsections (up/down arrows)
    • Label input field (e.g., "Technical Skills")
    • Items textarea with newline-separated entry
    • Empty state message
  • Integrated routing in resume-form.tsx to use the form for labeled list sections
  • Added "Labeled Lists" option to add-section-dialog.tsx with description and icon

Resume Rendering (All 4 Templates):

  • Created LabeledListSectionContent helper component for consistent rendering
  • Implemented in DynamicResumeSection (shared by all templates)
  • Two-column layout: bold label (left, 32px width) + comma-separated items (right)
  • Applied to: modern, modern-two-column, single-column, and two-column templates

Internationalization:

  • Added builder.genericLabeledListForm translation keys in all 5 language files
  • Translations include: addSubsectionLabel, labelPlaceholder, itemsPlaceholder, field labels, empty state message

Documentation:

  • Updated docs/agent/features/custom-sections.md with labeled lists section type overview
  • Included data structure examples (backend and frontend)
  • Added rendering examples and use case descriptions

Screenshots / Code Snippets (if applicable)

Before (Original Behavior)

Custom sections only allow single text input:

Skills
[textarea: "Python, React, Docker, Git, etc..."]

After (With Labeled Lists)

Users can now create structured subsections:

Skills (Custom Section Type: Labeled Lists)
┌─────────────────────────────┐
│ Technical Skills: Python, React, TypeScript │ (subsection 1)
│ Soft Skills: Communication, Leadership      │ (subsection 2)
│ Languages: English, Spanish, French         │ (subsection 3)
└─────────────────────────────┘

Data Format (JSON)

{
  "sectionType": "labeledLists",
  "namedLists": [
    {
      "id": 1,
      "label": "Technical Skills",
      "items": ["Python", "React", "TypeScript"]
    },
    {
      "id": 2,
      "label": "Languages",
      "items": ["English", "Spanish", "French"]
    }
  ]
}

How to Test

  1. Create a custom section with labeled lists:

    • Navigate to Resume Builder (/builder)
    • Click "Add Section" button
    • Select "Labeled Lists" from the section type dropdown
    • Verify the form appears with an "Add Subsection" button and empty state message
  2. Add subsections:

    • Click "Add Subsection"
    • Enter label: "Technical Skills"
    • Enter items (newline-separated): "Python\nReact\nTypeScript"
    • Click "Add Subsection" again and create another subsection: "Languages: English\nSpanish"
  3. Edit subsections:

    • Modify any label or items
    • Verify changes are reflected in the editor and resume preview
  4. Reorder subsections:

    • Click up/down arrows to reorder subsections
    • Verify order changes in the resume preview
  5. Delete subsections:

    • Click trash icon next to a subsection
    • Verify the subsection is removed
  6. Save and reload:

    • Save the resume (auto-save via context)
    • Reload the page or switch between different resumes
    • Verify the labeled list data persists correctly
  7. Test i18n:

    • Switch language in the UI settings
    • Verify form labels and button text translate correctly
    • Test with: English (en), Spanish (es), Chinese (zh), Japanese (ja), Portuguese (pt-BR)
  8. Test rendering across all templates:

    • Switch between all 4 resume templates
    • Verify labeled lists render in two-column layout: bold label + comma-separated items
    • Templates to test: Modern, Modern Two-Column, Single-Column, Two-Column
  9. Test edge cases:

    • Create labeled list with very long label or items
    • Create labeled list with special characters
    • Delete all subsections and verify empty state displays

Checklist

  • The code compiles successfully without any errors or warnings
  • The changes have been tested and verified
  • The documentation has been updated (if applicable)
  • The changes follow the project's coding guidelines and best practices
  • The commit messages are descriptive and follow the project's guidelines
  • All tests (if applicable) pass successfully
  • This pull request has been linked to the related issue (if applicable)

Additional Information

Type Safety and Validation:

  • All Pydantic models include field validators for data normalization (coercion, ID assignment)
  • TypeScript interfaces match backend schema exactly (no type mismatches)
  • Proper handling of null/undefined values with sensible defaults

i18n Completeness:

  • All 5 supported languages have complete translation keys
  • No hardcoded strings in the component

Code Quality:

  • Follows existing Swiss-style UI conventions and component patterns
  • Reuses existing translation and form architecture
  • ESLint and TypeScript checks pass

Summary by cubic

Adds a new Labeled Lists custom section type to group items under labeled subsections (e.g., Technical Skills, Languages) with a clean two-column render across all templates. Includes full editor support, backend schema/validation, i18n, and docs.

  • New Features

    • Backend: Added LABELED_LISTS to SectionType, LabeledListItem model, and CustomSection.namedLists with normalization and auto ID assignment.
    • Frontend: New GenericLabeledListForm with add/remove/reorder, label input, and newline-based items; integrated into add-section dialog and form routing.
    • Rendering: Consistent two-column layout (bold label + comma-separated items) in modern, modern-two-column, single-column, and two-column templates.
    • i18n: Added translations and keys for 5 languages.
    • Docs: Updated custom sections guide with schema, examples, and usage.
  • Migration

    • No breaking changes; existing resumes unaffected. Add via Add Section → Labeled Lists.

Written for commit 8b0e2b0. Summary will update on new commits.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/frontend/components/resume/dynamic-resume-section.tsx">

<violation number="1" location="apps/frontend/components/resume/dynamic-resume-section.tsx:43">
P2: `labeledLists` content gating checks only array length, so empty labeled-list entries can cause visually empty section rendering.</violation>
</file>

<file name="apps/frontend/messages/en.json">

<violation number="1" location="apps/frontend/messages/en.json:168">
P2: `itemsPlaceholder` implies comma-separated items, but the form parser splits only on newlines, creating misleading UX and potential single-item parsing errors.</violation>
</file>

<file name="apps/frontend/messages/ja.json">

<violation number="1" location="apps/frontend/messages/ja.json:167">
P3: Japanese localization typo in `labelPlaceholder` (`技技能`) introduces incorrect user-facing text.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

case 'stringList':
return Boolean(customSection.strings?.length);
case 'labeledLists':
return Boolean(customSection.namedLists?.length);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: labeledLists content gating checks only array length, so empty labeled-list entries can cause visually empty section rendering.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/frontend/components/resume/dynamic-resume-section.tsx, line 43:

<comment>`labeledLists` content gating checks only array length, so empty labeled-list entries can cause visually empty section rendering.</comment>

<file context>
@@ -38,6 +39,8 @@ export const DynamicResumeSection: React.FC<DynamicResumeSectionProps> = ({
       case 'stringList':
         return Boolean(customSection.strings?.length);
+      case 'labeledLists':
+        return Boolean(customSection.namedLists?.length);
       default:
         return false;
</file context>
Suggested change
return Boolean(customSection.namedLists?.length);
return Boolean(
customSection.namedLists?.some(
(list) => list.label.trim() || list.items.some((item) => item.trim())
)
);
Fix with Cubic

"subsectionLabel": "Subsection",
"addSubsectionLabel": "Add Subsection",
"labelPlaceholder": "e.g., Technical Skills, Languages",
"itemsPlaceholder": "e.g., Python, React, TypeScript",
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: itemsPlaceholder implies comma-separated items, but the form parser splits only on newlines, creating misleading UX and potential single-item parsing errors.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/frontend/messages/en.json, line 168:

<comment>`itemsPlaceholder` implies comma-separated items, but the form parser splits only on newlines, creating misleading UX and potential single-item parsing errors.</comment>

<file context>
@@ -159,6 +161,17 @@
+      "subsectionLabel": "Subsection",
+      "addSubsectionLabel": "Add Subsection",
+      "labelPlaceholder": "e.g., Technical Skills, Languages",
+      "itemsPlaceholder": "e.g., Python, React, TypeScript",
+      "fields": {
+        "label": "Label",
</file context>
Suggested change
"itemsPlaceholder": "e.g., Python, React, TypeScript",
"itemsPlaceholder": "e.g., Python\nReact\nTypeScript",
Fix with Cubic

"genericLabeledListForm": {
"subsectionLabel": "サブセクション",
"addSubsectionLabel": "サブセクションを追加",
"labelPlaceholder": "例:技技能、言語",
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Japanese localization typo in labelPlaceholder (技技能) introduces incorrect user-facing text.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/frontend/messages/ja.json, line 167:

<comment>Japanese localization typo in `labelPlaceholder` (`技技能`) introduces incorrect user-facing text.</comment>

<file context>
@@ -159,6 +161,17 @@
+    "genericLabeledListForm": {
+      "subsectionLabel": "サブセクション",
+      "addSubsectionLabel": "サブセクションを追加",
+      "labelPlaceholder": "例:技技能、言語",
+      "itemsPlaceholder": "例:Python、React、TypeScript",
+      "fields": {
</file context>
Suggested change
"labelPlaceholder": "例:技技能、言語",
"labelPlaceholder": "例:技術スキル、言語",
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant