Skip to content

Add contract templates and onboarding functionality#330

Merged
Starefossen merged 24 commits intomainfrom
sponsor-contracts
Feb 12, 2026
Merged

Add contract templates and onboarding functionality#330
Starefossen merged 24 commits intomainfrom
sponsor-contracts

Conversation

@Starefossen
Copy link
Member

@Starefossen Starefossen commented Feb 11, 2026

User description

Implement contract templates management, dynamic contract generation, and an onboarding process for sponsors. Enhance the sponsor data model and introduce new schemas to support these features. Update the router and components to facilitate contract template operations and onboarding submissions.


PR Type

Enhancement, Tests, Documentation


Description

  • Implement comprehensive contract templates management system with CRUD operations, dynamic PDF generation, and template selection logic based on tier, language, and defaults

  • Add sponsor self-service onboarding workflow with token-based access, form validation, and completion tracking

  • Extend sponsor data model with signature tracking (signatureStatus, signatureId, signerEmail), contract document storage, and onboarding fields (onboardingToken, onboardingComplete, onboardingCompletedAt)

  • Create new tRPC routers for contract templates and onboarding with endpoints for validation, submission, token generation, and PDF generation

  • Add contract readiness validation to check required fields across organizer, sponsor, and pipeline sources

  • Implement contract variable building and formatting utilities for date, currency, and optional field handling

  • Add activity logging for signature status changes and onboarding completion events

  • Create admin UI components for contract template editing and listing, and sponsor onboarding form component

  • Extend Sanity schemas for contractTemplate document type and organizer details in conference schema

  • Add comprehensive test suites for contract variables, contract readiness, and onboarding URL building

  • Establish Storybook documentation infrastructure with configuration, font setup, and extensive component stories for sponsor system, design system, and admin interfaces

  • Update ESLint configuration and TRPC client logging for Storybook compatibility

  • Add migration script to backfill signature and onboarding field defaults

  • Consolidate and reorganize documentation with updated SPONSOR_SYSTEM.md and new design system documentation


Diagram Walkthrough

flowchart LR
  A["Sponsor Data Model"] -->|extends| B["Signature & Contract Fields"]
  A -->|extends| C["Onboarding Fields"]
  D["Contract Templates"] -->|generates| E["Contract Variables"]
  E -->|formats| F["PDF Contract"]
  G["Onboarding Token"] -->|validates| H["Onboarding Form"]
  H -->|completes| I["Sponsor Onboarding"]
  J["Contract Readiness"] -->|checks| K["Required Fields"]
  L["Admin UI"] -->|manages| D
  L -->|submits| H
  M["Activity Logging"] -->|tracks| B
  M -->|tracks| I
  N["Storybook"] -->|documents| L
  N -->|documents| H
Loading

File Walkthrough

Relevant files
Tests
3 files
contract-variables.test.ts
Contract variables test suite with comprehensive coverage

tests/lib/sponsor-crm/contract-variables.test.ts

  • Comprehensive test suite for contract variable building functionality
  • Tests variable generation from sponsor, contact, tier, and conference
    data
  • Validates currency formatting, date formatting, and optional field
    handling
  • Tests CONTRACT_VARIABLE_DESCRIPTIONS constant for variable
    documentation
+285/-0 
contract-readiness.test.ts
Contract readiness validation test suite                                 

tests/lib/sponsor-crm/contract-readiness.test.ts

  • Test suite for contract readiness checking functionality
  • Tests detection of missing fields across organizer, sponsor, and
    pipeline sources
  • Validates grouping of missing fields by source
  • Tests edge cases like missing contact persons and optional conference
    fields
+319/-0 
onboarding.test.ts
Onboarding URL builder test suite                                               

tests/lib/sponsor-crm/onboarding.test.ts

  • Test suite for onboarding URL building functionality
  • Tests correct URL construction with base URL and token
  • Tests handling of base URLs without trailing slashes
+23/-0   
Enhancement
22 files
sponsor.ts
Contract and signature management router endpoints             

src/server/routers/sponsor.ts

  • Added updateSignatureStatus mutation to track digital signature status
    changes
  • Integrated contract template management router with CRUD operations
  • Added contractReadiness query to check sponsor contract readiness
  • Added generatePdf mutation for dynamic contract PDF generation
  • Extended sponsor update to handle signerEmail and contractTemplate
    fields
+274/-0 
contract-templates.ts
Contract template management and retrieval functions         

src/lib/sponsor-crm/contract-templates.ts

  • New module for contract template management (CRUD operations)
  • Implements template selection logic with scoring (tier, language,
    default)
  • Provides getTermsForConference to fetch general terms and conditions
  • Supports template sections with portable text blocks and variable
    substitution
+268/-0 
contract-variables.ts
Contract variable building and formatting utilities           

src/lib/sponsor-crm/contract-variables.ts

  • Builds contract variables from sponsor, contact, tier, and conference
    data
  • Formats dates and currency values for contract templates
  • Provides CONTRACT_VARIABLE_DESCRIPTIONS for UI documentation
  • Handles optional fields gracefully with undefined values
+169/-0 
onboarding.ts
Sponsor self-service onboarding implementation                     

src/lib/sponsor-crm/onboarding.ts

  • Implements sponsor self-service onboarding workflow with token-based
    access
  • Generates unique onboarding tokens and validates them
  • Completes onboarding by updating contact persons, billing, and sponsor
    info
  • Provides buildOnboardingUrl helper for generating onboarding portal
    links
+148/-0 
contract-readiness.ts
Contract readiness validation logic                                           

src/lib/sponsor-crm/contract-readiness.ts

  • Checks contract readiness by validating required fields across three
    sources
  • Organizer fields: organizer name, org number, address, dates, venue,
    email
  • Sponsor fields: org number, address, primary contact person
  • Pipeline fields: tier and contract value
+132/-0 
sponsorForConference.ts
Signature, contract, and onboarding fields to sponsor schema

sanity/schemaTypes/sponsorForConference.ts

  • Added signature status tracking fields (signatureStatus, signatureId,
    signerEmail)
  • Added contract document storage and reminder count fields
  • Added contract template reference field
  • Added onboarding fields (onboardingToken, onboardingComplete,
    onboardingCompletedAt)
+84/-0   
types.ts
Type definitions for signatures, contracts, and onboarding

src/lib/sponsor-crm/types.ts

  • Added SignatureStatus type with states: not-started, pending, signed,
    rejected, expired
  • Extended ActivityType with signature_status_change,
    onboarding_complete, contract_reminder_sent
  • Added signature and contract fields to SponsorForConference interface
  • Added onboarding fields to both base and expanded sponsor types
+55/-0   
utils.ts
Activity and action item icon/color utilities                       

src/components/admin/sponsor-crm/utils.ts

  • Added icon mappings for new activity types (signature_status_change,
    onboarding_complete, contract_reminder_sent)
  • Added color schemes for new activity types
  • Added action item types for signature rejection, expiration, and
    pending onboarding
  • Added icon and color utilities for new action item types
+30/-0   
onboarding.ts
Onboarding router with token and submission endpoints       

src/server/routers/onboarding.ts

  • New router for sponsor onboarding workflow
  • validate endpoint to check onboarding token validity
  • complete endpoint to submit onboarding form data
  • generateToken admin endpoint to create onboarding tokens with URLs
+81/-0   
sanity.ts
Extended Sanity query fields for sponsor relationships     

src/lib/sponsor-crm/sanity.ts

  • Extended SPONSOR_FOR_CONFERENCE_FIELDS query to include new signature
    and onboarding fields
  • Added conference organizer details (name, org number, address)
  • Added conference venue and date information
  • Added contract template reference and document fields
+41/-3   
action-items.ts
Action item generation for signature and onboarding events

src/lib/sponsor-crm/action-items.ts

  • Added action item generation for rejected digital signatures (priority
    2.5)
  • Added action item generation for expired digital signatures (priority
    3.5)
  • Added action item generation for pending onboarding (priority 6)
  • Integrated new action item types into existing priority system
+52/-0   
activity.ts
Activity logging for signature and onboarding events         

src/lib/sponsor-crm/activity.ts

  • Added logSignatureStatusChange function to track signature status
    transitions
  • Added logOnboardingComplete function to log onboarding completion
  • Both functions create activity records with timestamps and status
    values
+34/-0   
conference.ts
Organizer details fields to conference schema                       

sanity/schemaTypes/conference.ts

  • Added organizerOrgNumber field for organizer organization number
  • Added organizerAddress field for organizer registered address
  • Both fields used in contracts and invoices
+16/-0   
sponsor.ts
Sponsor address field for contract generation                       

sanity/schemaTypes/sponsor.ts

  • Added address field for registered company address
  • Field is hidden from non-admin users
  • Used in contract generation
+14/-0   
_app.ts
Register onboarding router in main app                                     

src/server/_app.ts

  • Imported new onboardingRouter
  • Registered onboarding router in main app router
+2/-0     
client.ts
Reduce TRPC client logging verbosity                                         

src/lib/trpc/client.ts

  • Modified logger link condition to only log errors in production
  • Removed development environment logging to reduce noise
+1/-2     
types.ts
Organizer details to conference type definition                   

src/lib/conference/types.ts

  • Added organizerOrgNumber optional field to Conference interface
  • Added organizerAddress optional field to Conference interface
+2/-0     
tailwind.css
Atkinson Hyperlegible font utility class                                 

src/styles/tailwind.css

  • Added .font-atkinson utility class for Atkinson Hyperlegible font
  • Uses CSS custom property --font-family-atkinson
+4/-0     
ContractTemplateEditorPage.tsx
Contract template editor admin page component                       

src/components/admin/sponsor/ContractTemplateEditorPage.tsx

  • New admin page component for creating and editing contract templates
  • Supports template settings (name, language, currency, tier
    association, header/footer text)
  • Collapsible reference section showing available template variables
    with descriptions
  • Dynamic section management with add, remove, and reorder functionality
  • Rich text editing for contract sections and general terms & conditions
  • Form validation and mutation handling with success/error notifications
+572/-0 
SponsorOnboardingForm.tsx
Sponsor self-service onboarding form component                     

src/components/sponsor/SponsorOnboardingForm.tsx

  • New sponsor self-service onboarding form component with token-based
    access
  • Collects company information (org number, address), contact persons,
    and billing details
  • Dynamic contact person management with primary contact designation
  • Form validation and error handling with user-friendly messages
  • Integrates with onboarding tRPC endpoints for token validation and
    submission
  • Displays completion state and handles already-completed onboarding
+468/-0 
contract-pdf.tsx
Contract PDF Generation Implementation                                     

src/lib/sponsor-crm/contract-pdf.tsx

  • New module for generating PDF contracts using react-pdf renderer
  • Implements generateContractPdf function that builds contract documents
    from templates and variable context
  • Includes helper components for rendering portable text blocks, info
    tables, event details, and package details
  • Supports multi-page documents with signature areas and appendices for
    terms and conditions
+462/-0 
ContractTemplateListPage.tsx
Contract Template List Management Page                                     

src/components/admin/sponsor/ContractTemplateListPage.tsx

  • New client component for displaying and managing contract templates
  • Implements template listing table with columns for title, language,
    tier, and status
  • Includes delete functionality with confirmation modal and
    success/error notifications
  • Provides "New Template" button and empty state with call-to-action
+205/-0 
Miscellaneous
4 files
sponsor-data.ts
Mock sponsor data factories for Storybook                               

src/mocks/sponsor-data.ts

  • Mock data factories for sponsor-related components in Storybook
  • Provides mockSponsor, mockContactPerson, mockBillingInfo factories
  • Includes mockReadinessReady and mockReadinessMissing for contract
    readiness
  • Exports mockSponsors object with various pipeline stage examples
+180/-0 
validate-stories.ts
Storybook story validation script                                               

scripts/validate-stories.ts

  • Validation script for Storybook story files
  • Checks for proper meta exports and story definitions
  • Reports warnings and errors with detailed diagnostics
  • Provides summary statistics on story validation
+124/-0 
index.ts
Documentation components barrel export                                     

src/docs/components/index.ts

  • New barrel export file for documentation components
  • Exports ButtonShowcase, ColorSwatch, and TypographyShowcase components
+3/-0     
test-storybook.sh
Storybook testing script                                                                 

scripts/test-storybook.sh

  • Bash script to start Storybook server and run tests
  • Waits for Storybook to be ready before running tests
  • Handles cleanup and exit codes properly
+31/-0   
Configuration changes
9 files
contractTemplate.ts
Sanity schema for contract template documents                       

sanity/schemaTypes/contractTemplate.ts

  • New Sanity schema type for contract templates
  • Supports language selection (Norwegian/English) and currency
    configuration
  • Defines contract sections with heading and portable text body
  • Includes header/footer text, general terms, and active/default flags
+163/-0 
contractTemplate.ts
Zod validation schemas for contract templates                       

src/server/schemas/contractTemplate.ts

  • Zod schemas for contract template CRUD operations
  • Schemas for input, update, list, and PDF generation
  • Schema for finding best template by conference, tier, and language
  • Validates required fields and array constraints
+60/-0   
sponsorForConference.ts
Zod schemas for signature and contract fields                       

src/server/schemas/sponsorForConference.ts

  • Added SignatureStatusSchema enum with five status values
  • Extended sponsor input/update schemas with signature and contract
    fields
  • Added UpdateSignatureStatusSchema for signature status mutations
  • Added validation for signer email and contract template references
+19/-0   
onboarding.ts
Zod validation schemas for onboarding workflow                     

src/server/schemas/onboarding.ts

  • Zod schemas for onboarding workflow validation
  • OnboardingTokenSchema for token validation
  • OnboardingSubmissionSchema for form submission with contact persons
    and billing
  • GenerateOnboardingTokenSchema for admin token generation
+36/-0   
main.ts
Storybook configuration for component documentation           

.storybook/main.ts

  • New Storybook configuration file for Next.js with Vite
  • Configures story file discovery and addon setup
  • Enables TypeScript support with react-docgen
  • Disables telemetry and configures sidebar onboarding
+36/-0   
index.ts
Migration for signature and onboarding field defaults       

migrations/033-add-signature-onboarding-fields/index.ts

  • Sanity migration to backfill new signature and onboarding fields
  • Sets default values: signatureStatus to 'not-started', reminderCount
    to 0, onboardingComplete to false
  • Applies to all existing sponsorForConference documents
+21/-0   
schema.ts
Register contract template schema type                                     

sanity/schema.ts

  • Imported new contractTemplate schema type
  • Added contractTemplate to schema types array
+2/-0     
preview-head.html
Storybook font configuration and CSS variables                     

.storybook/preview-head.html

  • Loads Google Fonts for Storybook (Inter, JetBrains Mono, Space
    Grotesk, IBM Plex, Bricolage, Atkinson)
  • Defines CSS custom properties for font families
  • Enables consistent typography across Storybook stories
+19/-0   
eslint.config.js
ESLint configuration for Storybook                                             

eslint.config.js

  • Added Storybook ESLint plugin configuration
  • Configured flat/recommended rules for Storybook files
  • Added storybook-static to ignore patterns
+5/-0     
Documentation
12 files
SponsorSystem.stories.tsx
Sponsor system architecture documentation in Storybook     

src/docs/SponsorSystem.stories.tsx

  • Comprehensive Storybook documentation for sponsor system architecture
  • Documents admin interface pages, system domains, and data model
  • Includes feature overview and technology stack information
  • Provides visual workflow diagrams for pipeline and contract processes
+429/-0 
SpeakerComponents.stories.tsx
Speaker component system Storybook documentation                 

src/docs/design-system/examples/SpeakerComponents.stories.tsx

  • New Storybook story file showcasing speaker component system with live
    examples
  • Demonstrates SpeakerAvatars, SpeakerAvatarsWithNames, and
    ClickableSpeakerNames components
  • Includes mock speaker data and comprehensive documentation of
    component variants
  • Provides layout patterns, props reference table, and usage examples
+627/-0 
SPONSOR_SYSTEM.md
Contract templates and sponsor onboarding system documentation

docs/SPONSOR_SYSTEM.md

  • Added address field to sponsor document schema for contract generation
  • Introduced new contractTemplate document type with sections,
    variables, and terms support
  • Extended sponsorForConference with signature tracking fields
    (signatureStatus, signatureId, signerEmail, contractSentAt,
    contractDocument, reminderCount, contractTemplate)
  • Added onboarding fields (onboardingToken, onboardingComplete,
    onboardingCompletedAt)
  • Updated activity types to include signature_status_change,
    onboarding_complete, contract_reminder_sent
  • Added contract template CRUD procedures and contract generation
    endpoints to tRPC router
  • Documented contract system architecture, readiness validation, and
    sponsor self-service onboarding flow
  • Updated file structure and test coverage documentation
  • Marked issues Schema Extensions for Contract Signing #300 and Sponsor Self-Service Onboarding Portal #306 as Done
+159/-57
SponsorTierEditor.stories.tsx
Sponsor tier editor Storybook documentation                           

src/components/admin/sponsor/SponsorTierEditor.stories.tsx

  • New Storybook story file documenting sponsor tier editor modal
    component
  • Includes interactive tier editor form with pricing, perks, and flags
    management
  • Demonstrates tier card listing with status indicators (sold out, most
    popular)
  • Provides documentation of component props and tier types (standard,
    community, media)
+428/-0 
Patterns.stories.tsx
Cloud Native pattern system Storybook documentation           

src/docs/design-system/brand/Patterns.stories.tsx

  • New Storybook story documenting Cloud Native Pattern system with CNCF
    project icons
  • Explains focus/diffusion technology for icon sizing and opacity
    effects
  • Documents pattern variants (dark, light, brand) and configuration
    presets
  • Lists available CNCF project icons across multiple categories
  • Provides live pattern demo and usage examples
+338/-0 
AdminOverview.stories.tsx
Admin System Overview Documentation Story                               

src/docs/AdminOverview.stories.tsx

  • New Storybook documentation file for admin system overview
  • Documents access control patterns and middleware authentication
  • Provides usage examples for shared admin components (EmailModal,
    ConfirmationModal, NotificationProvider, AdminPageHeader,
    FilterDropdown, LoadingSkeleton)
  • Includes best practices section covering tRPC usage, error handling,
    dark mode support, and responsive design
+334/-0 
SponsorThankYou.stories.tsx
Sponsor Thank You Card Component Stories                                 

src/components/SponsorThankYou.stories.tsx

  • New Storybook story file for SponsorThankYou component with six visual
    variants
  • Implements sponsor recognition cards with cloud-native themed
    gradients and messaging
  • Includes QR code section and responsive container queries for scaling
  • Provides mock data and variant configurations for code-heroes,
    cloud-wizards, tech-ninjas, deploy-legends, kubernetes-masters, and
    devops-rockstars themes
+312/-0 
SponsorCRMPipeline.stories.tsx
Sponsor CRM Pipeline Kanban Board Stories                               

src/components/admin/sponsor-crm/SponsorCRMPipeline.stories.tsx

  • New Storybook story documenting the SponsorCRMPipeline Kanban board
    component
  • Includes default pipeline view with mock sponsors across five status
    columns (Prospect, Contacted, Negotiating, Confirmed, Declined)
  • Provides documentation story explaining props, view modes (Pipeline,
    Tier, Invoice), and features
  • Shows search, filter, and view mode toggle UI patterns
+352/-0 
SponsorComponentIndex.stories.tsx
Sponsor Components Index and Navigation                                   

src/docs/SponsorComponentIndex.stories.tsx

  • Comprehensive component index documentation for all sponsor-related
    components
  • Organizes components into categories: Public Display, Dashboard, CRM
    Pipeline, Forms, Contact Management, Email & Communication, Tier
    Management, Onboarding & Contract, and Utilities
  • Provides component cards with paths, descriptions, and story
    availability indicators
  • Serves as navigation hub for sponsor system documentation
+374/-0 
SponsorCRMForm.stories.tsx
Sponsor CRM Form Modal Stories                                                     

src/components/admin/sponsor-crm/SponsorCRMForm.stories.tsx

  • New Storybook story for SponsorCRMForm modal component
  • Demonstrates form layout with sponsor selection, pipeline status, tier
    selection, contract value, tags, and notes fields
  • Includes tabbed interface for Pipeline and History views
  • Provides documentation with props, form sections, and tab descriptions
+323/-0 
SponsorTierManagement.stories.tsx
Sponsor Tier Management Component Stories                               

src/components/admin/sponsor/SponsorTierManagement.stories.tsx

  • New Storybook story for SponsorTierManagement component showing
    sponsors grouped by tier
  • Displays tier sections with color-coded badges (Platinum, Gold,
    Silver, Bronze)
  • Includes sponsor cards with edit, download, and delete actions
  • Provides documentation with props, features, and usage examples
+339/-0 
MobileFilterSheet.stories.tsx
Mobile Filter Sheet Component Stories                                       

src/components/admin/sponsor-crm/MobileFilterSheet.stories.tsx

  • New Storybook story for MobileFilterSheet bottom sheet component
  • Implements touch-friendly filter UI with status, tier, and tags filter
    sections
  • Includes interactive demo with filter state management and active
    filter count badge
  • Provides documentation explaining responsive design and
    mobile-specific features
+326/-0 
Refactoring
1 files
PhotoGalleryWithDownload.tsx
Update photo gallery download component import                     

src/components/admin/PhotoGalleryWithDownload.tsx

  • Updated import to use renamed DownloadableImage component instead of
    DownloadSpeakerImage
  • Maintains same functionality while using more generic component name
+2/-2     
Additional files
95 files
chromatic.yml +57/-0   
deploy-storybook.yml +81/-0   
pr-checks.yml +44/-0   
.env +10/-0   
TRPCDecorator.tsx +35/-0   
preview.tsx +130/-0 
AGENTS.md +127/-2 
BRANDING.md +0/-376 
STORYBOOK_STRUCTURE.md +263/-0 
knip.json +10/-3   
package.json +24/-1   
pnpm-lock.yaml +2225/-16
README.md +0/-56   
page.tsx +7/-7     
page.tsx +34/-0   
page.tsx +28/-0   
page.tsx +28/-0   
page.tsx +0/-4351
page.tsx +17/-0   
page.tsx +113/-0 
BackLink.stories.tsx +91/-0   
Button.stories.tsx +232/-0 
CollapsibleDescription.stories.tsx +116/-0 
Container.stories.tsx +60/-0   
Logo.stories.tsx +185/-0 
ShowMore.stories.tsx +201/-0 
ShowMore.tsx +4/-1     
SpeakerAvatars.stories.tsx +230/-0 
SponsorLogo.stories.tsx +108/-0 
Sponsors.stories.tsx +166/-0 
ThemeToggle.stories.tsx +141/-0 
VideoEmbed.stories.tsx +113/-0 
EmailModal.tsx +2/-2     
MemeGeneratorWithDownload.tsx +2/-2     
ProposalCard.stories.tsx +278/-0 
ProposalStatistics.stories.tsx +246/-0 
BoardViewSwitcher.stories.tsx +51/-0   
ContractReadinessIndicator.stories.tsx +172/-0 
ContractReadinessIndicator.tsx +86/-0   
ImportHistoricSponsorsButton.stories.tsx +220/-0 
OnboardingLinkButton.stories.tsx +132/-0 
OnboardingLinkButton.tsx +109/-0 
SponsorBoardColumn.stories.tsx +205/-0 
SponsorBulkActions.stories.tsx +94/-0   
SponsorCRMForm.tsx +14/-0   
SponsorCard.stories.tsx +211/-0 
AddonsCheckboxGroup.stories.tsx +137/-0 
ContractValueInput.stories.tsx +138/-0 
OrganizerCombobox.stories.tsx +264/-0 
SponsorCombobox.stories.tsx +257/-0 
StatusListbox.stories.tsx +207/-0 
TagCombobox.stories.tsx +77/-0   
TierRadioGroup.stories.tsx +101/-0 
SponsorActionItems.stories.tsx +258/-0 
SponsorActivityTimeline.stories.tsx +263/-0 
SponsorAddModal.stories.tsx +291/-0 
SponsorContactActions.stories.tsx +165/-0 
SponsorContactEditor.stories.tsx +350/-0 
SponsorContactTable.stories.tsx +338/-0 
SponsorDashboardMetrics.stories.tsx +255/-0 
SponsorIndividualEmailModal.stories.tsx +259/-0 
SponsorLogoEditor.stories.tsx +244/-0 
BrandingExampleHeroSection.tsx +0/-57   
BrandingHeroSection.tsx +0/-41   
ExpandableEmailTemplate.tsx +0/-114 
IconShowcase.tsx +0/-36   
InteractivePatternPreview.tsx +0/-246 
PatternExample.tsx +0/-73   
index.ts +0/-10   
SpeakerShareWrapper.tsx +1/-1     
ContactRoleSelect.stories.tsx +141/-0 
DownloadableImage.tsx +3/-3     
MissingAvatar.stories.tsx +213/-0 
OSIcons.stories.tsx +209/-0 
SpeakerSharingActions.tsx [link]   
DeveloperGuide.stories.tsx +195/-0 
Welcome.stories.tsx +225/-0 
ButtonShowcase.tsx [link]   
ColorSwatch.tsx +1/-1     
TypographyShowcase.tsx +1/-1     
Colors.stories.tsx +170/-0 
Icons.stories.tsx +313/-0 
Shadows.stories.tsx +149/-0 
Spacing.stories.tsx +186/-0 
Typography.stories.tsx +235/-0 
BrandStory.stories.tsx +212/-0 
Buttons.stories.tsx +33/-0   
ColorPalette.stories.tsx +165/-0 
TypographySystem.stories.tsx +220/-0 
data.ts [link]   
EmailTemplates.stories.tsx +355/-0 
HeroSections.stories.tsx +264/-0 
README.md +0/-5     
README.md +0/-135 
README.md +0/-121 

- Implemented contract templates management with CRUD operations.
- Added contract variables and context for dynamic contract generation.
- Created onboarding process for sponsors, including token generation and validation.
- Enhanced sponsor data model to include onboarding status and contract template references.
- Introduced new schemas for contract templates and onboarding submissions.
- Updated sponsor router to handle contract template operations and onboarding processes.
@vercel
Copy link

vercel bot commented Feb 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cloudnativedays Ready Ready Preview, Comment Feb 12, 2026 8:17pm

Request Review

…em examples

- Introduced HeroSections.stories.tsx with various hero section examples including Brand Gradient, Dark Slate, and Light Hero with Border.
- Added SpeakerComponents.stories.tsx showcasing SpeakerAvatars, SpeakerAvatarsWithNames, and ClickableSpeakerNames components with mock data.
- Updated TRPC client configuration to log errors only when they occur in the down direction.
- Enhanced Tailwind CSS styles by adding a new font class for Atkinson font.
@qodo-code-review
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 Security concerns

Token security:
src/server/routers/onboarding.ts exposes public procedures (validate, complete) gated only by a UUID token. The token has no expiry/rotation policy in validateOnboardingToken/generateOnboardingToken, and remains valid until overwritten; if leaked (logs, referrers, forwarded URLs), anyone can update sponsor contact/billing data. Consider adding an expiry (onboardingTokenCreatedAt + max age), single-use semantics (unset token after successful completion), rate limiting, and ensuring tokens are never logged. Additionally, buildOnboardingUrl uses NEXT_PUBLIC_BASE_URL with a fallback; verify this cannot be attacker-controlled in the deployment environment to avoid generating onboarding links to an unexpected domain.

⚡ Recommended focus areas for review

Atomicity

The signature status update performs multiple sequential Sanity patch commits (first setting signatureStatus, then conditionally setting contractStatus/contractSignedAt). If the second commit fails, data can become inconsistent (e.g., signatureStatus is signed but contractStatus not updated). Consider combining into a single patch/transaction or ensuring a single commit with conditional fields computed beforehand.

updateSignatureStatus: adminProcedure
  .input(UpdateSignatureStatusSchema)
  .mutation(async ({ input, ctx }) => {
    const { sponsorForConference: existing } =
      await getSponsorForConference(input.id)

    if (!existing) {
      throw new TRPCError({
        code: 'NOT_FOUND',
        message: 'Sponsor relationship not found',
      })
    }

    const oldStatus = existing.signatureStatus || 'not-started'

    await clientWrite
      .patch(input.id)
      .set({ signatureStatus: input.newStatus })
      .commit()

    if (input.newStatus === 'signed') {
      await clientWrite
        .patch(input.id)
        .set({
          contractStatus: 'contract-signed',
          contractSignedAt: getCurrentDateTime(),
        })
        .commit()
    }
Date Handling

formatDate uses new Date(dateStr) and toLocaleDateString, and TODAY_DATE is derived from new Date().toISOString(). This can be timezone-sensitive (date-only strings like 2026-06-10 may parse as UTC vs local depending on runtime) and can cause off-by-one day issues in some environments. Consider parsing date-only values explicitly (e.g., treat as local date) and make formatting locale/timezone rules explicit/consistent across server/CI.

function formatDate(dateStr: string): string {
  const date = new Date(dateStr)
  return date.toLocaleDateString('en-GB', {
    day: 'numeric',
    month: 'long',
    year: 'numeric',
  })
}

function formatCurrency(amount: number, currency: string): string {
  return new Intl.NumberFormat('nb-NO', {
    style: 'currency',
    currency,
    minimumFractionDigits: 0,
    maximumFractionDigits: 0,
  }).format(amount)
}

export function buildContractVariables(
  ctx: ContractVariableContext,
): Record<string, string> {
  const vars: Record<string, string> = {
    SPONSOR_NAME: ctx.sponsor.name,
    CONFERENCE_TITLE: ctx.conference.title,
    TODAY_DATE: formatDate(new Date().toISOString()),
  }

  if (ctx.sponsor.orgNumber) {
    vars.SPONSOR_ORG_NUMBER = ctx.sponsor.orgNumber
  }
  if (ctx.sponsor.address) {
    vars.SPONSOR_ADDRESS = ctx.sponsor.address
  }
  if (ctx.sponsor.website) {
    vars.SPONSOR_WEBSITE = ctx.sponsor.website
  }

  if (ctx.contactPerson) {
    vars.CONTACT_NAME = ctx.contactPerson.name
    vars.CONTACT_EMAIL = ctx.contactPerson.email
  }

  if (ctx.tier) {
    vars.TIER_NAME = ctx.tier.title
    if (ctx.tier.tagline) {
      vars.TIER_TAGLINE = ctx.tier.tagline
    }
  }

  if (ctx.addons && ctx.addons.length > 0) {
    vars.ADDONS_LIST = ctx.addons.map((a) => a.title).join(', ')
  }

  const currency = ctx.contractCurrency || 'NOK'
  vars.CONTRACT_CURRENCY = currency

  if (ctx.contractValue != null) {
    vars.CONTRACT_VALUE = formatCurrency(ctx.contractValue, currency)
    vars.CONTRACT_VALUE_NUMBER = String(ctx.contractValue)
  }

  if (ctx.conference.startDate) {
    vars.CONFERENCE_DATE = formatDate(ctx.conference.startDate)
    vars.CONFERENCE_YEAR = ctx.conference.startDate.slice(0, 4)

    if (ctx.conference.endDate) {
      const start = new Date(ctx.conference.startDate)
      const end = new Date(ctx.conference.endDate)
      if (start.getMonth() === end.getMonth()) {
        vars.CONFERENCE_DATES = `${start.getDate()}\u2013${end.toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })}`
      } else {
        vars.CONFERENCE_DATES = `${formatDate(ctx.conference.startDate)} \u2013 ${formatDate(ctx.conference.endDate)}`
      }
    } else {
      vars.CONFERENCE_DATES = vars.CONFERENCE_DATE
    }
Consistency

completeOnboarding updates the sponsorForConference document first, then optionally updates the referenced sponsor document in a separate read + patch. If the second update fails, onboarding is marked complete but sponsor fields may remain stale. Consider making the two updates transactional (where possible) or surfacing partial failure to callers/retrying to avoid inconsistent state.

export async function completeOnboarding(
  token: string,
  data: OnboardingSubmission,
): Promise<{
  success?: boolean
  sponsorForConferenceId?: string
  error?: Error
}> {
  try {
    const { sponsor, error: validateError } =
      await validateOnboardingToken(token)

    if (validateError || !sponsor) {
      return { error: validateError || new Error('Invalid onboarding token') }
    }

    if (sponsor.onboardingComplete) {
      return { error: new Error('Onboarding has already been completed') }
    }

    const contactPersons = prepareArrayWithKeys(data.contactPersons, 'contact')

    await clientWrite
      .patch(sponsor._id)
      .set({
        contactPersons,
        billing: data.billing,
        onboardingComplete: true,
        onboardingCompletedAt: getCurrentDateTime(),
      })
      .commit()

    if (data.logo || data.logoBright || data.orgNumber || data.address) {
      const sfcDoc = await clientRead.fetch<{ sponsor: { _ref: string } }>(
        `*[_type == "sponsorForConference" && _id == $id][0]{ sponsor }`,
        { id: sponsor._id },
      )

      if (sfcDoc?.sponsor?._ref) {
        const sponsorUpdates: Record<string, unknown> = {}
        if (data.logo) sponsorUpdates.logo = data.logo
        if (data.logoBright) sponsorUpdates.logoBright = data.logoBright
        if (data.orgNumber) sponsorUpdates.orgNumber = data.orgNumber
        if (data.address) sponsorUpdates.address = data.address

        await clientWrite
          .patch(sfcDoc.sponsor._ref)
          .set(sponsorUpdates)
          .commit()
      }
    }

@qodo-code-review
Copy link

qodo-code-review bot commented Feb 12, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Preserve array keys on update
Suggestion Impact:The update logic for sections was changed to keep an existing _key if provided (and only generate a new one when missing), and the sections type was updated to allow optional _key.

code diff:

   sections: Array<{
+    _key?: string
     heading: string
     body?: PortableTextBlock[]
   }>
@@ -164,9 +165,9 @@
     if (data.language !== undefined) updates.language = data.language
     if (data.currency !== undefined) updates.currency = data.currency
     if (data.sections !== undefined) {
-      let keyCounter = 0
-      updates.sections = data.sections.map((s) => ({
-        _key: `section-${++keyCounter}`,
+      // Preserve existing keys when provided, generate unique keys for new sections
+      updates.sections = data.sections.map((s, index) => ({
+        _key: s._key || `section-${Date.now()}-${index}`,
         heading: s.heading,
         body: s.body,
       }))

Preserve existing _key values for Sanity array items during updates to prevent
data loss and maintain content history. Generate new keys only for new items.

src/lib/sponsor-crm/contract-templates.ts [166-173]

 if (data.sections !== undefined) {
-  let keyCounter = 0
-  updates.sections = data.sections.map((s) => ({
-    _key: `section-${++keyCounter}`,
-    heading: s.heading,
-    body: s.body,
-  }))
+  // Helper to ensure unique keys for array items, preserving existing ones.
+  const prepareSectionsWithKeys = (sections: Array<{ _key?: string }>) => {
+    let keyCounter = 0;
+    return sections.map(s => ({
+      ...s,
+      _key: s._key || `section-${Date.now()}-${++keyCounter}`,
+    }));
+  };
+  updates.sections = prepareSectionsWithKeys(data.sections as Array<{_key?: string, heading: string, body: any[] | undefined}>);
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical issue with Sanity array updates. Regenerating _keys leads to data loss and breaks the CMS content history, which is a significant bug. The proposed fix is correct and essential for data integrity.

High
Use transaction for atomic updates
Suggestion Impact:Replaced separate patch/commit calls with a single Sanity transaction that patches sponsorForConference and conditionally patches the referenced sponsor document, then commits once for atomicity. Also refactored sponsorUpdates creation to support the conditional transaction patch.

code diff:

-    await clientWrite
-      .patch(sponsor._id)
-      .set({
+    // Build sponsor updates if any company info was provided
+    const sponsorUpdates: Record<string, unknown> = {}
+    if (data.logo) sponsorUpdates.logo = data.logo
+    if (data.logoBright) sponsorUpdates.logoBright = data.logoBright
+    if (data.orgNumber) sponsorUpdates.orgNumber = data.orgNumber
+    if (data.address) sponsorUpdates.address = data.address
+
+    // Use transaction for atomic updates to both documents
+    const transaction = clientWrite.transaction()
+
+    // Always update the sponsorForConference document
+    transaction.patch(sponsor._id, {
+      set: {
         contactPersons,
         billing: data.billing,
         onboardingComplete: true,
         onboardingCompletedAt: getCurrentDateTime(),
-      })
-      .commit()
+      },
+    })
 
-    if (data.logo || data.logoBright || data.orgNumber || data.address) {
+    // Conditionally update the sponsor document if there are updates
+    if (Object.keys(sponsorUpdates).length > 0) {
       const sfcDoc = await clientRead.fetch<{ sponsor: { _ref: string } }>(
         `*[_type == "sponsorForConference" && _id == $id][0]{ sponsor }`,
         { id: sponsor._id },
       )
 
       if (sfcDoc?.sponsor?._ref) {
-        const sponsorUpdates: Record<string, unknown> = {}
-        if (data.logo) sponsorUpdates.logo = data.logo
-        if (data.logoBright) sponsorUpdates.logoBright = data.logoBright
-        if (data.orgNumber) sponsorUpdates.orgNumber = data.orgNumber
-        if (data.address) sponsorUpdates.address = data.address
-
-        await clientWrite
-          .patch(sfcDoc.sponsor._ref)
-          .set(sponsorUpdates)
-          .commit()
+        transaction.patch(sfcDoc.sponsor._ref, {
+          set: sponsorUpdates,
+        })
       }
     }
+
+    await transaction.commit()

Use a Sanity transaction to atomically update the sponsorForConference and
sponsor documents. This ensures that either both updates succeed or both fail,
preventing data inconsistency.

src/lib/sponsor-crm/onboarding.ts [110-138]

-await clientWrite
-  .patch(sponsor._id)
-  .set({
+const sfcDoc = await clientRead.fetch<{ sponsor: { _ref: string } }>(
+  `*[_type == "sponsorForConference" && _id == $id][0]{ sponsor }`,
+  { id: sponsor._id },
+)
+
+const transaction = clientWrite.transaction()
+
+transaction.patch(sponsor._id, (p) =>
+  p.set({
     contactPersons,
     billing: data.billing,
     onboardingComplete: true,
     onboardingCompletedAt: getCurrentDateTime(),
-  })
-  .commit()
+  }),
+)
 
-if (data.logo || data.logoBright || data.orgNumber || data.address) {
-  const sfcDoc = await clientRead.fetch<{ sponsor: { _ref: string } }>(
-    `*[_type == "sponsorForConference" && _id == $id][0]{ sponsor }`,
-    { id: sponsor._id },
-  )
+if (
+  sfcDoc?.sponsor?._ref &&
+  (data.logo || data.logoBright || data.orgNumber || data.address)
+) {
+  const sponsorUpdates: Record<string, unknown> = {}
+  if (data.logo) sponsorUpdates.logo = data.logo
+  if (data.logoBright) sponsorUpdates.logoBright = data.logoBright
+  if (data.orgNumber) sponsorUpdates.orgNumber = data.orgNumber
+  if (data.address) sponsorUpdates.address = data.address
 
-  if (sfcDoc?.sponsor?._ref) {
-    const sponsorUpdates: Record<string, unknown> = {}
-    if (data.logo) sponsorUpdates.logo = data.logo
-    if (data.logoBright) sponsorUpdates.logoBright = data.logoBright
-    if (data.orgNumber) sponsorUpdates.orgNumber = data.orgNumber
-    if (data.address) sponsorUpdates.address = data.address
-
-    await clientWrite
-      .patch(sfcDoc.sponsor._ref)
-      .set(sponsorUpdates)
-      .commit()
-  }
+  transaction.patch(sfcDoc.sponsor._ref, (p) => p.set(sponsorUpdates))
 }
 
+await transaction.commit()
+

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a data consistency risk by updating two separate documents non-atomically. Using a transaction is the right approach to ensure data integrity, making this a valuable improvement for the application's robustness.

Medium
Generate unique keys for PortableText

Generate unique random _key properties for Portable Text blocks and spans
instead of using array indices to ensure data stability and uniqueness as
required by Sanity.

src/components/admin/sponsor/ContractTemplateEditorPage.tsx [475-488]

-const blocks = lines.map((line, i) => ({
-  _type: 'block',
-  _key: `p-${i}`,
-  style: 'normal',
-  children: [
-    {
-      _type: 'span',
-      _key: `s-${i}`,
-      text: line,
-      marks: [],
-    },
-  ],
-  markDefs: [],
-}))
+const blocks = lines.map((line) => {
+  const blockKey = `p-${Math.random().toString(36).slice(2, 11)}`;
+  const spanKey = `s-${Math.random().toString(36).slice(2, 11)}`;
+  return {
+    _type: 'block',
+    _key: blockKey,
+    style: 'normal',
+    children: [
+      {
+        _type: 'span',
+        _key: spanKey,
+        text: line,
+        marks: [],
+      },
+    ],
+    markDefs: [],
+  };
+});

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that using an array index for Sanity's _key is incorrect and will lead to data integrity issues. Providing a unique, random key is the proper solution and is critical for the feature to work reliably.

Medium
Assign unique keys to contacts
Suggestion Impact:The commit replaced the empty string `_key: ''` with a generated unique key (`contact-${Date.now()}-${index}`) when mapping valid contacts, addressing Sanity's requirement for unique array element keys.

code diff:

     const validContacts = contacts
       .filter((c) => c.name.trim() && c.email.trim())
-      .map((c) => ({
-        _key: '',
+      .map((c, index) => ({
+        _key: `contact-${Date.now()}-${index}`,
         name: c.name.trim(),
         email: c.email.trim(),
         phone: c.phone.trim() || undefined,

Generate a unique random _key for each contact person object instead of using an
empty string, as Sanity requires unique keys for array elements.

src/components/sponsor/SponsorOnboardingForm.tsx [177-186]

 const validContacts = contacts
   .filter((c) => c.name.trim() && c.email.trim())
   .map((c) => ({
-    _key: '',
+    _key: Math.random().toString(36).slice(2, 11),
     name: c.name.trim(),
     email: c.email.trim(),
     phone: c.phone.trim() || undefined,
     role: c.role || undefined,
     isPrimary: c.isPrimary,
   }))

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This suggestion fixes a critical bug where an empty string is used for Sanity's required _key field, which would cause data submission to fail or lead to data corruption. The proposed fix to generate a random key is correct.

Medium
Move initialization into useEffect
Suggestion Impact:The commit moved the sponsor-based initialization logic into a useEffect (importing useEffect and removing the render-phase state setting). It diverged from the suggestion by removing the initialized guard and using [sponsor] as the only dependency.

code diff:

-import { useState } from 'react'
+import { useState, useEffect } from 'react'
 import { api } from '@/lib/trpc/client'
 import {
   CheckCircleIcon,
@@ -56,9 +56,13 @@
   })
   const [error, setError] = useState<string | null>(null)
   const [submitted, setSubmitted] = useState(false)
-  const [initialized, setInitialized] = useState(false)
-
-  if (sponsor && !initialized) {
+
+  // Initialize form state from sponsor data when it becomes available
+  // This is intentional - we need to populate the form when async data arrives
+  useEffect(() => {
+    if (!sponsor) return
+
+    /* eslint-disable react-hooks/set-state-in-effect */
     if (sponsor.contactPersons?.length) {
       setContacts(
         sponsor.contactPersons.map((c) => ({
@@ -81,8 +85,8 @@
       orgNumber: sponsor.sponsorOrgNumber || '',
       address: sponsor.sponsorAddress || '',
     })
-    setInitialized(true)
-  }
+    /* eslint-enable react-hooks/set-state-in-effect */
+  }, [sponsor])
 

Encapsulate the form's data initialization logic within a useEffect hook. This
avoids setting state directly during the component's render phase, which is a
React anti-pattern.

src/components/sponsor/SponsorOnboardingForm.tsx [61-85]

-if (sponsor && !initialized) {
-  if (sponsor.contactPersons?.length) {
-    setContacts(
-      sponsor.contactPersons.map((c) => ({
-        name: c.name,
-        email: c.email,
-        phone: String(c.phone || ''),
-        role: String(c.role || ''),
-        isPrimary: Boolean(c.isPrimary),
-      })),
-    )
+useEffect(() => {
+  if (sponsor && !initialized) {
+    if (sponsor.contactPersons?.length) {
+      setContacts(
+        sponsor.contactPersons.map((c) => ({
+          name: c.name,
+          email: c.email,
+          phone: String(c.phone || ''),
+          role: String(c.role || ''),
+          isPrimary: Boolean(c.isPrimary),
+        })),
+      )
+    }
+    if (sponsor.billing) {
+      setBilling({
+        email: sponsor.billing.email,
+        reference: sponsor.billing.reference || '',
+        comments: sponsor.billing.comments || '',
+      })
+    }
+    setCompany({
+      orgNumber: sponsor.sponsorOrgNumber || '',
+      address: sponsor.sponsorAddress || '',
+    })
+    setInitialized(true)
   }
-  if (sponsor.billing) {
-    setBilling({
-      email: sponsor.billing.email,
-      reference: sponsor.billing.reference || '',
-      comments: sponsor.billing.comments || '',
-    })
-  }
-  setCompany({
-    orgNumber: sponsor.sponsorOrgNumber || '',
-    address: sponsor.sponsorAddress || '',
-  })
-  setInitialized(true)
-}
+}, [sponsor, initialized])

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that setting state directly in the render body is an anti-pattern in React. Moving this logic into a useEffect hook is the standard and correct approach to prevent side effects during rendering.

Medium
General
Combine patch updates atomically
Suggestion Impact:Replaced two separate patch/commit calls (with a conditional second patch) with a single patch that always sets signatureStatus and conditionally sets contractStatus and contractSignedAt when status is 'signed', making the update atomic.

code diff:

+        // Atomic update: set signatureStatus and conditionally set contract fields
         await clientWrite
           .patch(input.id)
-          .set({ signatureStatus: input.newStatus })
-          .commit()
-
-        if (input.newStatus === 'signed') {
-          await clientWrite
-            .patch(input.id)
-            .set({
+          .set({
+            signatureStatus: input.newStatus,
+            ...(input.newStatus === 'signed' && {
               contractStatus: 'contract-signed',
               contractSignedAt: getCurrentDateTime(),
-            })
-            .commit()
-        }
+            }),
+          })
+          .commit()

Combine two separate Sanity patch operations into a single atomic update. This
ensures signatureStatus, contractStatus, and contractSignedAt are updated
together, preventing partial state updates.

src/server/routers/sponsor.ts [1041-1054]

 await clientWrite
   .patch(input.id)
-  .set({ signatureStatus: input.newStatus })
-  .commit()
-if (input.newStatus === 'signed') {
-  await clientWrite
-    .patch(input.id)
-    .set({
+  .set({
+    signatureStatus: input.newStatus,
+    ...(input.newStatus === 'signed' && {
       contractStatus: 'contract-signed',
       contractSignedAt: getCurrentDateTime(),
-    })
-    .commit()
-}
+    }),
+  })
+  .commit()

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies a non-atomic update and provides a concise, efficient solution using a single patch operation. This improves code quality and prevents potential data inconsistencies.

Medium
  • Update

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds sponsor contract template management, dynamic contract readiness/onboarding tooling, and sets up Storybook documentation/testing infrastructure to support these workflows.

Changes:

  • Introduces Sanity schema + migration fields for contract templates, signature tracking, and sponsor onboarding.
  • Adds admin UI for contract template listing plus sponsor onboarding link generation and contract readiness indicator.
  • Adds Storybook (config, stories, CI workflows, scripts) to document and test UI components.

Reviewed changes

Copilot reviewed 122 out of 147 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/lib/dashboard/README.md Removes dashboard library README content
src/lib/conference/types.ts Extends Conference type with organizer org/address fields
src/docs/design-system/brand/ColorPalette.stories.tsx Adds brand color palette documentation story
src/docs/design-system/brand/Buttons.stories.tsx Adds brand button system story
src/docs/design-system/Spacing.stories.tsx Adds spacing scale documentation story
src/docs/design-system/Shadows.stories.tsx Adds shadows documentation story
src/docs/design-system/Colors.stories.tsx Adds foundation colors story
src/docs/components/index.ts Exports shared docs components
src/docs/components/TypographyShowcase.tsx Repoints typography types to design-system data
src/docs/components/ColorSwatch.tsx Repoints color types to design-system data
src/docs/DeveloperGuide.stories.tsx Adds developer guide Storybook page
src/components/icons/OSIcons.stories.tsx Adds Storybook docs for OS icons
src/components/common/MissingAvatar.stories.tsx Adds Storybook docs for MissingAvatar
src/components/common/DownloadableImage.tsx Renames/introduces generic downloadable wrapper
src/components/common/ContactRoleSelect.stories.tsx Adds Storybook docs for contact role selector demo
src/components/cfp/SpeakerShareWrapper.tsx Updates import path for sharing actions component
src/components/branding/index.ts Removes branding barrel export
src/components/branding/PatternExample.tsx Removes branding example component
src/components/branding/IconShowcase.tsx Removes branding icon showcase component
src/components/branding/ExpandableEmailTemplate.tsx Removes expandable email template component
src/components/branding/BrandingHeroSection.tsx Removes branding hero section component
src/components/branding/BrandingExampleHeroSection.tsx Removes branding example hero section component
src/components/admin/sponsor/SponsorContactActions.stories.tsx Adds Storybook docs for sponsor contact actions UI
src/components/admin/sponsor/ContractTemplateListPage.tsx Adds admin page for listing/deleting contract templates
src/components/admin/sponsor-crm/utils.ts Adds new activity/action item icons & colors
src/components/admin/sponsor-crm/form/TierRadioGroup.stories.tsx Adds TierRadioGroup stories
src/components/admin/sponsor-crm/form/TagCombobox.stories.tsx Adds TagCombobox stories
src/components/admin/sponsor-crm/form/StatusListbox.stories.tsx Adds StatusListbox stories
src/components/admin/sponsor-crm/form/ContractValueInput.stories.tsx Adds ContractValueInput stories
src/components/admin/sponsor-crm/form/AddonsCheckboxGroup.stories.tsx Adds AddonsCheckboxGroup stories
src/components/admin/sponsor-crm/SponsorCard.stories.tsx Adds SponsorCard stories
src/components/admin/sponsor-crm/SponsorCRMForm.tsx Adds onboarding link + contract readiness UI to CRM form
src/components/admin/sponsor-crm/SponsorBulkActions.stories.tsx Adds SponsorBulkActions stories with MSW mocking
src/components/admin/sponsor-crm/SponsorBoardColumn.stories.tsx Adds SponsorBoardColumn stories
src/components/admin/sponsor-crm/OnboardingLinkButton.tsx Adds onboarding link generation/copy UI
src/components/admin/sponsor-crm/OnboardingLinkButton.stories.tsx Adds stories for onboarding link button with MSW
src/components/admin/sponsor-crm/ContractReadinessIndicator.tsx Adds contract readiness indicator component
src/components/admin/sponsor-crm/ContractReadinessIndicator.stories.tsx Adds stories for readiness indicator with MSW
src/components/admin/sponsor-crm/BoardViewSwitcher.stories.tsx Adds BoardViewSwitcher stories
src/components/admin/ProposalStatistics.stories.tsx Adds ProposalStatistics stories
src/components/admin/PhotoGalleryWithDownload.tsx Switches to DownloadableImage wrapper
src/components/admin/MemeGeneratorWithDownload.tsx Switches to DownloadableImage wrapper
src/components/admin/EmailModal.tsx Tweaks Tailwind min-height classes in email UI
src/components/VideoEmbed.stories.tsx Adds VideoEmbed stories
src/components/ThemeToggle.stories.tsx Adds ThemeToggle stories
src/components/Sponsors.stories.tsx Adds Sponsors system component stories
src/components/SponsorLogo.stories.tsx Adds SponsorLogo stories
src/components/SpeakerAvatars.stories.tsx Adds SpeakerAvatars stories
src/components/ShowMore.tsx Adds explicit text color classes to content container
src/components/ShowMore.stories.tsx Adds ShowMore stories + interaction tests
src/components/Logo.stories.tsx Adds Logo/Logomark stories
src/components/Container.stories.tsx Adds Container stories
src/components/CollapsibleDescription.stories.tsx Adds CollapsibleDescription stories
src/components/Button.stories.tsx Adds Button stories + interaction tests
src/components/BackLink.stories.tsx Adds BackLink stories
src/app/(main)/sponsor/terms/page.tsx Adds public sponsor terms page fetching terms content
src/app/(main)/sponsor/onboarding/[token]/page.tsx Adds sponsor onboarding token page
src/app/(admin)/admin/sponsors/contracts/page.tsx Adds admin route for contract templates list
src/app/(admin)/admin/sponsors/contracts/new/page.tsx Adds admin route for creating contract templates
src/app/(admin)/admin/sponsors/contracts/[id]/page.tsx Adds admin route for editing contract templates
src/app/(admin)/admin/marketing/page.tsx Switches marketing download wrapper to DownloadableImage
src/mocks/sponsor-data.ts Adds Storybook mock factories for sponsor CRM components
scripts/validate-stories.ts Adds script to import/validate all story files
scripts/test-storybook.sh Adds script to run Storybook server + test runner
sanity/schemaTypes/sponsorForConference.ts Adds signature/onboarding/contract template fields
sanity/schemaTypes/sponsor.ts Adds sponsor address field
sanity/schemaTypes/contractTemplate.ts Adds new contract template document schema
sanity/schemaTypes/conference.ts Adds organizer org number/address fields
sanity/schema.ts Registers contractTemplate schema type
public/og/README.md Removes legacy static OG image README
package.json Adds Storybook scripts and dependencies
migrations/033-add-signature-onboarding-fields/index.ts Backfills defaults for new sponsorForConference fields
knip.json Updates knip entries/ignores for Storybook files/output
eslint.config.js Adds Storybook ESLint plugin and ignores
docs/STORYBOOK_STRUCTURE.md Adds Storybook structure/best practices doc
tests/lib/sponsor-crm/onboarding.test.ts Adds unit tests for onboarding URL builder
AGENTS.md Updates contributor guidance to reference Storybook + scripts
.storybook/preview.tsx Adds Storybook preview config (sorting, themes, MSW loader)
.storybook/preview-head.html Adds Storybook font loading + CSS variables
.storybook/main.ts Adds Storybook main config using Next.js Vite framework
.storybook/decorators/TRPCDecorator.tsx Adds tRPC + React Query decorator for stories
.storybook/.env Adds Storybook env file with placeholder values
.github/workflows/pr-checks.yml Adds Storybook tests job
.github/workflows/deploy-storybook.yml Adds GitHub Pages Storybook deployment workflow
.github/workflows/chromatic.yml Adds Chromatic visual regression workflow

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Adds comprehensive stories showing:
- Default form state (new sponsor)
- Pre-populated form with existing data
- Loading state during token validation
- Invalid/expired token error state
- Already completed onboarding state
- Community partner (no tier) variant

Uses MSW to mock tRPC endpoints for isolated testing.
…TemplateEditor, SponsorTemplatePicker, and SponsorBanner components
…h mock data for speakers, talks, workshops, and sponsors.
… and speakers

- Created stories for ProposalReviewForm, ProposalReviewList, ProposalReviewSummary, and ProposalsList to showcase various states and interactions.
- Added SpeakerActions and SpeakerMultiSelect stories to demonstrate speaker management functionalities.
- Implemented ProposalCoSpeaker and ProposalGuidanceSidebar stories to illustrate co-speaker management and guidance information for proposals.
- Included mock data and handlers for MSW to simulate API interactions in the stories.
- Removed AdminOverview.stories.tsx to streamline documentation.
- Updated SponsorComponentIndex.stories.tsx to reflect new component paths and titles.
- Introduced AdminPages.stories.tsx to showcase complete admin page patterns, including list and detail views, loading states, error handling, and best practices.
- Updated ButtonSystem to Buttons in Buttons.stories.tsx
- Updated PatternSystem to CloudNativePatterns in Patterns.stories.tsx
- Updated TypographySystem to Typography in TypographySystem.stories.tsx
- Updated EmailSystem to EmailTemplates in EmailTemplates.stories.tsx
- Updated HeroVariants to HeroSections in HeroSections.stories.tsx
- Updated SpeakerSystem to Overview in SpeakerComponents.stories.tsx

Additionally, added a script to analyze story files for export naming conventions and potential mismatches.
…ation; update tech stack and system descriptions
@Starefossen Starefossen merged commit 18ad100 into main Feb 12, 2026
5 of 7 checks passed
@Starefossen Starefossen deleted the sponsor-contracts branch February 12, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sponsor Self-Service Onboarding Portal Contract Template System Schema Extensions for Contract Signing

1 participant