Add migration scripts and preload participation awards script

- Created migration index file to manage database migrations.
- Added script to preload participation awards and related blog posts into the CMS.
- Introduced new types and default data for application phases.
- Added award articles data structure for the upcoming awards.
This commit is contained in:
syntaxbullet
2026-06-30 19:10:53 +02:00
parent 9545f2513d
commit 7ff405d1a2
47 changed files with 68728 additions and 986 deletions

View File

@@ -11,7 +11,7 @@ import Image from '@/spa/components/ui/UnoptimizedImage'
const NAVY = '#111D55';
const GOLD = '#EFBF04';
const CREAM = '#EFE5E3';
const PAGE_BG = '#fff';
const INK = '#3A3A3A';
const FF = '"IBM Plex Sans", sans-serif';
const FB = '"Inter", sans-serif';
@@ -81,10 +81,21 @@ export default function BlogDetail() {
const metaCopy = detail.metaCopy || blogDetailContent.meta;
const sidebarCta = detail.sidebarCta || blogDetailContent.sidebarCta;
const relatedCopy = detail.related || blogDetailContent.related;
const sidebarCtaUrl = fallbackText(sidebarCta.url, blogDetailContent.sidebarCta.url);
const sidebarCtaIsExternal = /^(mailto:|tel:|https?:)/.test(sidebarCtaUrl);
const sidebarCtaStyle: React.CSSProperties = {
display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
padding: '13px 20px',
background: NAVY, color: '#fff',
fontFamily: FF, fontSize: 14, fontWeight: 700,
textTransform: 'uppercase', letterSpacing: '0.1em',
textDecoration: 'none',
transition: 'background 0.15s',
};
if (!article) {
return (
<div style={{ minHeight: '60vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 24, padding: '80px 24px', background: CREAM }}>
<div style={{ minHeight: '60vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 24, padding: '80px 24px', background: PAGE_BG }}>
<h2 style={{ fontFamily: FF, fontSize: '2rem', fontWeight: 900, color: NAVY }}>{fallbackText(notFound.title, blogDetailContent.notFound.title)}</h2>
<Link to={fallbackText(notFound.backUrl, blogDetailContent.notFound.backUrl)} style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: NAVY, textDecoration: 'none', display: 'flex', alignItems: 'center', gap: 6 }}>
<ArrowLeft size={14} /> {fallbackText(notFound.backLabel, blogDetailContent.notFound.backLabel)}
@@ -177,7 +188,7 @@ export default function BlogDetail() {
</section>
{/* ── ARTICLE BODY ─────────────────────────────────────────────────── */}
<section style={{ background: CREAM, padding: isMobile ? '48px 0 0' : '80px 0 0' }}>
<section style={{ background: PAGE_BG, padding: isMobile ? '48px 0 0' : '80px 0 0' }}>
<div style={{ maxWidth: 1100, margin: '0 auto', padding: isMobile ? '0 24px' : '0 80px', display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 320px', gap: isMobile ? 40 : 80 }}>
{/* LEFT – article text */}
@@ -263,22 +274,25 @@ export default function BlogDetail() {
<p style={{ fontFamily: FB, fontSize: 16, color: 'rgba(58,58,58,0.55)', lineHeight: 1.7, marginBottom: 20 }}>
{fallbackText(sidebarCta.text, blogDetailContent.sidebarCta.text)}
</p>
<Link
to={fallbackText(sidebarCta.url, blogDetailContent.sidebarCta.url)}
style={{
display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
padding: '13px 20px',
background: NAVY, color: '#fff',
fontFamily: FF, fontSize: 14, fontWeight: 700,
textTransform: 'uppercase', letterSpacing: '0.1em',
textDecoration: 'none',
transition: 'background 0.15s',
}}
onMouseEnter={e => (e.currentTarget.style.background = GOLD)}
onMouseLeave={e => (e.currentTarget.style.background = NAVY)}
>
{fallbackText(sidebarCta.label, blogDetailContent.sidebarCta.label)} <ArrowRight size={12} />
</Link>
{sidebarCtaIsExternal ? (
<a
href={sidebarCtaUrl}
style={sidebarCtaStyle}
onMouseEnter={e => (e.currentTarget.style.background = GOLD)}
onMouseLeave={e => (e.currentTarget.style.background = NAVY)}
>
{fallbackText(sidebarCta.label, blogDetailContent.sidebarCta.label)} <ArrowRight size={12} />
</a>
) : (
<Link
to={sidebarCtaUrl}
style={sidebarCtaStyle}
onMouseEnter={e => (e.currentTarget.style.background = GOLD)}
onMouseLeave={e => (e.currentTarget.style.background = NAVY)}
>
{fallbackText(sidebarCta.label, blogDetailContent.sidebarCta.label)} <ArrowRight size={12} />
</Link>
)}
</div>
</div>
</aside>