Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 216 additions & 70 deletions .agents/skills/migrate-strapi-content/SKILL.md

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions .agents/skills/migrate-strapi-content/components-cheatsheet.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
source_kind,source_name,target,variant,layout,image_position,notes
slice,slices.brands,media.brand-logo-grid,,,,Logo media uploaded from old CDN; deduped by filename. SKIP if items[] empty after upload.
slice,slices.brands-with-intro,composite,,,,utilities.section-header (from intro) wrapped in sections.section-header + media.brand-logo-grid (logos uploaded). Header alone if logos empty.
slice,slices.capabilities-dynamic-cards,sections.tabbed-feature-overview,,,,"Each capability → elements.tabbed-feature (tabLabel from capability.label/title, optional tabIcon uploaded from CDN, content = sections.feature-overview). feature-overview.image is REQUIRED — upload via the brand-logo routine; SKIP that tab if no image survives upload. SKIP whole slice if no tabs survive."
slice,slices.case-study-card,cards.case-study-card,,,,Look up target by `card.data.attributes.slug` on target server; build ctaLink to `/user-stories/<slug>`.
slice,slices.chili-piper,sections.section-header,,,,Heading-only block. Title/label/text from slice.
slice,slices.company-stat-list,sections.three-column-grid,,,,"Each stat → elements.how-it-works-item (title=stat.value/number, description=stat.label/text). section field is REQUIRED — fall back to {title: 'Company Stats'} if slice intro is empty. itemStyle=default. May also appear as a root field on careers content type — verify before mapping."
slice,slices.content-videos-list,sections.section-header,,,,Heading-only block. Video items themselves are not migrated.
slice,slices.dark-cta-banner,sections.cta-banner,dark,,,"section: utilities.section-header from intro fields; background=dark (default for the slice variant)."
slice,slices.disclaimer,sections.disclaimer,,,,"Purpose-built component (title + content). SKIP if frontend renders disclaimers hardcoded for the route."
slice,slices.embed-form,SKIP,,,,No mapping unless caller provides a `hubspot-form` relation map.
slice,slices.features-card,sections.feature-card-grid,bordered,third,right,"Grid of split feature-cards on light background. Items = cards.feature-card variant=bordered layout=third imagePosition=right. SKIP if cards[] empty."
slice,slices.features-slice,sections.feature-card-grid,plain,half|third,,"layout = s.layout==='two' ? 'half' : 'third'; items = cards.feature-card variant=plain. SKIP if items have no title."
slice,slices.full-width-quote,testimonials.quote,fullwidth,,,Quote with full-width visual treatment.
slice,slices.getting-started-grid,sections.feature-card-grid,bordered,third,,"Items = cards.feature-card variant=bordered layout=third. Intro becomes the grid's built-in section header."
slice,slices.image-slider,media.image-gallery,contained,,,"Images uploaded from old CDN (dedupe by filename). SKIP if images[] empty after upload."
slice,slices.integration-cards-grid,sections.feature-card-grid,plain,,,"Items from integrations.data[] (cap 12). Each item: title, description, ctaLink to /integrations/<slug>."
slice,slices.interview,composite,dark+boxed,,,"sections.section-header (background=dark, boxed=true) for header/image + N standalone cards.content-card per Q&A pair (title=question, content=answer). content-card is allowed directly in page dynamic zone."
slice,slices.issues-header,sections.three-column-grid,bordered,,,"itemStyle=bordered. Each issue → elements.how-it-works-item (title=issue.title, description=issue.description). section field REQUIRED — fall back to slice intro/title, then default {title: 'Issues'} if empty."
slice,slices.intro,conditional,,,,"Position-dependent. If first slices[] entry AND no useCaseHero on the record → sections.hero. Otherwise → sections.section-header (no background, layout=center)."
slice,slices.large-features-slice,sections.two-column-grid,,,,"Single component. Grid's required section field carries the intro/title. Items = elements.how-it-works-item per feature (title + description). Same pattern as top-features."
slice,slices.large-video,media.video,,center,,"alignment=center. SKIP if url missing."
slice,slices.new-intro,sections.section-header,,,,Heading-only block.
slice,slices.news-list,sections.news-list,,,,"Auto-fetched chronological list — target component has NO attributes. Emit `{ __component: 'sections.news-list' }` with no fields. Always succeeds; never SKIP."
slice,slices.newsletter-banner,forms.newsletter,,,,"Limited migration: only the hubspotForm relation. Frontend renders fixed copy. SKIP if no resolvable hubspot-form on target."
slice,slices.quote,testimonials.quote,boxed,,,Standard quote treatment.
slice,slices.reviews-slider,sections.reviews,,,,"title REQUIRED (fall back to slice.title or 'What customers say'). subTitle from slice.label. description from slice.text/description. reviews relation: attempt to resolve old `reviews.data[]` against target `api::review.review` by author name match; on no match, leave relation empty and report under `manual_followup` so reviews can be hand-linked later."
slice,slices.related-case-studies,SKIP,,,,"Frontend renders related items algorithmically. Migrating creates duplicate content. Kept as SKIP."
slice,slices.section-with-image,cards.feature-card,bordered,full,flip_textPosition,"imagePosition = textPosition==='left' ? 'right' : 'left' (v4 textPosition = where text is; v5 imagePosition = where image is)."
slice,slices.side-hero-with-image,composite,bordered,full,right,sections.section-header (from intro) + cards.feature-card (variant=bordered layout=full imagePosition=right) for the hero content.
slice,slices.simple-text-next-to-image,cards.feature-card,bordered,full,left,"Single card. imagePosition=left. SKIP if no title."
slice,slices.stacking-cards,composite,,,,"sections.section-header (from title) + N standalone cards.content-card per card in cards[] (title=card.title, content=card.description as richtext). Schema constraint: feature-card-grid.items requires cards.feature-card, so we can't put content-cards in a grid wrapper; emitting them directly is allowed by the page dynamic zone."
slice,slices.text-next-to-big-image,cards.feature-card,bordered,full,right,"Single card. imagePosition=right."
slice,slices.text-next-to-image,cards.feature-card,bordered,full,flip_textPosition,"Same image-position flip as section-with-image."
slice,slices.text-slice,conditional,,,,"Three-way dispatch: has button → sections.cta-banner (section from label/title/text, ctaLinks from button); purple theme → sections.section-header (utilities.section-header.variant=purple inside); else → sections.richtext (markdown from label/title/text)."
slice,slices.text-with-cards,sections.feature-card-grid,plain,third,,"Grid with built-in section header from title/intro. Items = cards.feature-card size=sm with icon inline. SKIP if cards[] empty."
slice,slices.text-with-image-and-gradient,cards.feature-card,bordered,full,right,"Single card. ctaLinks from DownloadLink (button.link or button.download)."
slice,slices.text-with-key-numbers,SKIP,,,,"Awaiting new v5 component (no current grid can represent the {number, text} pairs cleanly). Reported in `skipped` with reason 'awaiting key-numbers component'."
slice,slices.top-features,sections.two-column-grid,,,,"Single component. Grid's required section field carries the intro (or default {title: 'Features'}). Items = elements.how-it-works-item per feature (title=feature.title, description=feature.description). feature.links/icon dropped (how-it-works-item has only icon + title + description)."
slice,slices.universal-rich-text,sections.richtext,,,,"Map richText directly. SKIP if empty."
root_field,careersHero,sections.hero,,,,"PREPEND to newContent. From careersHero.{label, title, text, button}. Include image when present (image field maps to hero's cover via existing media-upload pipeline)."
root_field,communityHero,composite,,,,"PREPEND sections.hero from communityHero.hero.{label, title, text, button}; then if communityHero.brandsWithIntro is present, also append a media.brand-logo-grid (logos uploaded from old CDN)."
root_field,featuresHero,sections.hero,,,,PREPEND. From featuresHero.{label/title/text/button} (or .hero.intro nested same as useCaseHero — verify per record)."
root_field,homeHero,sections.hero,,,,PREPEND on home universal. From homeHero.{label/title/text/button}.
root_field,useCaseHero,sections.hero,,,,"PREPEND on use-case records. From useCaseHero.hero.intro.{label, title, text, button}. Existing behavior — unchanged."
root_field,whiteHero,sections.hero,,,,PREPEND. White visual variant (background=light on hero if supported).
content_type,cms-comparison-comparator-grid,sections.comparator-grid,,,,"Comparator grid is a root-level field on cms-comparison records — handled by the cms-comparison content type's own mapping, not a slice rule."
fallback,*,migration.data-sink,,,,"If allowed in target dynamic zone; else SKIP and report."
7 changes: 6 additions & 1 deletion apps/strapi/src/api/page/content-types/page/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"sections.section-header",
"sections.two-columns-benefits",
"sections.two-column-grid",
"sections.three-column-grid",
"cards.feature-card",
"cards.content-card",
"cards.case-study-card",
Expand All @@ -77,6 +78,7 @@
"testimonials.quote",
"forms.newsletter",
"sections.testimonies",
"sections.reviews",
"sections.meet-the-team",
"media.video",
"sections.hero",
Expand All @@ -94,7 +96,10 @@
"sections.disclaimer",
"sections.richtext",
"sections.dynamic-case-studies-grid",
"sections.dynamic-features-grid"
"sections.dynamic-features-grid",
"sections.feature-overview",
"sections.tabbed-feature-overview",
"sections.news-list"
]
},
"children": {
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/blog/editors-picks.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"blogPosts": {
"type": "relation",
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/blog/resource-cta.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"type": "string"
},
"title": {
"type": "string",
"required": true
"type": "string"
},
"description": {
"type": "text"
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/cards/content-card.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"type": "string"
},
"title": {
"type": "string",
"required": true
"type": "string"
},
"content": {
"type": "richtext",
Expand Down
25 changes: 19 additions & 6 deletions apps/strapi/src/components/cards/feature-card.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"description": {
"type": "richtext"
Expand All @@ -26,22 +25,36 @@
},
"imagePosition": {
"type": "enumeration",
"enum": ["left", "right"],
"enum": [
"left",
"right"
],
"default": "right"
},
"variant": {
"type": "enumeration",
"enum": ["plain", "bordered"],
"enum": [
"plain",
"bordered"
],
"default": "plain"
},
"size": {
"type": "enumeration",
"enum": ["sm", "default", "lg"],
"enum": [
"sm",
"default",
"lg"
],
"default": "default"
},
"layout": {
"type": "enumeration",
"enum": ["full", "half", "third"],
"enum": [
"full",
"half",
"third"
],
"default": "full"
},
"ctaLinks": {
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/elements/conversion-feature.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
"repeatable": false
},
"label": {
"type": "string",
"required": true
"type": "string"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"description": {
"type": "text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
"type": "string"
},
"label": {
"type": "string",
"required": true
"type": "string"
},
"description": {
"type": "string"
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/elements/footer-item.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"links": {
"type": "component",
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/elements/hero-home-feature.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"icon": {
"type": "component",
Expand Down
2 changes: 0 additions & 2 deletions apps/strapi/src/components/elements/how-it-works-item.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
},
"title": {
"type": "string",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
Expand All @@ -27,7 +26,6 @@
},
"description": {
"type": "richtext",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
Expand Down
36 changes: 36 additions & 0 deletions apps/strapi/src/components/elements/tabbed-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"collectionName": "components_elements_tabbed_feature",
"info": {
"displayName": "TabbedFeature",
"icon": "tag",
"description": "Single tab inside TabbedFeatureOverview — pill label + nested feature-overview content"
},
"options": {},
"attributes": {
"tabLabel": {
"type": "string",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
}
}
},
"tabIcon": {
"type": "component",
"repeatable": false,
"component": "utilities.basic-image"
},
"content": {
"type": "component",
"repeatable": false,
"required": true,
"component": "sections.feature-overview",
"pluginOptions": {
"i18n": {
"localized": true
}
}
}
}
}
3 changes: 1 addition & 2 deletions apps/strapi/src/components/elements/team-member-item.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
"component": "utilities.basic-image"
},
"name": {
"type": "string",
"required": true
"type": "string"
},
"role": {
"type": "string",
Expand Down
1 change: 0 additions & 1 deletion apps/strapi/src/components/footer/footer-cta-badge.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
},
"text": {
"type": "string",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
Expand Down
1 change: 0 additions & 1 deletion apps/strapi/src/components/footer/footer-cta-card.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
},
"title": {
"type": "string",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
Expand Down
1 change: 0 additions & 1 deletion apps/strapi/src/components/footer/footer-cta.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"attributes": {
"heading": {
"type": "string",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/sections/community-banner.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"description": {
"type": "richtext"
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/sections/disclaimer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"content": {
"type": "text",
Expand Down
57 changes: 57 additions & 0 deletions apps/strapi/src/components/sections/feature-overview.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"collectionName": "components_sections_feature_overview",
"info": {
"displayName": "FeatureOverview",
"icon": "puzzle",
"description": "Bordered feature card with image and a 3-column item grid below"
},
"options": {},
"attributes": {
"label": {
"type": "string",
"pluginOptions": {
"i18n": {
"localized": true
}
}
},
"labelIcon": {
"type": "component",
"repeatable": false,
"component": "utilities.basic-image"
},
"title": {
"type": "string",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
}
}
},
"description": {
"type": "text",
"pluginOptions": {
"i18n": {
"localized": true
}
}
},
"ctaLinks": {
"type": "component",
"repeatable": true,
"component": "utilities.link"
},
"image": {
"type": "component",
"repeatable": false,
"component": "utilities.basic-image",
"required": true
},
"items": {
"type": "component",
"repeatable": true,
"component": "elements.how-it-works-item"
}
}
}
3 changes: 1 addition & 2 deletions apps/strapi/src/components/sections/hero-home.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"options": {},
"attributes": {
"title": {
"type": "string",
"required": true
"type": "string"
},
"cta": {
"type": "component",
Expand Down
3 changes: 1 addition & 2 deletions apps/strapi/src/components/sections/hero.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"type": "string"
},
"title": {
"type": "string",
"required": true
"type": "string"
},
"description": {
"type": "richtext"
Expand Down
Loading
Loading