diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 00000000..b9abd733 --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,57 @@ +name: Chromatic + +on: + pull_request: + branches: ['**'] + push: + branches: ['main'] + +jobs: + chromatic: + name: Visual Regression Tests + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required for Chromatic to detect changes + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.24.0 + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Initialize MSW + run: pnpm msw init public --save=false + + - name: Publish to Chromatic + uses: chromaui/action@latest + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + buildScriptName: build-storybook + onlyChanged: true + autoAcceptChanges: main + exitOnceUploaded: true diff --git a/.github/workflows/deploy-storybook.yml b/.github/workflows/deploy-storybook.yml new file mode 100644 index 00000000..09d84911 --- /dev/null +++ b/.github/workflows/deploy-storybook.yml @@ -0,0 +1,81 @@ +name: Deploy Storybook + +on: + push: + branches: ['main'] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + build: + name: Build Storybook + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.24.0 + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Initialize MSW + run: pnpm msw init public --save=false + + - name: Build Storybook + run: pnpm build-storybook + + - name: Add CNAME file + run: echo "design.cloudnativedays.no" > storybook-static/CNAME + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: storybook-static + + deploy: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index ae8fccdf..2a7f23b9 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -92,6 +92,50 @@ jobs: - name: Run tests run: pnpm run test + storybook: + name: Storybook Tests + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.24.0 + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Install Playwright browsers + run: pnpm exec playwright install --with-deps chromium + + - name: Initialize MSW + run: pnpm msw init public --save=false + + - name: Run Storybook tests + run: pnpm run storybook:test-ci + build: name: Build runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index b4794f89..44336592 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,9 @@ dist/ # backups *.tar.gz +storybook-static/* + +# msw +public/mockServiceWorker.js +# storybook local env +.storybook/.env diff --git a/.storybook/.env.example b/.storybook/.env.example new file mode 100644 index 00000000..fdfd575e --- /dev/null +++ b/.storybook/.env.example @@ -0,0 +1,20 @@ +# Storybook Environment Variables (Example Template) +# --------------------------------------------------------------------------- +# This file is an example/template only. Do NOT put real secrets or production +# configuration in this committed file. +# +# Recommended usage: +# 1. Copy this file to `.storybook/.env` (which should be gitignored). +# 2. Replace the example values in that copy with your real local values. +# +# The variables below are commented out so this example file does not define +# any active environment variables when loaded. +# --------------------------------------------------------------------------- + +# Sanity CMS +# NEXT_PUBLIC_SANITY_PROJECT_ID=your_project_id +# NEXT_PUBLIC_SANITY_DATASET=production +# NEXT_PUBLIC_SANITY_API_VERSION=2024-01-01 + +# Base URL for stories +# NEXT_PUBLIC_BASE_URL=http://localhost:6006 diff --git a/.storybook/decorators/TRPCDecorator.tsx b/.storybook/decorators/TRPCDecorator.tsx new file mode 100644 index 00000000..504ec18a --- /dev/null +++ b/.storybook/decorators/TRPCDecorator.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { httpBatchLink } from '@trpc/client' +import { api } from '@/lib/trpc/client' +import type { Decorator } from '@storybook/nextjs-vite' + +// Create these outside the decorator to avoid recreating on every render +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + refetchOnWindowFocus: false, + staleTime: Infinity, + }, + }, +}) + +const trpcClient = api.createClient({ + links: [ + httpBatchLink({ + url: '/api/trpc', + // MSW will intercept these requests (relative URL for cross-origin compatibility) + }), + ], +}) + +export const TRPCDecorator: Decorator = (Story) => { + return ( + + + + + + ) +} diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 00000000..f0449c6c --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,36 @@ +import type { StorybookConfig } from '@storybook/nextjs-vite' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +const config: StorybookConfig = { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: ['@storybook/addon-docs'], + framework: { + name: '@storybook/nextjs-vite', + options: { + nextConfigPath: join(__dirname, '../next.config.ts'), + }, + }, + staticDirs: ['../public'], + typescript: { + check: false, + reactDocgen: 'react-docgen-typescript', + reactDocgenTypescriptOptions: { + shouldExtractLiteralValuesFromEnum: true, + propFilter: (prop) => + prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, + }, + }, + core: { + disableTelemetry: true, + }, + docs: {}, + features: { + sidebarOnboardingChecklist: false, + }, +} + +export default config diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 00000000..1f288983 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,19 @@ + + + + + + diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 00000000..0b7bc87c --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,211 @@ +import type { Preview } from '@storybook/nextjs-vite' +import type { Decorator } from '@storybook/nextjs-vite' +import { initialize, mswLoader } from 'msw-storybook-addon' +import { TRPCDecorator } from './decorators/TRPCDecorator' +import '../src/styles/tailwind.css' + +// Initialize MSW +initialize() + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + nextjs: { + appDirectory: true, + navigation: { + pathname: '/', + query: {}, + }, + }, + backgrounds: { + default: 'light', + values: [ + { + name: 'light', + value: '#ffffff', + }, + { + name: 'dark', + value: '#111827', + }, + ], + }, + options: { + storySort: { + order: [ + 'Getting Started', + ['Introduction', 'Developer Guide'], + 'Design System', + [ + 'Foundation', + ['Spacing', 'Shadows', 'Icons'], + 'Brand', + [ + 'Brand Story', + 'Color Palette', + 'Typography', + 'Buttons', + 'Cloud Native Patterns', + ], + 'Examples', + ['Hero Sections', 'Conference Landing Page', 'Admin Pages'], + ], + 'Components', + ['Data Display', 'Feedback', 'Forms', 'Icons', 'Layout'], + 'Systems', + [ + 'Program', + [ + 'Architecture', + 'TalkCard', + 'TalkPromotionCard', + 'ProgramFilters', + 'ViewModeSelector', + 'ProgramScheduleView', + 'ProgramGridView', + 'ProgramListView', + 'ProgramAgendaView', + ], + 'Proposals', + [ + 'Architecture', + 'Admin', + [ + 'ProposalsList', + 'ProposalCard', + 'ProposalsFilter', + 'ProposalStatistics', + 'ProposalPreview', + 'ProposalReviewForm', + 'ProposalReviewList', + 'ProposalReviewSummary', + 'FeaturedTalksManager', + ], + 'ProposalCoSpeaker', + 'ProposalGuidanceSidebar', + 'CompactProposalList', + 'AttachmentDisplay', + ], + 'Speakers', + [ + 'Architecture', + 'Admin', + [ + 'SpeakerTable', + 'SpeakerActions', + 'SpeakerMultiSelect', + 'FeaturedSpeakersManager', + ], + 'Overview', + 'SpeakerAvatars', + 'ClickableSpeakerNames', + 'SpeakerDetailsForm', + 'SpeakerProfilePreview', + ], + 'Sponsors', + [ + 'Architecture', + 'Workflow Diagram', + 'Admin', + [ + 'Overview', + 'Pipeline', + [ + 'SponsorCRMPipeline', + 'SponsorCard', + 'SponsorBoardColumn', + 'BoardViewSwitcher', + 'SponsorCRMForm', + 'SponsorBulkActions', + 'MobileFilterSheet', + 'OnboardingLinkButton', + 'ContractReadinessIndicator', + 'ImportHistoricSponsorsButton', + ], + 'Dashboard', + ['Metrics', 'Action Items', 'Activity Timeline'], + 'Tiers', + [ + 'SponsorTierManagement', + 'SponsorTierEditor', + 'SponsorAddModal', + ], + 'Contacts', + [ + 'SponsorContactTable', + 'SponsorContactEditor', + 'SponsorContactActions', + ], + 'Email', + [ + 'EmailModal', + 'SponsorIndividualEmailModal', + 'SponsorDiscountEmailModal', + ], + 'Form', + [ + 'SponsorLogoEditor', + 'SponsorCombobox', + 'TierRadioGroup', + 'StatusListbox', + 'OrganizerCombobox', + 'TagCombobox', + 'ContractValueInput', + 'AddonsCheckboxGroup', + ], + 'SponsorCRMFilterBar', + 'SponsorContactRoleSelect', + ], + 'Components', + ['SponsorLogo', 'Sponsors', 'SponsorThankYou'], + 'Onboarding', + ['SponsorOnboardingForm'], + 'Email', + ['SponsorTemplatePicker', 'SponsorEmailTemplateEditor'], + 'Form', + ['SponsorGlobalInfoFields'], + 'Stream', + ['SponsorBanner'], + ], + ], + ], + }, + }, + }, + globalTypes: { + theme: { + name: 'Theme', + description: 'Global theme for components', + defaultValue: 'light', + toolbar: { + icon: 'circlehollow', + items: [ + { value: 'light', title: 'Light', icon: 'sun' }, + { value: 'dark', title: 'Dark', icon: 'moon' }, + ], + dynamicTitle: true, + }, + }, + }, + decorators: [ + TRPCDecorator, + ((Story, context) => { + const theme = context.globals.theme || 'light' + return ( +
+
+ +
+
+ ) + }) as Decorator, + ], + loaders: [mswLoader], +} + +export default preview diff --git a/AGENTS.md b/AGENTS.md index ce99845d..34acf349 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,7 +42,7 @@ The site is multi-tenant, meaning it can be used for multiple events or conferen - Follow Next.js best practices (App Router, Server Components, Server Actions where applicable). - Utilize TypeScript for type safety. - Adhere to Tailwind CSS utility-first principles for styling. -- Refer to the branding page (`/branding` or `docs/BRANDING.md`) for styling guidelines and maintain visual consistency. +- Refer to the Storybook documentation (run `pnpm storybook`) for comprehensive design system guidelines, brand colors, typography, and component examples. All UI/UX design decisions should align with the documented design system. - Fetch and manage content primarily through Sanity. - Implement authentication flows using NextAuth.js 5.0 with the specified providers. - Ensure code is clean, maintainable and only comment when absolutely required to understand why the code is written in a certain way. @@ -52,6 +52,134 @@ The site is multi-tenant, meaning it can be used for multiple events or conferen - **JSX/TSX Content:** Use HTML entities (`'` for apostrophes and `"` for quotes) instead of raw quotes in JSX/TSX content to comply with linting rules. - **Icons:** Use Heroicons (`@heroicons/react`) for all icon needs instead of creating custom SVG elements. Import icons from either `/24/outline` for stroke icons or `/24/solid` for filled icons. This ensures consistency and maintainability across the application. +### Storybook & Design System + +The Storybook (`pnpm storybook`) is the single source of truth for all UI/UX documentation and should be consulted and updated for all visual design tasks. + +**Storybook Structure:** + +- **Getting Started/** - Introduction and developer guides +- **Design System/Foundation/** - Colors, Typography, Spacing, Shadows, Icons +- **Design System/Brand/** - Brand story, color palette, typography system, buttons, cloud native patterns +- **Design System/Examples/** - Integration patterns showing how components work together (hero sections, conference landing page, admin pages) +- **Components/** - General-purpose reusable components organized by category: + - **Data Display/** - CollapsibleDescription, CollapsibleSection, DownloadableImage, Email Templates, ShowMore, TypewriterEffect, VideoEmbed + - **Feedback/** - ConfirmationModal, ErrorDisplay, LoadingSkeleton + - **Forms/** - Form Elements, FilterDropdown, PortableTextEditor + - **Icons/** - OSIcons, SocialIcons + - **Layout/** - BackLink, Button, Container, Logo, MissingAvatar, ThemeToggle +- **Systems/** - Domain-specific feature documentation organized by system: + - **Program/** - Schedule views (grid, list, agenda, schedule), filters, talk cards + - **Proposals/** - Proposal submission, review, and admin management components + - **Speakers/** - Speaker profiles, forms, and admin management components + - **Sponsors/** - Sponsor system (CRM pipeline, contacts, email, tiers, onboarding, dashboard) + +**Story Types & Organization:** + +There are two distinct types of stories with different purposes: + +1. **Component Stories** (individual component docs) + - Live alongside components in `/src/components/` + - Document a single component's props, variants, and usage + - Include interactive controls for testing + - Domain-specific components go under `Systems/{SystemName}/` + +2. **Examples Stories** (integration patterns) + - Located in `/src/docs/design-system/examples/` + - Show how multiple components work together + - Demonstrate common application patterns + - Render live components with realistic data + +**When to Update Storybook:** + +- When adding new reusable components, create corresponding stories +- When modifying brand colors, typography, or visual patterns +- When creating new page layouts or component variants +- When documenting UI/UX best practices or guidelines +- When adding domain-specific components, place them under the appropriate system + +**Story Files Location:** + +- General component stories: alongside components in `/src/components/` +- Domain-specific component stories: alongside components with `Systems/{SystemName}/` title prefix +- Documentation pages: `/src/docs/` (organized by category) +- System documentation: `/src/docs/{SystemName}.stories.tsx` + +**Creating Component Stories:** + +```typescript +// Component story (lives next to component file) +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { MyComponent } from './MyComponent' + +const meta = { + title: 'Components/Layout/MyComponent', // or 'Systems/Speakers/MyComponent' for domain-specific + component: MyComponent, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { variant: 'default' }, +} +``` + +**Creating Documentation Stories:** + +```typescript +// Documentation story (in /src/docs/) +import type { Meta, StoryObj } from '@storybook/nextjs-vite' + +const meta = { + title: 'Design System/Foundation/NewCategory', + parameters: { + layout: 'fullscreen', + options: { showPanel: false }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Documentation: Story = { + render: () => , +} +``` + +**Known Limitations:** + +- `CloudNativePattern` and components using it (e.g., `SpeakerPromotionCard`) cannot be rendered in Storybook due to static SVG import incompatibility. Document these with screenshots or code examples instead. + +**Interaction Testing:** + +Stories can include `play` functions that test component behavior interactively. Import testing utilities from `storybook/test`: + +```typescript +import { expect, fn, userEvent, within } from 'storybook/test' + +export const ClickTest: Story = { + args: { onClick: fn() }, + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement) + const button = canvas.getByRole('button') + await userEvent.click(button) + await expect(args.onClick).toHaveBeenCalled() + }, +} +``` + +Interaction tests run automatically in CI via `pnpm run storybook:test-ci` and can be run locally with `pnpm run storybook:test` (requires Storybook running). + +**Visual Regression with Chromatic:** + +The project uses Chromatic for visual regression testing. PRs automatically trigger visual snapshots that compare against the main branch. To set up Chromatic: + +1. Add `CHROMATIC_PROJECT_TOKEN` secret to GitHub repository settings +2. PRs will show visual diff status checks +3. Changes to main are auto-accepted as the new baseline + ### Privacy and GDPR Compliance - **User Data Protection:** Always abide by GDPR regulations when handling any user data, including but not limited to: @@ -115,7 +243,7 @@ The site is multi-tenant, meaning it can be used for multiple events or conferen - **Next.js Cache Components:** All production public pages use Next.js 16+ Cache Components with the `'use cache'` directive for optimal performance in our multi-tenant architecture. - **Wrapper Pattern:** Pages follow a consistent pattern where an outer component reads `headers()` to extract the domain, then passes it to an inner cached component that uses `getConferenceForDomain(domain)`. -- **Cache Durations:** Use `cacheLife('hours')` for frequently changing content (homepage, program, speakers, tickets), `cacheLife('days')` for stable content (CFP), and `cacheLife('max')` for static pages (conduct, terms, privacy, branding). +- **Cache Durations:** Use `cacheLife('hours')` for frequently changing content (homepage, program, speakers, tickets), `cacheLife('days')` for stable content (CFP), and `cacheLife('max')` for static pages (conduct, terms, privacy). - **Cache Tags:** Every cached component includes a `cacheTag('content:pagename')` for granular invalidation via `revalidateTag()`. - **Exclusions:** Authentication flows (`/signin`) and development/testing pages (`/css-test`) are intentionally excluded from caching as they require request-time execution. @@ -155,6 +283,10 @@ The site is multi-tenant, meaning it can be used for multiple events or conferen - **Testing:** `pnpm run test` - Runs Jest tests silently. - **Testing (Debug):** `pnpm run test:debug` - Runs Jest tests with debug output. - **Testing (Watch):** `pnpm run test:watch` - Runs Jest tests in watch mode. +- **Storybook:** `pnpm storybook` - Starts the Storybook development server for design system documentation. +- **Storybook Build:** `pnpm build-storybook` - Builds static Storybook for deployment. +- **Storybook Tests:** `pnpm run storybook:test` - Runs Storybook interaction tests (requires Storybook running). +- **Storybook Tests (CI):** `pnpm run storybook:test-ci` - Builds Storybook and runs tests in CI mode. - Run sanity commands with `pnpm sanity {command}` (e.g., `pnpm sanity deploy`) - do not use `npx sanity` directly. ## Code Organization & Refactoring diff --git a/__tests__/lib/sponsor-crm/contract-readiness.test.ts b/__tests__/lib/sponsor-crm/contract-readiness.test.ts new file mode 100644 index 00000000..715dd62e --- /dev/null +++ b/__tests__/lib/sponsor-crm/contract-readiness.test.ts @@ -0,0 +1,319 @@ +import { describe, it, expect } from '@jest/globals' +import { + checkContractReadiness, + groupMissingBySource, + type MissingField, +} from '@/lib/sponsor-crm/contract-readiness' +import type { SponsorForConferenceExpanded } from '@/lib/sponsor-crm/types' + +describe('contract-readiness', () => { + const createMockSponsor = ( + overrides: Partial = {}, + ): SponsorForConferenceExpanded => ({ + _id: 'sfc-1', + _createdAt: '2026-01-01T00:00:00Z', + _updatedAt: '2026-01-01T00:00:00Z', + sponsor: { + _id: 'sponsor-1', + name: 'Acme Corp', + website: 'https://acme.com', + logo: 'logo.png', + orgNumber: '123456789', + address: 'Oslo, Norway', + }, + conference: { + _id: 'conf-1', + title: 'Cloud Native Days Norway 2026', + organizer: 'Cloud Native Bergen', + organizerOrgNumber: '987654321', + organizerAddress: 'Bergen, Norway', + city: 'Bergen', + venueName: 'Bergen Conference Center', + venueAddress: 'Main St 1, Bergen', + startDate: '2026-06-10', + endDate: '2026-06-11', + sponsorEmail: 'sponsors@cloudnativebergen.no', + }, + tier: { + _id: 'tier-1', + title: 'Gold', + tagline: 'Premium sponsorship', + tierType: 'standard', + price: [{ _key: '1', amount: 50000, currency: 'NOK' }], + }, + contractStatus: 'none', + status: 'prospect', + invoiceStatus: 'not-sent', + contractValue: 50000, + contractCurrency: 'NOK', + contactPersons: [ + { + _key: 'contact-1', + name: 'John Doe', + email: 'john@acme.com', + isPrimary: true, + }, + ], + ...overrides, + }) + + describe('checkContractReadiness', () => { + it('should return ready=true when all required fields are present', () => { + const sponsor = createMockSponsor() + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(true) + expect(result.missing).toHaveLength(0) + }) + + it('should detect missing organizer fields', () => { + const sponsor = createMockSponsor({ + conference: { + _id: 'conf-1', + title: 'Cloud Native Days Norway 2026', + // Missing organizer, organizerOrgNumber, organizerAddress + }, + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(false) + expect(result.missing).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: 'conference.organizer', + label: 'Organizer name', + source: 'organizer', + }), + expect.objectContaining({ + field: 'conference.organizerOrgNumber', + label: 'Organizer org. number', + source: 'organizer', + }), + expect.objectContaining({ + field: 'conference.organizerAddress', + label: 'Organizer address', + source: 'organizer', + }), + ]), + ) + }) + + it('should detect missing sponsor fields', () => { + const sponsor = createMockSponsor({ + sponsor: { + _id: 'sponsor-1', + name: 'Acme Corp', + website: 'https://acme.com', + logo: 'logo.png', + // Missing orgNumber and address + }, + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(false) + expect(result.missing).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: 'sponsor.orgNumber', + label: 'Sponsor org. number', + source: 'sponsor', + }), + expect.objectContaining({ + field: 'sponsor.address', + label: 'Sponsor address', + source: 'sponsor', + }), + ]), + ) + }) + + it('should detect missing contact person', () => { + const sponsor = createMockSponsor({ + contactPersons: [], + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(false) + expect(result.missing).toContainEqual( + expect.objectContaining({ + field: 'contactPersons', + label: 'Primary contact person', + source: 'sponsor', + }), + ) + }) + + it('should accept contact person without isPrimary if only one contact', () => { + const sponsor = createMockSponsor({ + contactPersons: [ + { + _key: 'contact-1', + name: 'John Doe', + email: 'john@acme.com', + // iPrimary not set, but only one contact + }, + ], + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(true) + }) + + it('should require primary contact when multiple contacts exist', () => { + const sponsor = createMockSponsor({ + contactPersons: [ + { + _key: 'contact-1', + name: 'John Doe', + email: 'john@acme.com', + // No isPrimary + }, + { + _key: 'contact-2', + name: 'Jane Smith', + email: 'jane@acme.com', + // No isPrimary + }, + ], + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(false) + expect(result.missing).toContainEqual( + expect.objectContaining({ + field: 'contactPersons', + source: 'sponsor', + }), + ) + }) + + it('should detect missing pipeline fields', () => { + const sponsor = createMockSponsor({ + tier: undefined, + contractValue: 0, + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(false) + expect(result.missing).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: 'tier', + label: 'Sponsor tier', + source: 'pipeline', + }), + expect.objectContaining({ + field: 'contractValue', + label: 'Contract value', + source: 'pipeline', + }), + ]), + ) + }) + + it('should handle missing venue and dates gracefully', () => { + const sponsor = createMockSponsor({ + conference: { + _id: 'conf-1', + title: 'Cloud Native Days Norway 2026', + organizer: 'Cloud Native Bergen', + organizerOrgNumber: '987654321', + organizerAddress: 'Bergen, Norway', + sponsorEmail: 'sponsors@cloudnativebergen.no', + // Missing startDate, venueName + }, + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(false) + expect(result.missing).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: 'conference.startDate', + source: 'organizer', + }), + expect.objectContaining({ + field: 'conference.venueName', + source: 'organizer', + }), + ]), + ) + }) + + it('should handle all missing fields across all sources', () => { + const sponsor = createMockSponsor({ + sponsor: { + _id: 'sponsor-1', + name: 'Acme Corp', + website: 'https://acme.com', + logo: 'logo.png', + }, + conference: { + _id: 'conf-1', + title: 'Cloud Native Days Norway 2026', + }, + tier: undefined, + contractValue: undefined, + contactPersons: [], + }) + + const result = checkContractReadiness(sponsor) + + expect(result.ready).toBe(false) + expect(result.missing.length).toBeGreaterThan(5) + + const sources = new Set(result.missing.map((m) => m.source)) + expect(sources).toContain('organizer') + expect(sources).toContain('sponsor') + expect(sources).toContain('pipeline') + }) + }) + + describe('groupMissingBySource', () => { + it('should group missing fields by source', () => { + const missing: MissingField[] = [ + { + field: 'conference.organizer', + label: 'Organizer name', + source: 'organizer', + }, + { + field: 'sponsor.orgNumber', + label: 'Sponsor org. number', + source: 'sponsor', + }, + { field: 'tier', label: 'Sponsor tier', source: 'pipeline' }, + { + field: 'conference.startDate', + label: 'Conference start date', + source: 'organizer', + }, + ] + + const grouped = groupMissingBySource(missing) + + expect(grouped.organizer).toHaveLength(2) + expect(grouped.sponsor).toHaveLength(1) + expect(grouped.pipeline).toHaveLength(1) + }) + + it('should return empty arrays for sources with no missing fields', () => { + const missing: MissingField[] = [ + { field: 'tier', label: 'Sponsor tier', source: 'pipeline' }, + ] + + const grouped = groupMissingBySource(missing) + + expect(grouped.organizer).toHaveLength(0) + expect(grouped.sponsor).toHaveLength(0) + expect(grouped.pipeline).toHaveLength(1) + }) + }) +}) diff --git a/__tests__/lib/sponsor-crm/contract-variables.test.ts b/__tests__/lib/sponsor-crm/contract-variables.test.ts new file mode 100644 index 00000000..8bab2909 --- /dev/null +++ b/__tests__/lib/sponsor-crm/contract-variables.test.ts @@ -0,0 +1,285 @@ +import { describe, it, expect } from '@jest/globals' +import { + buildContractVariables, + CONTRACT_VARIABLE_DESCRIPTIONS, + type ContractVariableContext, +} from '@/lib/sponsor-crm/contract-variables' + +describe('contract-variables', () => { + describe('buildContractVariables', () => { + const createBasicContext = (): ContractVariableContext => ({ + sponsor: { + name: 'Acme Corporation', + orgNumber: '123456789', + address: 'Main Street 123, Oslo, Norway', + website: 'https://acme.com', + }, + contactPerson: { + name: 'John Doe', + email: 'john.doe@acme.com', + }, + tier: { + title: 'Gold Sponsor', + tagline: 'Premium partnership package', + }, + addons: [ + { title: 'Workshop Sponsorship' }, + { title: 'Party Sponsorship' }, + ], + contractValue: 75000, + contractCurrency: 'NOK', + conference: { + title: 'Cloud Native Days Norway 2026', + startDate: '2026-06-10', + endDate: '2026-06-11', + city: 'Bergen', + organizer: 'Cloud Native Bergen', + organizerOrgNumber: '987654321', + organizerAddress: 'Conference Street 1, Bergen, Norway', + venueName: 'Bergen Conference Center', + venueAddress: 'Venue Road 10, Bergen', + sponsorEmail: 'sponsors@cloudnativebergen.no', + }, + }) + + it('should build all basic required variables', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.SPONSOR_NAME).toBe('Acme Corporation') + expect(vars.CONFERENCE_TITLE).toBe('Cloud Native Days Norway 2026') + expect(vars.TODAY_DATE).toBeDefined() + }) + + it('should build sponsor variables', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.SPONSOR_ORG_NUMBER).toBe('123456789') + expect(vars.SPONSOR_ADDRESS).toBe('Main Street 123, Oslo, Norway') + expect(vars.SPONSOR_WEBSITE).toBe('https://acme.com') + }) + + it('should build contact person variables', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.CONTACT_NAME).toBe('John Doe') + expect(vars.CONTACT_EMAIL).toBe('john.doe@acme.com') + }) + + it('should build tier variables', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.TIER_NAME).toBe('Gold Sponsor') + expect(vars.TIER_TAGLINE).toBe('Premium partnership package') + }) + + it('should build addons list', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.ADDONS_LIST).toBe('Workshop Sponsorship, Party Sponsorship') + }) + + it('should format contract value with currency', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.CONTRACT_VALUE).toContain('75') + expect(vars.CONTRACT_VALUE).toContain('000') + expect(vars.CONTRACT_VALUE_NUMBER).toBe('75000') + expect(vars.CONTRACT_CURRENCY).toBe('NOK') + }) + + it('should handle different currencies', () => { + const ctx = createBasicContext() + ctx.contractCurrency = 'USD' + ctx.contractValue = 10000 + + const vars = buildContractVariables(ctx) + + expect(vars.CONTRACT_CURRENCY).toBe('USD') + expect(vars.CONTRACT_VALUE_NUMBER).toBe('10000') + }) + + it('should build conference date variables', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.CONFERENCE_DATE).toContain('10') + expect(vars.CONFERENCE_DATE).toContain('June') + expect(vars.CONFERENCE_DATE).toContain('2026') + expect(vars.CONFERENCE_YEAR).toBe('2026') + }) + + it('should build date range for multi-day conference in same month', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + // 10–11 June 2026 + expect(vars.CONFERENCE_DATES).toContain('10') + expect(vars.CONFERENCE_DATES).toContain('11') + expect(vars.CONFERENCE_DATES).toContain('June') + expect(vars.CONFERENCE_DATES).toContain('2026') + }) + + it('should build date range for conference spanning multiple months', () => { + const ctx = createBasicContext() + ctx.conference.startDate = '2026-05-31' + ctx.conference.endDate = '2026-06-01' + + const vars = buildContractVariables(ctx) + + expect(vars.CONFERENCE_DATES).toContain('May') + expect(vars.CONFERENCE_DATES).toContain('June') + }) + + it('should handle single-day conference', () => { + const ctx = createBasicContext() + delete ctx.conference.endDate + + const vars = buildContractVariables(ctx) + + expect(vars.CONFERENCE_DATES).toBe(vars.CONFERENCE_DATE) + }) + + it('should build conference location variables', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.CONFERENCE_CITY).toBe('Bergen') + expect(vars.VENUE_NAME).toBe('Bergen Conference Center') + expect(vars.VENUE_ADDRESS).toBe('Venue Road 10, Bergen') + }) + + it('should build organizer variables', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + expect(vars.ORG_NAME).toBe('Cloud Native Bergen') + expect(vars.ORG_ORG_NUMBER).toBe('987654321') + expect(vars.ORG_ADDRESS).toBe('Conference Street 1, Bergen, Norway') + expect(vars.ORG_EMAIL).toBe('sponsors@cloudnativebergen.no') + }) + + it('should handle missing optional sponsor fields', () => { + const ctx = createBasicContext() + delete ctx.sponsor.orgNumber + delete ctx.sponsor.address + delete ctx.sponsor.website + + const vars = buildContractVariables(ctx) + + expect(vars.SPONSOR_NAME).toBe('Acme Corporation') + expect(vars.SPONSOR_ORG_NUMBER).toBeUndefined() + expect(vars.SPONSOR_ADDRESS).toBeUndefined() + expect(vars.SPONSOR_WEBSITE).toBeUndefined() + }) + + it('should handle missing contact person', () => { + const ctx = createBasicContext() + delete ctx.contactPerson + + const vars = buildContractVariables(ctx) + + expect(vars.CONTACT_NAME).toBeUndefined() + expect(vars.CONTACT_EMAIL).toBeUndefined() + }) + + it('should handle missing tier', () => { + const ctx = createBasicContext() + delete ctx.tier + + const vars = buildContractVariables(ctx) + + expect(vars.TIER_NAME).toBeUndefined() + expect(vars.TIER_TAGLINE).toBeUndefined() + }) + + it('should handle empty addons list', () => { + const ctx = createBasicContext() + ctx.addons = [] + + const vars = buildContractVariables(ctx) + + expect(vars.ADDONS_LIST).toBeUndefined() + }) + + it('should default to NOK when currency not specified', () => { + const ctx = createBasicContext() + delete ctx.contractCurrency + + const vars = buildContractVariables(ctx) + + expect(vars.CONTRACT_CURRENCY).toBe('NOK') + }) + + it('should handle missing contract value', () => { + const ctx = createBasicContext() + delete ctx.contractValue + + const vars = buildContractVariables(ctx) + + expect(vars.CONTRACT_VALUE).toBeUndefined() + expect(vars.CONTRACT_VALUE_NUMBER).toBeUndefined() + expect(vars.CONTRACT_CURRENCY).toBe('NOK') + }) + + it('should handle missing optional conference fields', () => { + const ctx = createBasicContext() + delete ctx.conference.city + delete ctx.conference.organizerOrgNumber + delete ctx.conference.organizerAddress + delete ctx.conference.venueName + delete ctx.conference.venueAddress + delete ctx.conference.sponsorEmail + + const vars = buildContractVariables(ctx) + + expect(vars.CONFERENCE_TITLE).toBe('Cloud Native Days Norway 2026') + expect(vars.CONFERENCE_CITY).toBeUndefined() + expect(vars.ORG_ORG_NUMBER).toBeUndefined() + expect(vars.ORG_ADDRESS).toBeUndefined() + expect(vars.VENUE_NAME).toBeUndefined() + expect(vars.VENUE_ADDRESS).toBeUndefined() + expect(vars.ORG_EMAIL).toBeUndefined() + }) + + it('should format today date in correct format', () => { + const ctx = createBasicContext() + const vars = buildContractVariables(ctx) + + // Should be in format "11 February 2026" + const dateRegex = /^\d{1,2} [A-Z][a-z]+ \d{4}$/ + expect(vars.TODAY_DATE).toMatch(dateRegex) + }) + }) + + describe('CONTRACT_VARIABLE_DESCRIPTIONS', () => { + it('should have descriptions for all common variables', () => { + const expectedVars = [ + 'SPONSOR_NAME', + 'SPONSOR_ORG_NUMBER', + 'SPONSOR_ADDRESS', + 'CONTACT_NAME', + 'TIER_NAME', + 'CONTRACT_VALUE', + 'CONFERENCE_TITLE', + 'CONFERENCE_DATE', + 'ORG_NAME', + 'ORG_ORG_NUMBER', + 'ORG_ADDRESS', + 'VENUE_NAME', + ] + + expectedVars.forEach((varName) => { + expect(CONTRACT_VARIABLE_DESCRIPTIONS[varName]).toBeDefined() + expect(CONTRACT_VARIABLE_DESCRIPTIONS[varName].length).toBeGreaterThan( + 0, + ) + }) + }) + }) +}) diff --git a/__tests__/lib/sponsor-crm/onboarding.test.ts b/__tests__/lib/sponsor-crm/onboarding.test.ts new file mode 100644 index 00000000..caefd048 --- /dev/null +++ b/__tests__/lib/sponsor-crm/onboarding.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect } from '@jest/globals' +import { buildOnboardingUrl } from '@/lib/sponsor-crm/onboarding' + +describe('onboarding', () => { + describe('buildOnboardingUrl', () => { + it('should build correct onboarding URL', () => { + const baseUrl = 'https://cloudnativebergen.no' + const token = 'abc-123-def-456' + + const url = buildOnboardingUrl(baseUrl, token) + + expect(url).toBe( + 'https://cloudnativebergen.no/sponsor/onboarding/abc-123-def-456', + ) + }) + + it('should handle base URL without trailing slash', () => { + const url = buildOnboardingUrl('https://example.com', 'token-123') + + expect(url).toBe('https://example.com/sponsor/onboarding/token-123') + }) + }) +}) diff --git a/docs/BRANDING.md b/docs/BRANDING.md deleted file mode 100644 index a7223dfe..00000000 --- a/docs/BRANDING.md +++ /dev/null @@ -1,376 +0,0 @@ -# Cloud Native Days Norway - Branding Guide - -This document outlines the comprehensive brand system for Cloud Native Days Norway, including colors, typography, icons, patterns, and component examples. - -**📖 For a comprehensive interactive brand guide with live examples, visit: [/branding](/branding)** - -The live branding page includes: - -- Interactive color palette with usage guidelines -- Typography showcase with all font combinations -- Complete icon library from Heroicons -- Cloud Native pattern system with authentic project logos -- Button showcase with all variants and states -- Hero examples with different configurations -- Speaker display patterns and guidelines -- Talk promotion components -- Call-to-action examples -- Professional email templates -- Component documentation and accessibility guidelines - -## Brand Story & Design Principles - -Cloud Native Days Norway embodies the spirit of Norway's tech community: innovative yet grounded, collaborative yet independent, modern yet respectful of tradition. Our visual identity draws inspiration from Bergen's dramatic landscapes—the meeting of mountains and sea, the interplay of mist and clarity, the harmony of natural and urban elements. - -### Design Principles - -1. **Developer-First**: Every design choice considers the developer experience and technical audience -2. **Accessible by Design**: We prioritize accessibility and inclusive design in all brand applications -3. **Nordic Minimalism**: Clean, functional design that lets content shine without unnecessary complexity -4. **Community Driven**: Our brand reflects the collaborative spirit of the open source community - -## Quick Reference - -### Primary Colors - -- **Cloud Blue**: `bg-brand-cloud-blue` or `text-brand-cloud-blue` (#1D4ED8) - - Used in headlines and CTA buttons. Strong, tech-oriented, and accessible. -- **Aqua Gradient**: `bg-aqua-gradient` (linear-gradient(135deg, #3B82F6, #06B6D4)) - - Available as: `bg-aqua-gradient`, `bg-aqua-gradient-to-r`, `bg-aqua-gradient-to-b` - - Used in backgrounds, section dividers, or digital badges. -- **Brand Gradient**: `bg-brand-gradient` (linear-gradient(135deg, #1D4ED8, #06B6D4)) - - Enhanced brand gradient for premium sections and hero areas. -- **Nordic Gradient**: `bg-nordic-gradient` (linear-gradient(135deg, #6366F1, #1D4ED8)) - - Accent gradient combining nordic purple with cloud blue. - -### Secondary Colors - -- **Sky Mist**: `bg-brand-sky-mist` or `bg-neutral-mist` (#E0F2FE) - - A soft sky blue for background fills, cards, or hover states. -- **Fresh Green**: `bg-brand-fresh-green` or `bg-secondary-500` (#10B981) - - Reflects the green in the logo. Good for highlights, tags, or eco-related themes. -- **Glacier White**: `bg-brand-glacier-white` or `bg-neutral-glacier` (#F9FAFB) - - A clean background neutral to keep the interface minimal and modern. - -### Accent Colors - -- **Nordic Purple**: `bg-brand-nordic-purple` or `bg-accent-purple` (#6366F1) - - Subtle contrast for agenda highlights, speaker names, or session tags. -- **Sunbeam Yellow**: `bg-brand-sunbeam-yellow` or `bg-accent-yellow` (#FACC15) - - For urgency, early-bird ticket alerts, and callouts without breaking the cool-tone harmony. - -### Neutral Base - -- **Slate Gray**: `bg-brand-slate-gray` or `text-neutral-slate` (#334155) - - For body text, navigation, or footer elements. -- **Frosted Steel**: `bg-brand-frosted-steel` or `bg-neutral-steel` (#CBD5E1) - - For dividers, secondary buttons, or muted labels. - -### Convenience Color Classes - -The colors are also mapped to semantic color scales: - -- `bg-primary-{50-950}` - Blue tones (aqua-start to cloud-blue) -- `bg-secondary-{50-950}` - Green tones (centered on fresh-green) - -## Cloud Native Pattern System - -Our animated background patterns incorporate authentic cloud native project logos with intelligent focus/diffusion effects. The pattern system includes icons from major CNCF projects: - -### Container Orchestration - -- Kubernetes, containerd, etcd - the foundation of modern container orchestration - -### Observability & Monitoring - -- Prometheus, Jaeger, Falco for comprehensive system observability and security - -### Service Mesh & Networking - -- Istio, Envoy, Cilium for secure, observable service-to-service communication - -### Packaging & GitOps - -- Helm, Argo, Crossplane for application packaging and deployment automation - -### Available Project Icons - -The pattern system includes white versions of icons from these cloud native projects: - -- Argo, Backstage, Cilium, CloudNativePG, Crossplane, etcd, Falco -- gRPC, Harbor, Helm, Istio, Jaeger, KubeVirt, Kured, Logging Operator -- OpenFeature, Prometheus, Shipwright, VirtualKubelet, Vitess, WasmEdge Runtime - -### Focus/Diffusion Technology - -- **Small Icons (Sharp Focus)**: Higher opacity, vibrant colors, no blur. Draw attention as foreground elements -- **Medium Icons (Balanced)**: Moderate opacity and subtle blur. Provide visual texture without distraction -- **Large Icons (Soft Diffusion)**: Lower opacity, muted colors, soft blur. Create atmospheric background depth - -### Pattern Configuration Options - -- **Content Background**: Subtle pattern for content sections and cards (opacity: 0.06, baseSize: 25, iconCount: 18) -- **Hero Section**: Perfect balance for wide hero sections (opacity: 0.15, baseSize: 52, iconCount: 38) -- **Dramatic Background**: Dense, dramatic effect for special sections (opacity: 0.2, baseSize: 58, iconCount: 55) - -## Icon Library (Heroicons) - -Our comprehensive icon system uses Heroicons with cloud native and tech-focused selections: - -### Platform Icons - -- **Cloud Infrastructure**: CloudIcon - scalable, distributed infrastructure -- **Server & Compute**: ServerIcon - compute resources and workload execution -- **Container & Packaging**: CubeIcon - application packaging and isolation -- **Queue & Lists**: QueueListIcon - task queues and ordered processing - -### Data & Storage Icons - -- **Database & Storage**: CircleStackIcon - data storage and database systems -- **Command Line & CLI**: CommandLineIcon - developer tools and terminal operations -- **Configuration & Settings**: CogIcon - system configuration management -- **Tools & Utilities**: WrenchScrewdriverIcon - development and operational tools - -### Operations Icons - -- **Security & Protection**: ShieldCheckIcon - security measures and compliance -- **Monitoring & Analytics**: ChartBarIcon - observability dashboards and metrics -- **Performance & Speed**: BoltIcon - high-performance computing and deployment -- **Observability & Insights**: EyeIcon - system visibility and monitoring - -### Network & Connectivity Icons - -- **Global Distribution**: GlobeAltIcon - multi-region deployment strategies -- **Service Mesh & Links**: LinkIcon - service interconnection patterns -- **CI/CD & Automation**: ArrowPathIcon - continuous integration and deployment -- **Deployment & Launch**: RocketLaunchIcon - application deployment processes - -### Icon Usage Guidelines - -- **Outline Style**: Clean, stroke-based icons for most UI elements and content sections -- **Solid Style**: Filled icons for emphasis, status indicators, and important highlights -- Available in multiple sizes: 4x4, 6x6, 8x8, 12x12 (Tailwind classes: `h-4 w-4` through `h-12 w-12`) -- Color with brand palette: `text-brand-cloud-blue`, `text-brand-fresh-green`, etc. - -## Email Templates - -The brand system includes professional email templates for all conference communications: - -### Template Types - -1. **Proposal Response Templates** - - Proposal Acceptance Email with speaker onboarding information - - Proposal Rejection Email with constructive feedback and community engagement - -2. **Speaker Communication Templates** - - Direct Speaker Email for individual outreach with rich content support - - Speaker Broadcast Template for speaker-specific announcements - -3. **General Communication Templates** - - Community Broadcast Email with customizable content and unsubscribe management - - Base Email Template providing consistent structure and branding - -### Template Features - -- Consistent branding with logo and color scheme -- Mobile-responsive design with email client compatibility -- Accessible design with proper contrast ratios and alt text -- Automated personalization with dynamic content -- Unsubscribe management and compliance features -- Rich HTML content support with fallback text versions - -## Button System - -Our button system provides consistent, accessible interactions with clear visual hierarchy: - -### Button Variants - -- **Primary**: `bg-brand-cloud-blue` - Main actions, CTAs -- **Secondary**: `border border-brand-cloud-blue text-brand-cloud-blue` - Secondary actions -- **Success**: `bg-brand-fresh-green` - Positive actions, confirmations -- **Warning**: `bg-brand-sunbeam-yellow` - Caution, important notices -- **Danger**: `bg-red-600` - Delete, destructive actions -- **Ghost**: `text-brand-cloud-blue hover:bg-brand-sky-mist` - Subtle actions - -### Button Sizes - -- **Small**: `px-3 py-1.5 text-sm` - Compact spaces, secondary actions -- **Medium**: `px-4 py-2 text-base` - Default size for most buttons -- **Large**: `px-6 py-3 text-lg` - Hero sections, important CTAs - -### Button States - -- **Default**: Normal interactive state -- **Hover**: Enhanced background/border colors -- **Focus**: Visible focus ring for accessibility -- **Disabled**: Reduced opacity, no interactions -- **Loading**: Spinner indicator for async actions - -## Component Examples - -The branding page showcases real implementations of key components: - -### Hero Sections - -- Multiple hero variants with different layouts and messaging -- Background pattern integration with customizable opacity -- Typography combinations showcasing font pairings -- Responsive design with mobile-optimized layouts - -### Speaker Components - -- **Featured Speaker**: Large format with detailed information and CTA -- **Speaker Grid**: Multiple speakers in grid layouts (2x2, 3x3, etc.) -- **Compact Speaker List**: Dense list format for directories -- **Speaker Cards**: Individual speaker cards with consistent styling -- **Social Sharing**: Download-ready speaker images for social media - -### Talk Components - -- **Talk Cards**: Format-specific styling (presentation, workshop, keynote) -- **Talk Promotion**: Banner-style promotion with detailed information -- **Schedule Integration**: Talk cards within schedule context - -### Call-to-Action Components - -- **Standard CTA**: Balanced speaker submission and ticket messaging -- **Speaker Focus**: Emphasizes CFP submissions -- **Ticket Focus**: Emphasizes ticket sales -- **Custom Messaging**: Fully customizable for campaigns -- **Organizer Context**: Community-focused messaging for organizers - -## Usage Guidelines - -### Accessibility Standards - -- All components maintain WCAG 2.1 AA compliance -- Color contrast ratios meet accessibility requirements -- Focus states are clearly visible and consistent -- Alt text provided for all images and icons -- Semantic HTML structure for screen readers - -### Responsive Design - -- Mobile-first approach with progressive enhancement -- Breakpoints: `sm` (640px), `md` (768px), `lg` (1024px), `xl` (1280px) -- Flexible grid systems that adapt to different screen sizes -- Touch-friendly button sizes on mobile devices - -### Performance Considerations - -- Optimized images with appropriate formats (WebP with fallbacks) -- Lazy loading for pattern backgrounds and images -- Minimal animation impact on performance -- Efficient CSS with Tailwind's purging system - -### Brand Consistency - -- Consistent spacing using Tailwind's spacing scale -- Typography hierarchy maintained across all components -- Color usage follows established palette guidelines -- Icon usage follows outlined standards and sizing - -## Typography - -### Primary Fonts (Headings/Branding) - -- **JetBrains Mono**: `font-jetbrains` - - Monospaced font for developers. Playful, readable, distinctly "dev culture." - - Great for hero text, quotes, or session titles. -- **Space Grotesk**: `font-space-grotesk` (also default `font-display`) - - Clean, geometric sans-serif with a slightly quirky personality. - - Offers great contrast and friendliness without losing professionalism. -- **Bricolage Grotesque**: `font-bricolage` - - Grotesque-style with some expressive, almost rebellious energy. - - Matches the "nerdy and proud" community vibe. - -### Secondary Fonts (Body/UI Text) - -- **Inter**: `font-inter` (also default `font-sans`) - - Versatile, neutral sans-serif with high legibility. - - Pairs well with more expressive display fonts. -- **IBM Plex Sans**: `font-ibm-plex-sans` - - Great balance of precision and friendliness. -- **IBM Plex Mono**: `font-ibm-plex-mono` - - Monospaced variant for code snippets. -- **Atkinson Hyperlegible**: `font-atkinson` - - Designed for readability with unique, humanistic forms. - - Strong accessibility and inclusive design signal. - -### Suggested Font Pairings - -1. **JetBrains Mono + Inter**: "Dev terminal meets clean UI" - - ```html -

- Conference Title -

-

Body content here...

- ``` - -2. **Space Grotesk + IBM Plex Sans**: "Playful headings with structured body" - - ```html -

- Section Title -

-

Body content here...

- ``` - -3. **Bricolage Grotesque + Atkinson Hyperlegible**: "Edgy but accessible" - - ```html -

Subsection

-

Accessible body text...

- ``` - -## Usage Examples - -### Hero Section - -```html -
-

Cloud Native Days Norway

-

- A community-driven conference for cloud native technologies -

-
-``` - -### Call to Action Button - -```html - -``` - -### Card Component - -```html -
-

- Speaker Name -

-

Speaker bio and description...

- - Keynote - -
-``` - -### Alert/Notice - -```html -
-

- Early Bird: Limited time offer - register now! -

-
-``` diff --git a/docs/SPONSOR_SYSTEM.md b/docs/SPONSOR_SYSTEM.md index 2224e9d3..132cc801 100644 --- a/docs/SPONSOR_SYSTEM.md +++ b/docs/SPONSOR_SYSTEM.md @@ -19,13 +19,14 @@ All sponsor data is stored in Sanity CMS across five document types: The base company record. Conference-independent — a single sponsor can participate across multiple conferences/years. -| Field | Description | -| ------------ | --------------------------------------------------- | -| `name` | Company name | -| `website` | Company URL | -| `logo` | Inline SVG logo | -| `logoBright` | Optional bright/white variant for dark backgrounds | -| `orgNumber` | Company registration number (admin-only visibility) | +| Field | Description | +| ------------ | ---------------------------------------------------------- | +| `name` | Company name | +| `website` | Company URL | +| `logo` | Inline SVG logo | +| `logoBright` | Optional bright/white variant for dark backgrounds | +| `orgNumber` | Company registration number (admin-only visibility) | +| `address` | Registered company address (admin-only, used in contracts) | ### `sponsorTier` @@ -47,37 +48,67 @@ Defines pricing tiers for a conference. Each tier is scoped to a single conferen The CRM join document linking a sponsor to a conference with relationship metadata. This is the central document the CRM pipeline operates on. -| Field | Description | -| ------------------ | ------------------------------------------------------------------------------------------------------ | -| `sponsor` | Reference to `sponsor` document | -| `conference` | Reference to `conference` document | -| `tier` | Reference to a `sponsorTier` (standard/special) | -| `addons[]` | Array of references to addon-type `sponsorTier` documents | -| `contactPersons[]` | Per-conference contacts (name, email, phone, role, `isPrimary`) | -| `billing` | Per-conference billing info (email, reference, comments) | -| `status` | Pipeline stage: `prospect` → `contacted` → `negotiating` → `closed-won` / `closed-lost` | -| `contractStatus` | `none` → `verbal-agreement` → `contract-sent` → `contract-signed` | -| `invoiceStatus` | `not-sent` → `sent` → `paid` / `overdue` / `cancelled` | -| `assignedTo` | Reference to an organizer (speaker with `isOrganizer: true`) | -| `contractValue` | Actual contract value (defaults to tier price) | -| `contractCurrency` | `NOK`, `USD`, `EUR`, or `GBP` | -| `tags[]` | Classification tags (see Tags section below) | -| `notes` | Freeform text | -| Timestamps | `contactInitiatedAt`, `contractSignedAt`, `invoiceSentAt`, `invoicePaidAt` | - -Contact person roles are defined by `CONTACT_ROLE_OPTIONS` in `src/lib/sponsor/types.ts`. The `isPrimary` flag identifies the main contact for contract signing (Phase 2). +| Field | Description | +| ----------------------- | ------------------------------------------------------------------------------------------------------ | +| `sponsor` | Reference to `sponsor` document | +| `conference` | Reference to `conference` document | +| `tier` | Reference to a `sponsorTier` (standard/special) | +| `addons[]` | Array of references to addon-type `sponsorTier` documents | +| `contactPersons[]` | Per-conference contacts (name, email, phone, role, `isPrimary`) | +| `billing` | Per-conference billing info (email, reference, comments) | +| `status` | Pipeline stage: `prospect` → `contacted` → `negotiating` → `closed-won` / `closed-lost` | +| `contractStatus` | `none` → `verbal-agreement` → `contract-sent` → `contract-signed` | +| `signatureStatus` | Digital signature state: `not-started` → `pending` → `signed` / `rejected` / `expired` | +| `signatureId` | External ID from e-signing provider (read-only) | +| `signerEmail` | Email of the person designated to sign the contract | +| `contractSentAt` | When the contract was sent for signing (read-only) | +| `contractDocument` | Generated PDF contract stored as a Sanity file asset | +| `reminderCount` | Number of contract signing reminders sent (read-only) | +| `contractTemplate` | Reference to the `contractTemplate` used to generate the contract | +| `invoiceStatus` | `not-sent` → `sent` → `paid` / `overdue` / `cancelled` | +| `assignedTo` | Reference to an organizer (speaker with `isOrganizer: true`) | +| `contractValue` | Actual contract value (defaults to tier price) | +| `contractCurrency` | `NOK`, `USD`, `EUR`, or `GBP` | +| `tags[]` | Classification tags (see Tags section below) | +| `notes` | Freeform text | +| `onboardingToken` | Unique token for sponsor self-service onboarding portal (read-only) | +| `onboardingComplete` | Whether the sponsor has completed onboarding (read-only) | +| `onboardingCompletedAt` | When the sponsor completed onboarding (read-only) | +| Timestamps | `contactInitiatedAt`, `contractSignedAt`, `invoiceSentAt`, `invoicePaidAt` | + +Contact person roles are defined by `CONTACT_ROLE_OPTIONS` in `src/lib/sponsor/types.ts`. The `isPrimary` flag identifies the main contact for contract signing. + +### `contractTemplate` + +Defines the structure and content for contract PDF generation. Each template is scoped to a conference and optionally to a specific tier. Supports variable substitution via `{{{VARIABLE}}}` placeholders. + +| Field | Description | +| ------------ | --------------------------------------------------------------------- | +| `title` | Internal name for identification | +| `conference` | Reference to the owning conference | +| `tier` | Optional reference to a `sponsorTier` for tier-specific contracts | +| `language` | `nb` (Norwegian) or `en` (English) | +| `currency` | Default currency for this template | +| `sections[]` | Ordered array of `{ heading, body }` — body is PortableText with vars | +| `headerText` | Text shown in PDF header (e.g. organization name) | +| `footerText` | Text shown in PDF footer (e.g. org number, contact info) | +| `terms` | General terms & conditions (PortableText, included as Appendix 1) | +| `isDefault` | Fallback template when no tier-specific template exists | +| `isActive` | Whether this template is available for use | + +**Contract template variables:** `SPONSOR_NAME`, `SPONSOR_ORG_NUMBER`, `SPONSOR_ADDRESS`, `SPONSOR_WEBSITE`, `CONTACT_NAME`, `CONTACT_EMAIL`, `TIER_NAME`, `TIER_TAGLINE`, `CONTRACT_VALUE`, `CONTRACT_VALUE_NUMBER`, `CONTRACT_CURRENCY`, `CONFERENCE_TITLE`, `CONFERENCE_DATE`, `CONFERENCE_DATES`, `CONFERENCE_YEAR`, `CONFERENCE_CITY`, `VENUE_NAME`, `VENUE_ADDRESS`, `TODAY_DATE`, `ORG_NAME`, `ORG_ORG_NUMBER`, `ORG_ADDRESS`, `ORG_EMAIL`, `ADDONS_LIST`. ### `sponsorActivity` Audit log for CRM actions. Each activity references a `sponsorForConference` document. -| Field | Description | -| -------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `activityType` | `stage_change`, `invoice_status_change`, `contract_status_change`, `contract_signed`, `note`, `email`, `call`, `meeting` | -| `description` | Human-readable summary | -| `metadata` | Structured data with `oldValue`, `newValue`, `timestamp`, `additionalData` | -| `createdBy` | Reference to the organizer who performed the action | -| `createdAt` | ISO timestamp | +| Field | Description | +| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `activityType` | `stage_change`, `invoice_status_change`, `contract_status_change`, `contract_signed`, `note`, `email`, `call`, `meeting`, `signature_status_change`, `onboarding_complete`, `contract_reminder_sent` | +| `description` | Human-readable summary | +| `metadata` | Structured data with `oldValue`, `newValue`, `timestamp`, `additionalData` | +| `createdBy` | Reference to the organizer who performed the action | +| `createdAt` | ISO timestamp | ## Status Enumerations @@ -91,6 +122,10 @@ All CRM status values are defined as TypeScript union types in `src/lib/sponsor- `none` → `verbal-agreement` → `contract-sent` → `contract-signed` +### Signature Status (`SignatureStatus`) + +`not-started` → `pending` → `signed` / `rejected` / `expired` + ### Invoice Status (`InvoiceStatus`) `not-sent` → `sent` → `paid` / `overdue` / `cancelled` @@ -135,17 +170,25 @@ src/ │ ├── activity.ts # Activity logging helpers │ ├── activities.ts # Activity list/query operations │ ├── action-items.ts # Action item management -│ └── bulk.ts # Bulk update/delete operations +│ ├── bulk.ts # Bulk update/delete operations +│ ├── contract-templates.ts # Contract template CRUD and lookup +│ ├── contract-variables.ts # Variable substitution for contract generation +│ ├── contract-pdf.tsx # PDF generation using React-PDF +│ ├── contract-readiness.ts # Contract signing readiness validation +│ ├── onboarding.ts # Sponsor self-service onboarding (token, validation, completion) +│ └── pipeline.ts # Pipeline aggregation utilities ├── server/ │ ├── routers/sponsor.ts # tRPC router (all sponsor procedures) │ └── schemas/ │ ├── sponsor.ts # Zod schemas for core sponsor operations -│ └── sponsorForConference.ts # Zod schemas for CRM operations +│ ├── sponsorForConference.ts # Zod schemas for CRM operations +│ └── onboarding.ts # Zod schemas for onboarding submissions ├── components/ │ ├── Sponsors.tsx # Public sponsor display (grouped by tier) │ ├── SponsorLogo.tsx # Public inline SVG logo renderer │ ├── SponsorThankYou.tsx # Marketing thank-you card for sponsors │ ├── sponsor/ +│ │ ├── SponsorOnboardingForm.tsx # Sponsor self-service onboarding form │ │ └── SponsorProspectus.tsx # Public sponsorship prospectus page │ └── admin/ │ ├── sponsor/ # Sponsor management admin UI @@ -173,7 +216,10 @@ src/ │ ├── SponsorCard.tsx # Kanban card │ ├── SponsorBulkActions.tsx # Multi-select action bar │ ├── BoardViewSwitcher.tsx # Pipeline/Contract/Invoice toggle +│ ├── ContractReadinessIndicator.tsx # Contract readiness status display +│ ├── OnboardingLinkButton.tsx # Onboarding link generation button │ ├── ImportHistoricSponsorsButton.tsx # Historic import dialog +│ ├── MobileFilterSheet.tsx # Mobile-responsive filter panel │ ├── utils.ts # CRM-specific UI utilities │ └── form/ # Form sub-components │ ├── constants.ts # Status/tag constants with labels & icons @@ -190,12 +236,16 @@ src/ │ ├── useSponsorCRMFormMutations.ts # CRM form mutation hooks │ └── useSponsorDragDrop.ts # Drag-and-drop for board columns └── app/ - ├── (main)/sponsor/page.tsx # Public /sponsor prospectus page + ├── (main)/sponsor/ + │ ├── page.tsx # Public /sponsor prospectus page + │ ├── terms/page.tsx # Public sponsor terms page + │ └── onboarding/[token]/page.tsx # Sponsor self-service onboarding └── (admin)/admin/ ├── sponsors/ │ ├── page.tsx # Sponsor management page │ ├── crm/page.tsx # CRM pipeline page │ ├── tiers/page.tsx # Tier management page + │ ├── contracts/page.tsx # Contract template management page │ ├── templates/page.tsx # Email template management page │ └── activity/page.tsx # Activity log page └── marketing/page.tsx # Marketing page (includes SponsorThankYou) @@ -205,20 +255,22 @@ sanity/schemaTypes/ ├── sponsorTier.ts # Tier document schema ├── sponsorForConference.ts # CRM join document schema ├── sponsorActivity.ts # Activity log schema -└── sponsorEmailTemplate.ts # Email template schema +├── sponsorEmailTemplate.ts # Email template schema +└── contractTemplate.ts # Contract template schema ``` ### API Layer All sponsor operations go through a single tRPC router at `src/server/routers/sponsor.ts`, organized into namespaces. See `docs/TRPC_SERVER_ARCHITECTURE.md` for general tRPC patterns. -| Namespace | Procedures | Purpose | -| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- | -| `sponsor.*` | `list`, `getById`, `create`, `update`, `delete` | Core sponsor company CRUD | -| `sponsor.tiers.*` | `list`, `listByConference`, `getById`, `create`, `update`, `delete` | Tier management | -| `sponsor.crm.*` | `listOrganizers`, `list`, `getById`, `create`, `update`, `moveStage`, `updateInvoiceStatus`, `updateContractStatus`, `bulkUpdate`, `bulkDelete`, `delete`, `copyFromPreviousYear`, `importAllHistoric` | CRM pipeline operations | -| `sponsor.crm.activities.*` | `list` | Activity log queries | -| `sponsor.emailTemplates.*` | `list`, `create`, `update`, `delete` | Email template CRUD | +| Namespace | Procedures | Purpose | +| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | +| `sponsor.*` | `list`, `getById`, `create`, `update`, `delete` | Core sponsor company CRUD | +| `sponsor.tiers.*` | `list`, `listByConference`, `getById`, `create`, `update`, `delete` | Tier management | +| `sponsor.crm.*` | `listOrganizers`, `list`, `getById`, `create`, `update`, `moveStage`, `updateInvoiceStatus`, `updateContractStatus`, `bulkUpdate`, `bulkDelete`, `delete`, `copyFromPreviousYear`, `importAllHistoric` | CRM pipeline operations | +| `sponsor.crm.activities.*` | `list` | Activity log queries | +| `sponsor.emailTemplates.*` | `list`, `create`, `update`, `delete` | Email template CRUD | +| `sponsor.contractTemplates.*` | `list`, `get`, `create`, `update`, `delete`, `findBest`, `contractReadiness`, `generatePdf` | Contract template CRUD and PDF gen | All procedures are protected by `adminProcedure` (requires `isOrganizer: true`). @@ -250,6 +302,53 @@ Sponsor contact management integrates with the email system (see `docs/EMAIL_SYS Sponsor tier assignments feed into the ticket allocation system, where each tier level maps to a specific number of complimentary tickets (configured in the tickets admin page). +## Contract System + +The contract system enables organizers to generate, manage, and (eventually) digitally sign sponsorship agreements directly from the CRM. + +### Contract Templates + +Contract templates are stored in Sanity as `contractTemplate` documents. Each template belongs to a conference and contains ordered sections with PortableText bodies that support `{{{VARIABLE}}}` substitution. Templates can be scoped to a specific tier or marked as a default fallback. + +The `findBestContractTemplate()` function selects the most appropriate template by matching on conference, tier, and language — falling back to the default template if no tier-specific one exists. + +### Contract PDF Generation + +PDF generation uses React-PDF (`@react-pdf/renderer`) to produce professional contract documents. The generated PDF includes: + +- Header with organizer name and logo +- Info table with parties, dates, and venue details +- Contract sections with variable substitution +- Package/tier details table +- Appendix 1: General Terms & Conditions +- Footer with organizer contact information + +Variable values are built from the `SponsorForConferenceExpanded` record by `buildContractVariables()` in `contract-variables.ts`. + +### Contract Readiness + +Before a contract can be generated or sent, all required data must be present. The `checkContractReadiness()` function in `contract-readiness.ts` validates 11 required fields and categorizes any missing ones by responsible party: + +| Source | Required Fields | +| ------------- | ---------------------------------------------------------------------- | +| **Organizer** | Conference name, org number, address, dates, venue name, sponsor email | +| **Sponsor** | Org number, address, primary contact person | +| **Pipeline** | Tier assignment, contract value | + +The `ContractReadinessIndicator` component displays readiness status in the CRM form — green when ready, amber with a categorized list of missing fields when not. + +## Sponsor Self-Service Onboarding + +The onboarding portal (`/sponsor/onboarding/[token]`) allows sponsors to self-service their data entry after an organizer initiates the relationship. The flow: + +1. Organizer generates a unique onboarding token via the CRM +2. Sponsor receives a link (e.g. via email) to `/sponsor/onboarding/{token}` +3. Sponsor fills in: company information (org number, address), contact persons, and billing details +4. On submission, the system patches both the `sponsor` document (org data) and the `sponsorForConference` document (contacts, billing) +5. An `onboarding_complete` activity is logged + +Token validation checks existence, expiry, and whether onboarding was already completed. + ## Public-Facing Components The public website displays sponsors using data fetched from Sanity (not through tRPC): @@ -263,16 +362,19 @@ The public website displays sponsors using data fetched from Sanity (not through Tests are located in `__tests__/` mirroring the source structure: -| Test file | Covers | -| ------------------------------------------ | -------------------------------------------- | -| `lib/sponsor/validation.test.ts` | Sponsor and tier input validation | -| `lib/sponsor/utils.test.ts` | Tier sorting, formatting, grouping utilities | -| `lib/sponsor/templates.test.ts` | Template variable processing utilities | -| `lib/sponsor/sponsorForConference.test.ts` | CRM Zod schema validation | -| `lib/sponsor-crm/bulk.test.ts` | Bulk update/delete operations | -| `components/Sponsors.test.tsx` | Public sponsor display component | -| `components/SponsorLogo.test.tsx` | Logo rendering | -| `components/SponsorProspectus.test.tsx` | Prospectus page | +| Test file | Covers | +| -------------------------------------------- | -------------------------------------------- | +| `lib/sponsor/validation.test.ts` | Sponsor and tier input validation | +| `lib/sponsor/utils.test.ts` | Tier sorting, formatting, grouping utilities | +| `lib/sponsor/templates.test.ts` | Template variable processing utilities | +| `lib/sponsor/sponsorForConference.test.ts` | CRM Zod schema validation | +| `lib/sponsor-crm/bulk.test.ts` | Bulk update/delete operations | +| `lib/sponsor-crm/contract-readiness.test.ts` | Contract readiness validation logic | +| `lib/sponsor-crm/contract-variables.test.ts` | Contract variable building and substitution | +| `lib/sponsor-crm/onboarding.test.ts` | Onboarding URL building | +| `components/Sponsors.test.tsx` | Public sponsor display component | +| `components/SponsorLogo.test.tsx` | Logo rendering | +| `components/SponsorProspectus.test.tsx` | Prospectus page | ## Roadmap @@ -304,13 +406,13 @@ End-to-end sponsor contract workflow with digital signatures, automated reminder | Issue | Summary | Status | | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------ | -| [#300](https://github.com/CloudNativeBergen/website/issues/300) | Schema extensions (`signature_status`, `signer_email`, `contract_document`, `isPrimary`) | Open | +| [#300](https://github.com/CloudNativeBergen/website/issues/300) | Schema extensions (`signature_status`, `signer_email`, `contract_document`, `isPrimary`) | Done | | [#301](https://github.com/CloudNativeBergen/website/issues/301) | Contract template system with tier-based PDF generation | Done | | [#302](https://github.com/CloudNativeBergen/website/issues/302) | Sponsor email templates (ContractSent, ContractReminder, WelcomeOnboarding) | Open | | [#303](https://github.com/CloudNativeBergen/website/issues/303) | Posten.no e-signature integration (OAuth2, BankID, webhooks) | Open | | [#304](https://github.com/CloudNativeBergen/website/issues/304) | Admin UI — send contract flow with signature status badges | Open | | [#305](https://github.com/CloudNativeBergen/website/issues/305) | Automated contract reminders (daily cron, Slack notifications) | Open | -| [#306](https://github.com/CloudNativeBergen/website/issues/306) | Sponsor self-service onboarding portal (`/sponsor/onboarding/[token]`) | Open | +| [#306](https://github.com/CloudNativeBergen/website/issues/306) | Sponsor self-service onboarding portal (`/sponsor/onboarding/[token]`) | Done | ### Related Issues @@ -320,7 +422,7 @@ End-to-end sponsor contract workflow with digital signatures, automated reminder ## Key Design Decisions -**Separated sponsor vs. CRM types.** A sponsor company (`sponsor`) is a conference-independent entity holding only company-level data (name, logo, website). Contact persons and billing details live on `sponsorForConference` — the per-conference relationship record — since contacts and billing arrangements often differ between conferences/years. +**Separated sponsor vs. CRM types.** A sponsor company (`sponsor`) is a conference-independent entity holding only company-level data (name, logo, website, org number, address). Contact persons and billing details live on `sponsorForConference` — the per-conference relationship record — since contacts and billing arrangements often differ between conferences/years. **`sponsorForConference` as the single source of truth.** All conference-sponsor relationships are managed exclusively through `sponsorForConference` documents. There is no inline `sponsors[]` array on conference documents. Public-facing pages query `sponsorForConference` docs with `status == "closed-won"` and project them into the `ConferenceSponsor` shape. The `Conference.sponsors` TypeScript property is populated at runtime from this query for backward compatibility with downstream consumers. diff --git a/docs/STORYBOOK_STRUCTURE.md b/docs/STORYBOOK_STRUCTURE.md new file mode 100644 index 00000000..dd033de7 --- /dev/null +++ b/docs/STORYBOOK_STRUCTURE.md @@ -0,0 +1,250 @@ +# Storybook Structure & Best Practices + +This document outlines our approach to organizing and writing Storybook stories for the Cloud Native Days Norway component library. + +## Story Organization Principles + +### 1. One Interactive/Playground Story + +Every component should have a primary **Interactive** story with full controls enabled. This allows users to: + +- Experiment with different prop combinations +- Quickly test component behavior +- Use as a live playground + +**Example:** + +```typescript +export const Interactive: Story = { + args: { + variant: 'primary', + size: 'md', + children: 'Button Text', + }, + parameters: { + docs: { + description: { + story: + 'Interactive playground - use controls to experiment with different variants, sizes, and states.', + }, + }, + }, +} +``` + +### 2. Visual Comparison Stories + +For components with multiple variants or states, create a **Visual Variants** or **AllVariants** story that displays them side-by-side. This enables: + +- Quick visual inspection without clicking through stories +- Easy design review and QA +- Visual regression testing +- Documentation of all available options + +**Example:** + +```typescript +export const AllVariants: Story = { + render: () => ( +
+ + + +
+ ), + parameters: { + docs: { + description: { + story: 'Comprehensive visual overview for design review and visual regression testing.', + }, + }, + }, +} +``` + +### 3. Specific State Stories + +Keep dedicated stories for: + +- **Important variants** with significant visual differences (for visual regression testing) +- **Edge cases** that test specific scenarios (long text, empty states, error states) +- **Common use cases** that stakeholders frequently reference + +**When to create separate stories:** + +- ✅ Different status states (Prospect, Contacted, Negotiating, etc.) - each has unique styling +- ✅ Edge cases (NoLogo, LongSponsorName, EmptyState) +- ✅ Common configurations (WithIcon, Disabled, Loading) + +**When to use controls instead:** + +- ❌ Simple visual variants (colors) - use interactive story +- ❌ Size variations (sm, md, lg) - use interactive story or AllVariants +- ❌ Boolean toggles - use interactive story + +## Hierarchy Structure + +Our Storybook uses a multi-level hierarchy. Refer to the `AGENTS.md` Storybook & Design System section for the canonical, up-to-date structure. The top-level categories are: + +```text +Getting Started/ - Introduction and developer guides +Design System/ - Brand, Foundation, Examples +Components/{Category}/ - Generic reusable components (Data Display, Feedback, Forms, Icons, Layout) +Systems/{SystemName}/ - Domain-specific components (Program, Proposals, Speakers, Sponsors) +``` + +### Placing New Stories + +- **Generic reusable components** → `Components/{Category}/ComponentName` (e.g., `Components/Layout/Button`) +- **Domain-specific components** → `Systems/{SystemName}/ComponentName` (e.g., `Systems/Program/TalkCard`) +- **Admin components for a system** → `Systems/{SystemName}/Admin/ComponentName` (e.g., `Systems/Sponsors/Admin/Pipeline/SponsorCard`) +- **Integration examples** → `Design System/Examples/ExampleName` + +## ArgTypes Configuration + +Always configure `argTypes` to improve the Controls panel experience: + +```typescript +argTypes: { + variant: { + control: 'select', + options: ['primary', 'secondary', 'success'], + description: 'Visual style variant following brand color system', + }, + size: { + control: 'select', + options: ['sm', 'md', 'lg'], + description: 'Component size', + }, + disabled: { + control: 'boolean', + description: 'Disable interaction', + }, + children: { + control: 'text', + description: 'Content to display', + }, +} +``` + +## Story Descriptions + +Add descriptions at two levels: + +### 1. Component-level Description + +In the meta object's `parameters.docs.description.component`: + +```typescript +parameters: { + docs: { + description: { + component: 'Consistent, accessible button system...', + }, + }, +} +``` + +### 2. Story-level Description + +In individual stories' `parameters.docs.description.story`: + +```typescript +parameters: { + docs: { + description: { + story: 'Tests layout with very long sponsor name...', + }, + }, +} +``` + +## Visual Regression Testing + +Stories serve as visual regression test cases. When using tools like Chromatic or Percy: + +- Each story becomes a test snapshot +- Individual variant stories help identify exactly what changed +- Visual comparison stories help catch layout issues + +**Best Practice:** Keep individual stories for states that need precise visual testing (status changes, error states, etc.) + +## Common Patterns + +### Interactive Components (State Management) + +```typescript +export const Interactive: Story = { + render: () => { + const [value, setValue] = useState('initial') + return + }, +} +``` + +### Async Server Components (Next.js) + +```typescript +function ComponentWrapper(props: Parameters[0]) { + const [rendered, setRendered] = useState(null) + + useEffect(() => { + Component(props).then(setRendered) + }, [props]) + + if (!rendered) return
Loading...
+ return rendered +} +``` + +### Grid Layouts for Comparison + +```typescript +render: () => ( +
+
+

Variant 1

+ +
+
+

Variant 2

+ +
+
+) +``` + +## Checklist for New Components + +When creating Storybook stories for a new component: + +- [ ] Create `ComponentName.stories.tsx` next to the component file +- [ ] Add component to appropriate hierarchy (`Components/{Category}/` or `Systems/{SystemName}/`) +- [ ] Add `tags: ['autodocs']` to meta for automatic documentation +- [ ] Configure `argTypes` for all important props +- [ ] Add component-level description +- [ ] Create **Interactive** story with controls +- [ ] Create **VisualVariants/AllVariants** story if component has multiple variants +- [ ] Add stories for important edge cases +- [ ] Add story-level descriptions explaining each use case +- [ ] Test with `pnpm storybook:test` to ensure no rendering errors +- [ ] Document any special setup requirements (decorators, mocks, etc.) + +## Testing Stories + +Run the test runner to verify all stories render without errors: + +```bash +# Run all story tests +pnpm storybook:test + +# Run specific story file +pnpm storybook:test --grep "ButtonStories" +``` + +## References + +- [Storybook Best Practices](https://storybook.js.org/docs/writing-stories/stories-for-multiple-frameworks) +- [Component Story Format (CSF)](https://storybook.js.org/docs/api/csf) +- [Controls Documentation](https://storybook.js.org/docs/essentials/controls) +- [Visual Testing Guide](https://storybook.js.org/docs/writing-tests/visual-testing) diff --git a/eslint.config.js b/eslint.config.js index 50be1e09..cc053712 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,6 +2,7 @@ const nextPlugin = require('@next/eslint-plugin-next') const tseslint = require('typescript-eslint') const reactHooksPlugin = require('eslint-plugin-react-hooks') const importPlugin = require('eslint-plugin-import') +const storybookPlugin = require('eslint-plugin-storybook') const eslintConfig = [ // Global ignores - these apply to all configurations @@ -28,12 +29,16 @@ const eslintConfig = [ '__tests__/**/*.test.{js,ts,tsx}', '__tests__/**/testdata/**', '__tests__/**/mocks/**', + 'storybook-static/**', // Built Storybook output ], }, // TypeScript configuration ...tseslint.configs.recommended, + // Storybook configuration + ...storybookPlugin.configs['flat/recommended'], + // Main configuration for TypeScript and JavaScript files { plugins: { diff --git a/knip.json b/knip.json index 6961b7e7..1041c55b 100644 --- a/knip.json +++ b/knip.json @@ -13,11 +13,15 @@ "sanity.config.ts", "sanity/actions/**/*.js", "scripts/**/*.ts", - "migrations/**/index.ts" + "migrations/**/index.ts", + "src/**/*.stories.{ts,tsx}", + "src/docs/**/*.{ts,tsx}", + ".storybook/**/*.{ts,tsx}" ], "project": [ "src/**/*.{ts,tsx,js,jsx}", "sanity/**/*.{ts,tsx,js,jsx}", + ".storybook/**/*.{ts,tsx,js,jsx}", "!src/**/*.test.{ts,tsx}", "!**/__tests__/**" ], @@ -32,13 +36,16 @@ "migrations/**", "**/middleware.ts", "src/lib/empty-module.ts", - "scripts/strip-comments.ts" + "scripts/strip-comments.ts", + "storybook-static/**", + "src/components/email/CoSpeakerResponseTemplate.tsx" ], "ignoreDependencies": [ "eslint-config-next", "postcss", "jest-environment-jsdom", - "ts-node" + "ts-node", + "chromatic" ], "ignoreExportsUsedInFile": true, "includeEntryExports": true diff --git a/migrations/033-add-signature-onboarding-fields/index.ts b/migrations/033-add-signature-onboarding-fields/index.ts new file mode 100644 index 00000000..65f3bb45 --- /dev/null +++ b/migrations/033-add-signature-onboarding-fields/index.ts @@ -0,0 +1,21 @@ +import { defineMigration, at, setIfMissing } from 'sanity/migrate' + +export default defineMigration({ + title: 'Add signature and onboarding fields to sponsorForConference', + description: + 'Backfills default values for new signatureStatus, reminderCount, and onboardingComplete fields ' + + 'on all sponsorForConference documents to support contract signing and sponsor onboarding features.', + documentTypes: ['sponsorForConference'], + + migrate: { + document(_doc) { + const operations = [] + + operations.push(at('signatureStatus', setIfMissing('not-started'))) + operations.push(at('reminderCount', setIfMissing(0))) + operations.push(at('onboardingComplete', setIfMissing(false))) + + return operations + }, + }, +}) diff --git a/package.json b/package.json index 5d366587..36c275f9 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,10 @@ "format:check": "prettier --check .", "format": "prettier --write --list-different .", "typecheck": "tsc --noEmit", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", + "storybook:test": "test-storybook --maxWorkers=2", + "storybook:test-ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"pnpm build-storybook --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:127.0.0.1:6006 && pnpm storybook:test\"", "sanity": "sanity", "manage-orphaned-files": "tsx scripts/manage-orphaned-files.ts" }, @@ -38,6 +42,7 @@ "@portabletext/to-html": "^5.0.1", "@portabletext/types": "^4.0.1", "@react-email/render": "^2.0.4", + "@react-pdf/renderer": "^4.3.2", "@sanity/image-url": "^2.0.3", "@sanity/types": "^5.8.1", "@tailwindcss/forms": "^0.5.11", @@ -86,6 +91,9 @@ "@sanity/migrate": "^5.2.3", "@sanity/vision": "^5.8.1", "@starefossen/sanity-plugin-inline-svg-input": "^1.3.7", + "@storybook/addon-docs": "^10.2.8", + "@storybook/nextjs-vite": "^10.2.8", + "@storybook/test-runner": "^0.24.2", "@tailwindcss/postcss": "^4.1.18", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -97,18 +105,33 @@ "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "autoprefixer": "^10.4.24", + "chromatic": "^12.0.0", + "concurrently": "^9.1.2", "eslint": "^10.0.0", "eslint-config-next": "^16.1.6", "eslint-plugin-import": "^2.32.0", "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-storybook": "^10.2.8", + "glob": "^13.0.2", + "http-server": "^14.1.1", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "knip": "^5.83.1", + "msw": "^2.12.10", + "msw-storybook-addon": "^2.0.6", + "playwright": "^1.58.2", "prettier": "^3.8.1", "prettier-plugin-tailwindcss": "^0.7.2", + "storybook": "^10.2.8", "ts-jest": "^29.4.6", "ts-node": "^10.9.2", "tsx": "^4.21.0", - "typescript-eslint": "^8.54.0" + "typescript-eslint": "^8.54.0", + "wait-on": "^8.0.3" + }, + "msw": { + "workerDirectory": [ + "public" + ] } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13655e21..fbb1a640 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: '@react-email/render': specifier: ^2.0.4 version: 2.0.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@react-pdf/renderer': + specifier: ^4.3.2 + version: 4.3.2(react@19.2.4) '@sanity/image-url': specifier: ^2.0.3 version: 2.0.3 @@ -186,6 +189,15 @@ importers: '@starefossen/sanity-plugin-inline-svg-input': specifier: ^1.3.7 version: 1.3.7(@noble/hashes@2.0.1)(@sanity/ui@3.1.11(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react-is@19.2.4)(react@19.2.4)(styled-components@6.3.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sanity@5.8.1(@emotion/is-prop-valid@1.4.0)(@noble/hashes@2.0.1)(@swc/helpers@0.5.18)(@types/node@25.2.2)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(babel-plugin-react-compiler@1.0.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(styled-components@6.3.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(styled-components@6.3.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + '@storybook/addon-docs': + specifier: ^10.2.8 + version: 10.2.8(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@storybook/nextjs-vite': + specifier: ^10.2.8 + version: 10.2.8(@babel/core@7.29.0)(esbuild@0.27.3)(next@16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@storybook/test-runner': + specifier: ^0.24.2 + version: 0.24.2(@swc/helpers@0.5.18)(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3)) '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.1.18 @@ -219,6 +231,12 @@ importers: autoprefixer: specifier: ^10.4.24 version: 10.4.24(postcss@8.5.6) + chromatic: + specifier: ^12.0.0 + version: 12.2.0 + concurrently: + specifier: ^9.1.2 + version: 9.2.1 eslint: specifier: ^10.0.0 version: 10.0.0(jiti@2.6.1) @@ -231,6 +249,15 @@ importers: eslint-plugin-react-hooks: specifier: ^7.0.1 version: 7.0.1(eslint@10.0.0(jiti@2.6.1)) + eslint-plugin-storybook: + specifier: ^10.2.8 + version: 10.2.8(eslint@10.0.0(jiti@2.6.1))(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + glob: + specifier: ^13.0.2 + version: 13.0.2 + http-server: + specifier: ^14.1.1 + version: 14.1.1 jest: specifier: ^30.2.0 version: 30.2.0(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3)) @@ -240,12 +267,24 @@ importers: knip: specifier: ^5.83.1 version: 5.83.1(@types/node@25.2.2)(typescript@5.9.3) + msw: + specifier: ^2.12.10 + version: 2.12.10(@types/node@25.2.2)(typescript@5.9.3) + msw-storybook-addon: + specifier: ^2.0.6 + version: 2.0.6(msw@2.12.10(@types/node@25.2.2)(typescript@5.9.3)) + playwright: + specifier: ^1.58.2 + version: 1.58.2 prettier: specifier: ^3.8.1 version: 3.8.1 prettier-plugin-tailwindcss: specifier: ^0.7.2 version: 0.7.2(prettier@3.8.1) + storybook: + specifier: ^10.2.8 + version: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) ts-jest: specifier: ^29.4.6 version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(esbuild@0.27.3)(jest-util@30.2.0)(jest@30.2.0(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3)))(typescript@5.9.3) @@ -258,6 +297,9 @@ importers: typescript-eslint: specifier: ^8.54.0 version: 8.54.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) + wait-on: + specifier: ^8.0.3 + version: 8.0.5 packages: @@ -1516,6 +1558,32 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@hapi/address@5.1.1': + resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} + engines: {node: '>=14.0.0'} + + '@hapi/formula@3.0.2': + resolution: {integrity: sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==} + + '@hapi/hoek@11.0.7': + resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} + + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + + '@hapi/pinpoint@2.0.1': + resolution: {integrity: sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==} + + '@hapi/tlds@1.1.5': + resolution: {integrity: sha512-Vq/1gnIIsvFUpKlDdfrPd/ssHDpAyBP/baVukh3u2KSG2xoNjsnRNjQiPmuyPPGqsn1cqVWWhtZHfOBaLizFRQ==} + engines: {node: '>=14.0.0'} + + '@hapi/topo@5.1.0': + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + + '@hapi/topo@6.0.2': + resolution: {integrity: sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==} + '@headlessui/react@2.2.9': resolution: {integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==} engines: {node: '>=10'} @@ -1703,6 +1771,10 @@ packages: cpu: [x64] os: [win32] + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} + engines: {node: '>=18'} + '@inquirer/ansi@2.0.3': resolution: {integrity: sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} @@ -1716,6 +1788,15 @@ packages: '@types/node': optional: true + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/confirm@6.0.4': resolution: {integrity: sha512-WdaPe7foUnoGYvXzH4jp4wH/3l+dBhZ3uwhKjXjwdrq5tEIFaANxj6zrGHxLdsIA0yKM0kFPVcEalOZXBB5ISA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} @@ -1725,6 +1806,15 @@ packages: '@types/node': optional: true + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/core@11.1.1': resolution: {integrity: sha512-hV9o15UxX46OyQAtaoMqAOxGR8RVl1aZtDx1jHbCtSJy1tBdTfKxLPKf7utsE4cRy4tcmCQ4+vdV+ca+oNxqNA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} @@ -1761,6 +1851,10 @@ packages: '@types/node': optional: true + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + engines: {node: '>=18'} + '@inquirer/figures@2.0.3': resolution: {integrity: sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} @@ -1828,6 +1922,15 @@ packages: '@types/node': optional: true + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/type@4.0.3': resolution: {integrity: sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} @@ -1874,6 +1977,10 @@ packages: node-notifier: optional: true + '@jest/create-cache-key-function@30.2.0': + resolution: {integrity: sha512-44F4l4Enf+MirJN8X/NhdGkl71k5rBYiwdVlo4HxOwbu0sHV8QKrGEedb1VUU4K3W7fBKE0HGfbn7eZm0Ti3zg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/diff-sequences@30.0.1': resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1953,6 +2060,15 @@ packages: resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4': + resolution: {integrity: sha512-6PyZBYKnnVNqOSB0YFly+62R7dmov8segT27A+RVTBVd4iAE6kbW9QBJGlyR2yG4D4ohzhZSTIu7BK1UTtmFFA==} + peerDependencies: + typescript: '>= 4.3.x' + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1990,10 +2106,20 @@ packages: '@marijn/find-cluster-break@1.0.2': resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@mdx-js/react@3.1.1': + resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} + peerDependencies: + '@types/react': '>=16' + react: '>=16' + '@mswjs/interceptors@0.39.8': resolution: {integrity: sha512-2+BzZbjRO7Ct61k8fMNHEtoKjeWI9pIlHFTqBwZ5icHpqszIgEZbjb1MW5Z0+bITTCTl3gk4PDBxs9tA/csXvA==} engines: {node: '>=18'} + '@mswjs/interceptors@0.41.2': + resolution: {integrity: sha512-7G0Uf0yK3f2bjElBLGHIQzgRgMESczOMyYVasq1XK8P5HaXtlW4eQhz9MBL+TQILZLaruq+ClGId+hH0w4jvWw==} + engines: {node: '>=18'} + '@mux/mux-data-google-ima@0.2.8': resolution: {integrity: sha512-0ZEkHdcZ6bS8QtcjFcoJeZxJTpX7qRIledf4q1trMWPznugvtajCjCM2kieK/pzkZj1JM6liDRFs1PJSfVUs2A==} @@ -2025,6 +2151,9 @@ packages: '@napi-rs/wasm-runtime@1.1.1': resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + '@next/env@16.0.0': + resolution: {integrity: sha512-s5j2iFGp38QsG1LWRQaE2iUY3h1jc014/melHFfLdrsMJPqxqDQwWNwyQTcNoUSGZlCVZuM7t7JDMmSyRilsnA==} + '@next/env@16.1.6': resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} @@ -2430,6 +2559,49 @@ packages: react: ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^18.0 || ^19.0 || ^19.0.0-rc + '@react-pdf/fns@3.1.2': + resolution: {integrity: sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g==} + + '@react-pdf/font@4.0.4': + resolution: {integrity: sha512-8YtgGtL511txIEc9AjiilpZ7yjid8uCd8OGUl6jaL3LIHnrToUupSN4IzsMQpVTCMYiDLFnDNQzpZsOYtRS/Pg==} + + '@react-pdf/image@3.0.4': + resolution: {integrity: sha512-z0ogVQE0bKqgXQ5smgzIU857rLV7bMgVdrYsu3UfXDDLSzI7QPvzf6MFTFllX6Dx2rcsF13E01dqKPtJEM799g==} + + '@react-pdf/layout@4.4.2': + resolution: {integrity: sha512-gNu2oh8MiGR+NJZYTJ4c4q0nWCESBI6rKFiodVhE7OeVAjtzZzd6l65wsN7HXdWJqOZD3ttD97iE+tf5SOd/Yg==} + + '@react-pdf/pdfkit@4.1.0': + resolution: {integrity: sha512-Wm/IOAv0h/U5Ra94c/PltFJGcpTUd/fwVMVeFD6X9tTTPCttIwg0teRG1Lqq617J8K4W7jpL/B0HTH0mjp3QpQ==} + + '@react-pdf/png-js@3.0.0': + resolution: {integrity: sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==} + + '@react-pdf/primitives@4.1.1': + resolution: {integrity: sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==} + + '@react-pdf/reconciler@2.0.0': + resolution: {integrity: sha512-7zaPRujpbHSmCpIrZ+b9HSTJHthcVZzX0Wx7RzvQGsGBUbHP4p6s5itXrAIOuQuPvDepoHGNOvf6xUuMVvdoyw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@react-pdf/render@4.3.2': + resolution: {integrity: sha512-el5KYM1sH/PKcO4tRCIm8/AIEmhtraaONbwCrBhFdehoGv6JtgnXiMxHGAvZbI5kEg051GbyP+XIU6f6YbOu6Q==} + + '@react-pdf/renderer@4.3.2': + resolution: {integrity: sha512-EhPkj35gO9rXIyyx29W3j3axemvVY5RigMmlK4/6Ku0pXB8z9PEE/sz4ZBOShu2uot6V4xiCR3aG+t9IjJJlBQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@react-pdf/stylesheet@6.1.2': + resolution: {integrity: sha512-E3ftGRYUQGKiN3JOgtGsLDo0hGekA6dmkmi/MYACytmPTKxQRBSO3126MebmCq+t1rgU9uRlREIEawJ+8nzSbw==} + + '@react-pdf/textkit@6.1.0': + resolution: {integrity: sha512-sFlzDC9CDFrJsnL3B/+NHrk9+Advqk7iJZIStiYQDdskbow8GF/AGYrpIk+vWSnh35YxaGbHkqXq53XOxnyrjQ==} + + '@react-pdf/types@2.9.2': + resolution: {integrity: sha512-dufvpKId9OajLLbgn9q7VLUmyo1Jf+iyGk2ZHmCL8nIDtL8N1Ejh9TH7+pXXrR0tdie1nmnEb5Bz9U7g4hI4/g==} + '@react-stately/flags@3.1.2': resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} @@ -2457,6 +2629,15 @@ packages: '@rolldown/pluginutils@1.0.0-rc.2': resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.57.1': resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} cpu: [arm] @@ -2939,6 +3120,15 @@ packages: peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x + '@sideway/address@4.1.5': + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} + + '@sideway/formula@3.0.1': + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + + '@sideway/pinpoint@2.0.0': + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + '@sinclair/typebox@0.34.48': resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} @@ -2951,6 +3141,9 @@ packages: '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@starefossen/sanity-plugin-inline-svg-input@1.3.7': resolution: {integrity: sha512-ByKszE7wrbjjvW3iHDmPLGjrTu7YnGtfkBn4osEGinDpaHifQMPut9EfGj408R3g46Kq/Um6DYH12u3ra2LciQ==} engines: {node: '>=18'} @@ -2961,6 +3154,90 @@ packages: sanity: ^3 || ^4 styled-components: ^5 || ^6 + '@storybook/addon-docs@10.2.8': + resolution: {integrity: sha512-cEoWqQrLzrxOwZFee5zrD4cYrdEWKV80POb7jUZO0r5vfl2DuslIr3n/+RfLT52runCV4aZcFEfOfP/IWHNPxg==} + peerDependencies: + storybook: ^10.2.8 + + '@storybook/builder-vite@10.2.8': + resolution: {integrity: sha512-+6/Lwi7W0YIbzHDh798GPp0IHUYDwp0yv0Y1eVNK/StZD0tnv4/1C28NKyP+O7JOsFsuWI1qHiDhw8kNURugZw==} + peerDependencies: + storybook: ^10.2.8 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@storybook/csf-plugin@10.2.8': + resolution: {integrity: sha512-kKkLYhRXb33YtIPdavD2DU25sb14sqPYdcQFpyqu4TaD9truPPqW8P5PLTUgERydt/eRvRlnhauPHavU1kjsnA==} + peerDependencies: + esbuild: '*' + rollup: '*' + storybook: ^10.2.8 + vite: '*' + webpack: '*' + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + + '@storybook/global@5.0.0': + resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} + + '@storybook/icons@2.0.1': + resolution: {integrity: sha512-/smVjw88yK3CKsiuR71vNgWQ9+NuY2L+e8X7IMrFjexjm6ZR8ULrV2DRkTA61aV6ryefslzHEGDInGpnNeIocg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@storybook/nextjs-vite@10.2.8': + resolution: {integrity: sha512-Zc4s5IYh/pfCV8amym3ghF5ITIw5P/eVKFVXruR4ZLKRH3sXuG4O98ityehKnHhp8S5cnV5InGIeacGhXTV++w==} + peerDependencies: + next: ^14.1.0 || ^15.0.0 || ^16.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.2.8 + typescript: '*' + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/react-dom-shim@10.2.8': + resolution: {integrity: sha512-Xde9X3VszFV1pTXfc2ZFM89XOCGRxJD8MUIzDwkcT9xaki5a+8srs/fsXj75fMY6gMYfcL5lNRZvCqg37HOmcQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.2.8 + + '@storybook/react-vite@10.2.8': + resolution: {integrity: sha512-x5kmw+TPhxkQV84n4e9X0q6/rA5T8V2QQFolMuN+U93q1HX1r+GZ6g/nXaaq9ox168PhHUJZQnn+LzSQKGCMBA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.2.8 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@storybook/react@10.2.8': + resolution: {integrity: sha512-nMFqQFUXq6Zg2O5SeuomyWnrIx61QfpNQMrfor8eCEzHrWNnXrrvVsz2RnHIgXN8RVyaWGDPh1srAECu/kDHXw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.2.8 + typescript: '>= 4.9.x' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/test-runner@0.24.2': + resolution: {integrity: sha512-76DbflDTGAKq8Af6uHbWTGnKzKHhjLbJaZXRFhVnKqFocoXcej58C9DpM0BJ3addu7fSDJmPwfR97OINg16XFQ==} + engines: {node: '>=20.0.0'} + hasBin: true + peerDependencies: + storybook: ^0.0.0-0 || ^10.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 + '@svgdotjs/svg.draggable.js@3.0.6': resolution: {integrity: sha512-7iJFm9lL3C40HQcqzEfezK2l+dW2CpoVY3b77KQGqc8GXWa6LhhmX5Ckv7alQfUXBuZbjpICZ+Dvq1czlGx7gA==} peerDependencies: @@ -3068,6 +3345,12 @@ packages: '@swc/helpers@0.5.18': resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} + '@swc/jest@0.2.39': + resolution: {integrity: sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA==} + engines: {npm: '>= 7.0.0'} + peerDependencies: + '@swc/core': '*' + '@swc/types@0.1.25': resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} @@ -3228,6 +3511,12 @@ packages: '@types/react-dom': optional: true + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@trpc/client@11.9.0': resolution: {integrity: sha512-3r4RT/GbR263QO+2gCPyrs5fEYaXua3/AzCs+GbWC09X0F+mVkyBpO3GRSDObiNU/N1YB597U7WGW3WA1d1TVw==} peerDependencies: @@ -3285,6 +3574,9 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -3297,6 +3589,12 @@ packages: '@types/cookies@0.9.2': resolution: {integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/doctrine@0.0.9': + resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} + '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} @@ -3357,6 +3655,9 @@ packages: '@types/koa@2.15.0': resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -3392,6 +3693,9 @@ packages: '@types/react@19.2.13': resolution: {integrity: sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==} + '@types/resolve@1.20.6': + resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} + '@types/send@0.17.6': resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} @@ -3410,6 +3714,9 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@types/stylis@4.2.7': resolution: {integrity: sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==} @@ -3434,6 +3741,9 @@ packages: '@types/uuid@8.3.4': resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} + '@types/wait-on@5.3.4': + resolution: {integrity: sha512-EBsPjFMrFlMbbUFf9D1Fp+PAB2TwmUn7a3YtHyD9RLuTIk1jDd8SxXVAoez2Ciy+8Jsceo2MYEYZzJ/DvorOKw==} + '@types/which@3.0.4': resolution: {integrity: sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==} @@ -3690,6 +4000,18 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@workos-inc/authkit-nextjs@2.13.0': resolution: {integrity: sha512-ppxzhfakPumHPPggYSROaAlgxfS7viFMPmWPG76Tp6Rh9G7YqkBSp7xtvMtM6gXOFFMvvEJRcKEta6YHeercTQ==} peerDependencies: @@ -3720,6 +4042,9 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + abs-svg-path@0.1.1: + resolution: {integrity: sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -3746,6 +4071,10 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -3767,6 +4096,10 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3809,6 +4142,10 @@ packages: apexcharts@5.3.6: resolution: {integrity: sha512-sVEPw+J0Gp0IHQabKu8cfdsxlfME0e36Wid7RIaPclGM2OUt+O7O4+6mfAmTUYhy5bDk8cNHzEhPfVtLCIXEJA==} + append-transform@2.0.0: + resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==} + engines: {node: '>=8'} + archiver-utils@5.0.2: resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} engines: {node: '>= 14'} @@ -3817,6 +4154,9 @@ packages: resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} engines: {node: '>= 14'} + archy@1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -3880,9 +4220,17 @@ packages: resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} engines: {node: '>=12.0.0'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -3924,6 +4272,9 @@ packages: resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} engines: {node: '>=4'} + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -3997,6 +4348,10 @@ packages: resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} engines: {node: '>= 0.6.0'} + base64-js@0.0.8: + resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} + engines: {node: '>= 0.4'} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -4004,6 +4359,10 @@ packages: resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + basic-ftp@5.1.0: resolution: {integrity: sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==} engines: {node: '>=10.0.0'} @@ -4038,9 +4397,15 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + browserify-zlib@0.1.4: resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -4069,10 +4434,18 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + caching-transform@4.0.0: + resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} + engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -4123,6 +4496,10 @@ packages: peerDependencies: react: '>=17.0.0' + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4151,6 +4528,10 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -4158,6 +4539,18 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chromatic@12.2.0: + resolution: {integrity: sha512-GswmBW9ZptAoTns1BMyjbm55Z7EsIJnUvYKdQqXIBZIKbGErmpA+p4c0BYA+nzw5B0M+rb3Iqp1IaH8TFwIQew==} + hasBin: true + peerDependencies: + '@chromatic-com/cypress': ^0.*.* || ^1.0.0 + '@chromatic-com/playwright': ^0.*.* || ^1.0.0 + peerDependenciesMeta: + '@chromatic-com/cypress': + optional: true + '@chromatic-com/playwright': + optional: true + ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} @@ -4168,6 +4561,10 @@ packages: classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + clean-stack@3.0.1: resolution: {integrity: sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==} engines: {node: '>=10'} @@ -4206,6 +4603,10 @@ packages: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} engines: {node: '>=6'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -4233,6 +4634,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color2k@2.0.3: resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==} @@ -4243,6 +4647,13 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@3.0.2: + resolution: {integrity: sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==} + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -4256,6 +4667,11 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concurrently@9.2.1: + resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} + engines: {node: '>=18'} + hasBin: true + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -4270,6 +4686,9 @@ packages: console-table-printer@2.15.0: resolution: {integrity: sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -4281,12 +4700,20 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + core-js-compat@3.48.0: resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + corser@2.0.1: + resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} + engines: {node: '>= 0.4.0'} + crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} @@ -4306,6 +4733,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} @@ -4348,6 +4778,10 @@ packages: custom-media-element@1.4.5: resolution: {integrity: sha512-cjrsQufETwxjvwZbYbKBCJNvmQ2++G9AvT45zDi7NXL9k2PdVcs2h0jQz96J6G4TMKRCcEsoJ+QTgQD00Igtjw==} + cwd@0.10.0: + resolution: {integrity: sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA==} + engines: {node: '>=0.8'} + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -4431,6 +4865,10 @@ packages: resolution: {integrity: sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==} engines: {node: '>= 16'} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -4442,6 +4880,18 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + default-require-extensions@3.0.1: + resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} + engines: {node: '>=8'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -4450,6 +4900,10 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -4473,10 +4927,16 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + diff@4.0.4: resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} + diffable-html@4.1.0: + resolution: {integrity: sha512-++kyNek+YBLH8cLXS+iTj/Hiy2s5qkRJEJ8kgu/WHbFrVY2vz9xPFUT+fii2zGF0m1CaojDlQJjkfrCt7YWM1g==} + dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} @@ -4496,18 +4956,31 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-serializer@0.2.2: + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + domelementtype@1.3.1: + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domhandler@2.4.2: + resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} @@ -4515,6 +4988,9 @@ packages: dompurify@3.3.1: resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} + domutils@1.7.0: + resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -4552,6 +5028,9 @@ packages: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -4561,6 +5040,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.0: + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} + encoding-japanese@2.2.0: resolution: {integrity: sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==} engines: {node: '>=8.10.0'} @@ -4572,6 +5055,12 @@ packages: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} + entities@1.1.2: + resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -4580,6 +5069,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -4618,6 +5111,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + esbuild-register@3.6.0: resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} peerDependencies: @@ -4723,6 +5219,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-plugin-storybook@10.2.8: + resolution: {integrity: sha512-BtysXrg1RoYT3DIrCc+svZ0+L3mbWsu7suxTLGrihBY5HfWHkJge+qjlBBR1Nm2ZMslfuFS5K0NUWbWCJRu6kg==} + peerDependencies: + eslint: '>=8' + storybook: ^10.2.8 + eslint-scope@9.1.0: resolution: {integrity: sha512-CkWE42hOJsNj9FJRaoMX9waUFYhqY4jmyLFdAdzZr6VaCg3ynLYx4WnOdkaIifGfH4gsUcBTn4OZbHXkpLD0FQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -4774,6 +5276,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -4785,6 +5290,9 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} @@ -4822,6 +5330,18 @@ packages: resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} engines: {node: '>= 0.8.0'} + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expand-tilde@1.2.2: + resolution: {integrity: sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==} + engines: {node: '>=0.10.0'} + + expect-playwright@0.8.0: + resolution: {integrity: sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg==} + deprecated: ⚠️ The 'expect-playwright' package is deprecated. The Playwright core assertions (via @playwright/test) now cover the same functionality. Please migrate to built-in expect. See https://playwright.dev/docs/test-assertions for migration. + expect@30.2.0: resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4892,6 +5412,22 @@ packages: resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} engines: {node: '>=6'} + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-file-up@0.1.3: + resolution: {integrity: sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A==} + engines: {node: '>=0.10.0'} + + find-pkg@0.1.2: + resolution: {integrity: sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw==} + engines: {node: '>=0.10.0'} + + find-process@1.4.11: + resolution: {integrity: sha512-mAOh9gGk9WZ4ip5UjV0o6Vb4SrfnAmtsFNzkMRH9HQiFXVQnDyQFrSHTK5UoG6E+KV+s+cIznbtwpfN41l2nFA==} + hasBin: true + find-up-simple@1.0.1: resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} engines: {node: '>=18'} @@ -4935,10 +5471,17 @@ packages: debug: optional: true + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + foreground-child@2.0.0: + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} + engines: {node: '>=8.0.0'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -4969,12 +5512,24 @@ packages: react-dom: optional: true + fromentries@1.3.2: + resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-exists-sync@0.1.0: + resolution: {integrity: sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==} + engines: {node: '>=0.10.0'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5063,10 +5618,22 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@13.0.2: + resolution: {integrity: sha512-035InabNu/c1lW0tzPhAgapKctblppqsKKG9ZaNzbr+gXwWMjXoiyGSyB9sArzrjG7jY+zntRq5ZSUYemrnWVQ==} + engines: {node: 20 || >=22} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + global-modules@0.2.3: + resolution: {integrity: sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA==} + engines: {node: '>=0.10.0'} + + global-prefix@0.1.5: + resolution: {integrity: sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw==} + engines: {node: '>=0.10.0'} + globals@16.4.0: resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} engines: {node: '>=18'} @@ -5092,6 +5659,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphql@16.12.0: + resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + groq-js@1.26.0: resolution: {integrity: sha512-1WxWfmeownBbB2UhvFGyLT3yl/NFGF2qUoev+650Or2qDnoyXjvL83lwspUFuG4piWdDRh2iETljXKoDxacH+w==} engines: {node: '>= 14'} @@ -5140,6 +5711,10 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + hasha@5.2.2: + resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} + engines: {node: '>=8'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -5154,6 +5729,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} @@ -5169,12 +5747,26 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} hotscript@1.0.13: resolution: {integrity: sha512-C++tTF1GqkGYecL+2S1wJTfoH6APGAsbb7PAWQ3iVIwgG/EFseAfEVOKFgAFq4yK3+6j1EjUD4UQ9dRJHX/sSQ==} + hsl-to-hex@1.0.0: + resolution: {integrity: sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==} + + hsl-to-rgb-for-reals@1.1.1: + resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==} + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -5197,6 +5789,9 @@ packages: resolution: {integrity: sha512-5mRhTXZhv4B0kIcsn3bFBjol2o8vzP35mhtxdXBGPA3V3gZd6Sa2PIIFbT//DiqAX8UuywlcJit5jRKej4nV4Q==} engines: {node: '>=16.0.0'} + htmlparser2@3.10.1: + resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -5204,9 +5799,18 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http-server@14.1.1: + resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==} + engines: {node: '>=12'} + hasBin: true + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} @@ -5215,6 +5819,9 @@ packages: humanize-list@1.0.1: resolution: {integrity: sha512-4+p3fCRF21oUqxhK0yZ6yaSP/H5/wZumc7q1fH99RkW7Q13aAxDeP78BKjoR+6y+kaHqKF/JWuQhsNuuI2NKtA==} + hyphen@1.14.1: + resolution: {integrity: sha512-kvL8xYl5QMTh+LwohVN72ciOxC0OEV79IPdJSTwEXok9y9QHebXGdFgrED4sWfiax/ODx++CAMk3hMy4XPJPOw==} + i18next@23.16.8: resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} @@ -5241,6 +5848,11 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -5312,6 +5924,9 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.4: + resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -5362,6 +5977,11 @@ packages: engines: {node: '>=8'} hasBin: true + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -5399,6 +6019,11 @@ packages: is-hotkey@0.2.0: resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -5480,6 +6105,9 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -5492,10 +6120,22 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + is-windows@0.2.0: + resolution: {integrity: sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==} + engines: {node: '>=0.10.0'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -5525,14 +6165,30 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} + istanbul-lib-hook@3.0.0: + resolution: {integrity: sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==} + engines: {node: '>=8'} + + istanbul-lib-instrument@4.0.3: + resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} + engines: {node: '>=8'} + istanbul-lib-instrument@6.0.3: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} + istanbul-lib-processinfo@2.0.3: + resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==} + engines: {node: '>=8'} + istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + istanbul-lib-source-maps@5.0.6: resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} @@ -5553,6 +6209,9 @@ packages: engines: {node: '>=10'} hasBin: true + jay-peg@1.1.1: + resolution: {integrity: sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==} + jest-changed-files@30.2.0: resolution: {integrity: sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5615,6 +6274,10 @@ packages: resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-junit@16.0.0: + resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==} + engines: {node: '>=10.12.0'} + jest-leak-detector@30.2.0: resolution: {integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5640,6 +6303,10 @@ packages: jest-resolve: optional: true + jest-process-manager@0.4.0: + resolution: {integrity: sha512-80Y6snDyb0p8GG83pDxGI/kQzwVTkCxc7ep5FPe/F6JYdvRDhwr6RzRmPSP7SEwuLhxo80lBS/NqOdUIbHIfhw==} + deprecated: ⚠️ The 'jest-process-manager' package is deprecated. Please migrate to Playwright's built-in test runner (@playwright/test) which now includes full Jest-style features and parallel testing. See https://playwright.dev/docs/intro for details. + jest-regex-util@30.0.1: resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5660,6 +6327,9 @@ packages: resolution: {integrity: sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-serializer-html@7.1.0: + resolution: {integrity: sha512-xYL2qC7kmoYHJo8MYqJkzrl/Fdlx+fat4U1AqYg+kafqwcKPiMkOcjWHPKhueuNEgr+uemhGc+jqXYiwCyRyLA==} + jest-snapshot@30.2.0: resolution: {integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5672,6 +6342,12 @@ packages: resolution: {integrity: sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-watch-typeahead@3.0.1: + resolution: {integrity: sha512-SFmHcvdueTswZlVhPCWfLXMazvwZlA2UZTrcE7MC3NwEVeWvEcOx6HUe+igMbnmA6qowuBSW4in8iC6J2EYsgQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + jest: ^30.0.0 + jest-watcher@30.2.0: resolution: {integrity: sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5694,6 +6370,13 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + joi@17.13.3: + resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} + + joi@18.0.2: + resolution: {integrity: sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==} + engines: {node: '>= 20'} + jose@5.10.0: resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} @@ -5817,6 +6500,10 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + knip@5.83.1: resolution: {integrity: sha512-av3ZG/Nui6S/BNL8Tmj12yGxYfTnwWnslouW97m40him7o8MwiMjZBY9TPvlEWUci45aVId0/HbgTwSKIDGpMw==} engines: {node: '>=18.18.0'} @@ -5945,6 +6632,9 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} + linebreak@1.1.0: + resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -5977,6 +6667,9 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.flattendeep@4.4.0: + resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} + lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -5991,10 +6684,17 @@ packages: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -6063,6 +6763,9 @@ packages: media-chrome@4.17.2: resolution: {integrity: sha512-o/IgiHx0tdSVwRxxqF5H12FK31A/A8T71sv3KdAvh7b6XeBS9dXwqvIFwlR9kdEuqg3n7xpmRIuL83rmYq8FTg==} + media-engine@1.0.3: + resolution: {integrity: sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==} + media-tracks@0.3.4: resolution: {integrity: sha512-5SUElzGMYXA7bcyZBL1YzLTxH9Iyw1AeYNJxzByqbestrrtB0F3wfiWUr7aROpwodO4fwnxOt78Xjb3o3ONNQg==} @@ -6097,6 +6800,11 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -6142,6 +6850,14 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + module-alias@2.3.4: + resolution: {integrity: sha512-bOclZt8hkpuGgSSoG07PKmvzTizROilUTvLNyrMqvlC9snhs7y7GzjNWAVbISIOlhCP1T14rH1PDAV9iNyBq/w==} + motion-dom@12.33.0: resolution: {integrity: sha512-XRPebVypsl0UM+7v0Hr8o9UAj0S2djsQWRdHBd5iVouVpMrQqAI0C/rDAT3QaYnXnHuC5hMcwDHCboNeyYjPoQ==} @@ -6165,6 +6881,25 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw-storybook-addon@2.0.6: + resolution: {integrity: sha512-ExCwDbcJoM2V3iQU+fZNp+axVfNc7DWMRh4lyTXebDO8IbpUNYKGFUrA8UqaeWiRGKVuS7+fU+KXEa9b0OP6uA==} + peerDependencies: + msw: ^2.0.0 + + msw@2.12.10: + resolution: {integrity: sha512-G3VUymSE0/iegFnuipujpwyTM2GuZAKXNeerUSrG2+Eg391wW63xFs5ixWsK9MWzr1AGoSkYGmyAzNgbR3+urw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@3.0.0: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} @@ -6264,6 +6999,10 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + node-preload@0.2.1: + resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} + engines: {node: '>=8'} + node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -6278,6 +7017,9 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-svg-path@1.1.0: + resolution: {integrity: sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==} + npm-run-path@3.1.0: resolution: {integrity: sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==} engines: {node: '>=8'} @@ -6292,6 +7034,11 @@ packages: nwsapi@2.2.23: resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + nyc@15.1.0: + resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==} + engines: {node: '>=8.9'} + hasBin: true + oauth4webapi@3.8.4: resolution: {integrity: sha512-EKlVEgav8zH31IXxvhCqjEgQws6S9QmnmJyLXmeV5REf59g7VmqRVa5l/rhGWtUqGm2rLVTNwukn9hla5kJ2WQ==} @@ -6348,10 +7095,18 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -6360,6 +7115,10 @@ packages: resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} engines: {node: '>=20'} + os-homedir@1.0.2: + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} + outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} @@ -6402,6 +7161,10 @@ packages: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-map@3.0.0: + resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} + engines: {node: '>=8'} + p-map@7.0.4: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} @@ -6418,12 +7181,19 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-hash@4.0.0: + resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==} + engines: {node: '>=8'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} pako@0.2.9: resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -6443,6 +7213,13 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} + parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + + parse-svg-path@0.1.2: + resolution: {integrity: sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -6475,6 +7252,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -6485,6 +7266,10 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -6531,6 +7316,16 @@ packages: player.style@0.3.1: resolution: {integrity: sha512-z/T8hJGaTkHT9vdXgWdOgF37eB1FV7/j52VXQZ2lgEhpru9oT8TaUWIxp6GoxTnhPBM4X6nSbpkAHrT7UTjUKg==} + playwright-core@1.58.2: + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.58.2: + resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} + engines: {node: '>=18'} + hasBin: true + pluralize-esm@9.0.5: resolution: {integrity: sha512-Kb2dcpMsIutFw2hYrN0EhsAXOUJTd6FVMIxvNAkZCMQLVt9NGZqQczvGpYDxNWCZeCWLHUPxQIBudWzt1h7VVA==} engines: {node: '>=14.0.0'} @@ -6543,6 +7338,10 @@ packages: resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} engines: {node: '>=10'} + portfinder@1.0.38: + resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} + engines: {node: '>= 10.12'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -6657,10 +7456,18 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-on-spawn@1.1.0: + resolution: {integrity: sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==} + engines: {node: '>=8'} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -6674,6 +7481,9 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pump@2.0.1: resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} @@ -6720,6 +7530,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue@6.0.2: + resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -6755,6 +7568,15 @@ packages: peerDependencies: react: ^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental + react-docgen-typescript@2.4.0: + resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==} + peerDependencies: + typescript: '>= 4.3.x' + + react-docgen@8.0.2: + resolution: {integrity: sha512-+NRMYs2DyTP4/tqWz371Oo50JqmWltR1h2gcdgUMAWZJIAvrd0/SqlCfx7tpzpl/s36rzw6qH2MjoNrxtRNYhA==} + engines: {node: ^20.9.0 || >=22} + react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: @@ -6852,6 +7674,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -6896,6 +7722,10 @@ packages: resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true + release-zalgo@1.0.0: + resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==} + engines: {node: '>=4'} + remeda@2.33.5: resolution: {integrity: sha512-FqmpPA9i9T5EGcqgyHf9kHjefnyCZM1M3kSdZjPk1j2StGNoJyoYp0807RYcjNkQ1UpsEQa5qzgsjLY4vYtT8g==} @@ -6910,6 +7740,9 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + reselect@5.1.1: resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} @@ -6926,6 +7759,10 @@ packages: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} + resolve-dir@0.1.1: + resolution: {integrity: sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -6950,14 +7787,25 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} + rettime@0.10.1: + resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@5.0.10: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true @@ -6970,6 +7818,10 @@ packages: rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -7021,6 +7873,9 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + scheduler@0.25.0-rc-603e6108-20241029: + resolution: {integrity: sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -7030,6 +7885,9 @@ packages: scrollmirror@1.2.4: resolution: {integrity: sha512-UkEHHOV6j5cE3IsObQRK6vO4twSuhE4vtLD4UmX+doHgrtg2jRwXkz4O6cz0jcoxK5NGU7rFjyvLcWHzw7eQ5A==} + secure-compare@3.0.1: + resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + selderee@0.11.0: resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} @@ -7092,6 +7950,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -7115,13 +7977,23 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-swizzle@0.2.4: + resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} + simple-wcswidth@1.1.2: resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + slate-dom@0.119.0: resolution: {integrity: sha512-foc8a2NkE+1SldDIYaoqjhVKupt8RSuvHI868rfYOcypD4we5TT7qunjRKJ852EIRh/Ql8sSTepXgXKOUJnt1w==} peerDependencies: @@ -7159,6 +8031,13 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spawn-wrap@2.0.0: + resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==} + engines: {node: '>=8'} + + spawnd@5.0.0: + resolution: {integrity: sha512-28+AJr82moMVWolQvlAIv3JcYDkjkFTEmfDc503wxrF5l2rQ3dFz6DpbXp3kD4zmgGGldfM4xM4v1sFj/ZaIOA==} + spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -7192,6 +8071,10 @@ packages: standardwebhooks@1.0.0: resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + stdin-discarder@0.3.1: resolution: {integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==} engines: {node: '>=18'} @@ -7200,6 +8083,15 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + storybook@10.2.8: + resolution: {integrity: sha512-885uSIn8NQw2ZG7vy84K45lHCOSyz1DVsDV8pHiHQj3J0riCuWLNeO50lK9z98zE8kjhgTtxAAkMTy5nkmNRKQ==} + hasBin: true + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} @@ -7213,6 +8105,10 @@ packages: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} + string-length@6.0.0: + resolution: {integrity: sha512-1U361pxZHEQ+FeSjzqRpV+cu2vTzYeWeafXFLykiFlv4Vc0n3njgU8HrMbyik5uwm77naWMuVG8fhEF+Ovb1Kg==} + engines: {node: '>=16'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -7282,6 +8178,10 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -7345,6 +8245,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svg-arc-to-cubic-bezier@3.2.0: + resolution: {integrity: sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==} + svix@1.84.1: resolution: {integrity: sha512-K8DPPSZaW/XqXiz1kEyzSHYgmGLnhB43nQCMeKjWGCUpLIpAMMM8kx3rVVOSm6Bo6EHyK1RQLPT4R06skM/MlQ==} @@ -7358,6 +8261,10 @@ packages: tabbable@6.4.0: resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tailwind-merge@3.4.0: resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} @@ -7398,13 +8305,27 @@ packages: through2@4.0.2: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tiny-invariant@1.3.1: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + tlds@1.261.0: resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} hasBin: true @@ -7446,6 +8367,10 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -7455,6 +8380,10 @@ packages: ts-brand@0.2.0: resolution: {integrity: sha512-H5uo7OqMvd91D2EefFmltBP9oeNInNzWLAZUSt6coGDn8b814Eis6SnEtzyXORr9ccEb38PfzyiRVDacdkycSQ==} + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + ts-jest@29.4.6: resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -7552,6 +8481,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.4.4: + resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} + engines: {node: '>=20'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -7627,14 +8560,24 @@ packages: resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} engines: {node: '>=4'} + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + unicode-property-aliases-ecmascript@2.2.0: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + union@0.5.0: + resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} + engines: {node: '>= 0.8.0'} + unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -7651,9 +8594,16 @@ packages: universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -7663,6 +8613,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + urlpattern-polyfill@10.1.0: resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} @@ -7759,11 +8712,30 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + vite-compatible-readable-stream@3.6.1: + resolution: {integrity: sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==} + engines: {node: '>= 6'} + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + vite-plugin-storybook-nextjs@3.1.12: + resolution: {integrity: sha512-/9qKlYWHVXz6AnQBngxD+v26fbdfVaTXeQqVQOvWocOE03FV+sdkwVsallJbmb8iMrkO36G7iLd2T1J0BXUIVQ==} + peerDependencies: + next: ^14.1.0 || ^15.0.0 || ^16.0.0 + storybook: ^0.0.0-0 || ^9.0.0 || ^10.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + vite-tsconfig-paths@6.1.0: resolution: {integrity: sha512-kpd3sY9glHIDaq4V/Tlc1Y8WaKtutoc3B525GHxEVKWX42FKfQsXvjFOemu1I8VIN8pNbrMLWVTbW79JaRUxKg==} peerDependencies: @@ -7820,6 +8792,21 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + wait-on@7.2.0: + resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==} + engines: {node: '>=12.0.0'} + hasBin: true + + wait-on@8.0.5: + resolution: {integrity: sha512-J3WlS0txVHkhLRb2FsmRg3dkMTCV1+M6Xra3Ho7HzZDHpE7DCOnoSoCJsZotrmW3uRMhvIJGSKUKrh/MeF4iag==} + engines: {node: '>=12.0.0'} + hasBin: true + + wait-port@0.2.14: + resolution: {integrity: sha512-kIzjWcr6ykl7WFbZd0TMae8xovwqcqbx6FM9l+7agOgUByhzdjfzZBPK2CPufldTOMxbUivss//Sh9MFawmPRQ==} + engines: {node: '>=8'} + hasBin: true + walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -7841,6 +8828,14 @@ packages: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -7892,6 +8887,10 @@ packages: resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} engines: {node: '>= 0.4'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -7955,6 +8954,10 @@ packages: utf-8-validate: optional: true + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + xdg-basedir@4.0.0: resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} engines: {node: '>=8'} @@ -7967,6 +8970,9 @@ packages: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} + xml@1.0.1: + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -8023,10 +9029,17 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -9382,6 +10395,28 @@ snapshots: '@floating-ui/utils@0.2.10': {} + '@hapi/address@5.1.1': + dependencies: + '@hapi/hoek': 11.0.7 + + '@hapi/formula@3.0.2': {} + + '@hapi/hoek@11.0.7': {} + + '@hapi/hoek@9.3.0': {} + + '@hapi/pinpoint@2.0.1': {} + + '@hapi/tlds@1.1.5': {} + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + + '@hapi/topo@6.0.2': + dependencies: + '@hapi/hoek': 11.0.7 + '@headlessui/react@2.2.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@floating-ui/react': 0.26.28(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -9508,6 +10543,8 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@inquirer/ansi@1.0.2': {} + '@inquirer/ansi@2.0.3': {} '@inquirer/checkbox@5.0.4(@types/node@25.2.2)': @@ -9519,6 +10556,13 @@ snapshots: optionalDependencies: '@types/node': 25.2.2 + '@inquirer/confirm@5.1.21(@types/node@25.2.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.2.2) + '@inquirer/type': 3.0.10(@types/node@25.2.2) + optionalDependencies: + '@types/node': 25.2.2 + '@inquirer/confirm@6.0.4(@types/node@25.2.2)': dependencies: '@inquirer/core': 11.1.1(@types/node@25.2.2) @@ -9526,6 +10570,19 @@ snapshots: optionalDependencies: '@types/node': 25.2.2 + '@inquirer/core@10.3.2(@types/node@25.2.2)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@25.2.2) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 25.2.2 + '@inquirer/core@11.1.1(@types/node@25.2.2)': dependencies: '@inquirer/ansi': 2.0.3 @@ -9560,6 +10617,8 @@ snapshots: optionalDependencies: '@types/node': 25.2.2 + '@inquirer/figures@1.0.15': {} + '@inquirer/figures@2.0.3': {} '@inquirer/input@5.0.4(@types/node@25.2.2)': @@ -9623,6 +10682,10 @@ snapshots: optionalDependencies: '@types/node': 25.2.2 + '@inquirer/type@3.0.10(@types/node@25.2.2)': + optionalDependencies: + '@types/node': 25.2.2 + '@inquirer/type@4.0.3(@types/node@25.2.2)': optionalDependencies: '@types/node': 25.2.2 @@ -9699,6 +10762,10 @@ snapshots: - supports-color - ts-node + '@jest/create-cache-key-function@30.2.0': + dependencies: + '@jest/types': 30.2.0 + '@jest/diff-sequences@30.0.1': {} '@jest/environment-jsdom-abstract@30.2.0(jsdom@26.1.0)': @@ -9844,6 +10911,14 @@ snapshots: '@types/yargs': 17.0.35 chalk: 4.1.2 + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + glob: 13.0.2 + react-docgen-typescript: 2.4.0(typescript@5.9.3) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + optionalDependencies: + typescript: 5.9.3 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -9888,6 +10963,12 @@ snapshots: '@marijn/find-cluster-break@1.0.2': {} + '@mdx-js/react@3.1.1(@types/react@19.2.13)(react@19.2.4)': + dependencies: + '@types/mdx': 2.0.13 + '@types/react': 19.2.13 + react: 19.2.4 + '@mswjs/interceptors@0.39.8': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -9897,6 +10978,15 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 + '@mswjs/interceptors@0.41.2': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + '@mux/mux-data-google-ima@0.2.8': dependencies: mux-embed: 5.9.0 @@ -9948,6 +11038,8 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@next/env@16.0.0': {} + '@next/env@16.1.6': {} '@next/eslint-plugin-next@16.1.6': @@ -10344,32 +11436,141 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@react-stately/flags@3.1.2': - dependencies: - '@swc/helpers': 0.5.18 + '@react-pdf/fns@3.1.2': {} - '@react-stately/utils@3.11.0(react@19.2.4)': + '@react-pdf/font@4.0.4': dependencies: - '@swc/helpers': 0.5.18 - react: 19.2.4 + '@react-pdf/pdfkit': 4.1.0 + '@react-pdf/types': 2.9.2 + fontkit: 2.0.4 + is-url: 1.2.4 - '@react-types/shared@3.33.0(react@19.2.4)': + '@react-pdf/image@3.0.4': dependencies: - react: 19.2.4 + '@react-pdf/png-js': 3.0.0 + jay-peg: 1.1.1 - '@rexxars/react-json-inspector@9.0.1(react@19.2.4)': + '@react-pdf/layout@4.4.2': dependencies: - debounce: 1.2.1 - md5-o-matic: 0.1.1 - react: 19.2.4 + '@react-pdf/fns': 3.1.2 + '@react-pdf/image': 3.0.4 + '@react-pdf/primitives': 4.1.1 + '@react-pdf/stylesheet': 6.1.2 + '@react-pdf/textkit': 6.1.0 + '@react-pdf/types': 2.9.2 + emoji-regex-xs: 1.0.0 + queue: 6.0.2 + yoga-layout: 3.2.1 - '@rexxars/react-split-pane@1.0.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@react-pdf/pdfkit@4.1.0': + dependencies: + '@babel/runtime': 7.28.6 + '@react-pdf/png-js': 3.0.0 + browserify-zlib: 0.2.0 + crypto-js: 4.2.0 + fontkit: 2.0.4 + jay-peg: 1.1.1 + linebreak: 1.1.0 + vite-compatible-readable-stream: 3.6.1 + + '@react-pdf/png-js@3.0.0': + dependencies: + browserify-zlib: 0.2.0 + + '@react-pdf/primitives@4.1.1': {} + + '@react-pdf/reconciler@2.0.0(react@19.2.4)': + dependencies: + object-assign: 4.1.1 + react: 19.2.4 + scheduler: 0.25.0-rc-603e6108-20241029 + + '@react-pdf/render@4.3.2': + dependencies: + '@babel/runtime': 7.28.6 + '@react-pdf/fns': 3.1.2 + '@react-pdf/primitives': 4.1.1 + '@react-pdf/textkit': 6.1.0 + '@react-pdf/types': 2.9.2 + abs-svg-path: 0.1.1 + color-string: 1.9.1 + normalize-svg-path: 1.1.0 + parse-svg-path: 0.1.2 + svg-arc-to-cubic-bezier: 3.2.0 + + '@react-pdf/renderer@4.3.2(react@19.2.4)': + dependencies: + '@babel/runtime': 7.28.6 + '@react-pdf/fns': 3.1.2 + '@react-pdf/font': 4.0.4 + '@react-pdf/layout': 4.4.2 + '@react-pdf/pdfkit': 4.1.0 + '@react-pdf/primitives': 4.1.1 + '@react-pdf/reconciler': 2.0.0(react@19.2.4) + '@react-pdf/render': 4.3.2 + '@react-pdf/types': 2.9.2 + events: 3.3.0 + object-assign: 4.1.1 + prop-types: 15.8.1 + queue: 6.0.2 + react: 19.2.4 + + '@react-pdf/stylesheet@6.1.2': + dependencies: + '@react-pdf/fns': 3.1.2 + '@react-pdf/types': 2.9.2 + color-string: 1.9.1 + hsl-to-hex: 1.0.0 + media-engine: 1.0.3 + postcss-value-parser: 4.2.0 + + '@react-pdf/textkit@6.1.0': + dependencies: + '@react-pdf/fns': 3.1.2 + bidi-js: 1.0.3 + hyphen: 1.14.1 + unicode-properties: 1.4.1 + + '@react-pdf/types@2.9.2': + dependencies: + '@react-pdf/font': 4.0.4 + '@react-pdf/primitives': 4.1.1 + '@react-pdf/stylesheet': 6.1.2 + + '@react-stately/flags@3.1.2': + dependencies: + '@swc/helpers': 0.5.18 + + '@react-stately/utils@3.11.0(react@19.2.4)': + dependencies: + '@swc/helpers': 0.5.18 + react: 19.2.4 + + '@react-types/shared@3.33.0(react@19.2.4)': + dependencies: + react: 19.2.4 + + '@rexxars/react-json-inspector@9.0.1(react@19.2.4)': + dependencies: + debounce: 1.2.1 + md5-o-matic: 0.1.1 + react: 19.2.4 + + '@rexxars/react-split-pane@1.0.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) '@rolldown/pluginutils@1.0.0-rc.2': {} + '@rollup/pluginutils@5.3.0(rollup@4.57.1)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.57.1 + '@rollup/rollup-android-arm-eabi@4.57.1': optional: true @@ -11173,6 +12374,14 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.2.4 + '@sideway/address@4.1.5': + dependencies: + '@hapi/hoek': 9.3.0 + + '@sideway/formula@3.0.1': {} + + '@sideway/pinpoint@2.0.0': {} + '@sinclair/typebox@0.34.48': {} '@sinonjs/commons@3.0.1': @@ -11185,6 +12394,8 @@ snapshots: '@stablelib/base64@1.0.1': {} + '@standard-schema/spec@1.1.0': {} + '@starefossen/sanity-plugin-inline-svg-input@1.3.7(@noble/hashes@2.0.1)(@sanity/ui@3.1.11(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react-is@19.2.4)(react@19.2.4)(styled-components@6.3.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sanity@5.8.1(@emotion/is-prop-valid@1.4.0)(@noble/hashes@2.0.1)(@swc/helpers@0.5.18)(@types/node@25.2.2)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(babel-plugin-react-compiler@1.0.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(styled-components@6.3.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(styled-components@6.3.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': dependencies: '@sanity/incompatible-plugin': 1.0.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -11199,6 +12410,147 @@ snapshots: - canvas - supports-color + '@storybook/addon-docs@10.2.8(@types/react@19.2.13)(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@mdx-js/react': 3.1.1(@types/react@19.2.13)(react@19.2.4) + '@storybook/csf-plugin': 10.2.8(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@storybook/react-dom-shim': 10.2.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@types/react' + - esbuild + - rollup + - vite + - webpack + + '@storybook/builder-vite@10.2.8(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@storybook/csf-plugin': 10.2.8(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + ts-dedent: 2.2.0 + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - esbuild + - rollup + - webpack + + '@storybook/csf-plugin@10.2.8(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + unplugin: 2.3.11 + optionalDependencies: + esbuild: 0.27.3 + rollup: 4.57.1 + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + + '@storybook/global@5.0.0': {} + + '@storybook/icons@2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@storybook/nextjs-vite@10.2.8(@babel/core@7.29.0)(esbuild@0.27.3)(next@16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@storybook/builder-vite': 10.2.8(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@storybook/react': 10.2.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + '@storybook/react-vite': 10.2.8(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + next: 16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite-plugin-storybook-nextjs: 3.1.12(next@16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + - esbuild + - rollup + - supports-color + - webpack + + '@storybook/react-dom-shim@10.2.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@storybook/react-vite@10.2.8(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.4(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@rollup/pluginutils': 5.3.0(rollup@4.57.1) + '@storybook/builder-vite': 10.2.8(esbuild@0.27.3)(rollup@4.57.1)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@storybook/react': 10.2.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + empathic: 2.0.0 + magic-string: 0.30.21 + react: 19.2.4 + react-docgen: 8.0.2 + react-dom: 19.2.4(react@19.2.4) + resolve: 1.22.11 + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + tsconfig-paths: 4.2.0 + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - typescript + - webpack + + '@storybook/react@10.2.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/react-dom-shim': 10.2.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + react: 19.2.4 + react-docgen: 8.0.2 + react-dom: 19.2.4(react@19.2.4) + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@storybook/test-runner@0.24.2(@swc/helpers@0.5.18)(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@jest/types': 30.2.0 + '@swc/core': 1.15.11(@swc/helpers@0.5.18) + '@swc/jest': 0.2.39(@swc/core@1.15.11(@swc/helpers@0.5.18)) + expect-playwright: 0.8.0 + jest: 30.2.0(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3)) + jest-circus: 30.2.0 + jest-environment-node: 30.2.0 + jest-junit: 16.0.0 + jest-process-manager: 0.4.0 + jest-runner: 30.2.0 + jest-serializer-html: 7.1.0 + jest-watch-typeahead: 3.0.1(jest@30.2.0(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3))) + nyc: 15.1.0 + playwright: 1.58.2 + playwright-core: 1.58.2 + rimraf: 3.0.2 + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + uuid: 8.3.2 + transitivePeerDependencies: + - '@swc/helpers' + - '@types/node' + - babel-plugin-macros + - debug + - esbuild-register + - node-notifier + - supports-color + - ts-node + '@svgdotjs/svg.draggable.js@3.0.6(@svgdotjs/svg.js@3.2.5)': dependencies: '@svgdotjs/svg.js': 3.2.5 @@ -11275,6 +12627,13 @@ snapshots: dependencies: tslib: 2.8.1 + '@swc/jest@0.2.39(@swc/core@1.15.11(@swc/helpers@0.5.18))': + dependencies: + '@jest/create-cache-key-function': 30.2.0 + '@swc/core': 1.15.11(@swc/helpers@0.5.18) + '@swc/counter': 0.1.3 + jsonc-parser: 3.3.1 + '@swc/types@0.1.25': dependencies: '@swc/counter': 0.1.3 @@ -11414,6 +12773,10 @@ snapshots: '@types/react': 19.2.13 '@types/react-dom': 19.2.3(@types/react@19.2.13) + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + '@trpc/client@11.9.0(@trpc/server@11.9.0(typescript@5.9.3))(typescript@5.9.3)': dependencies: '@trpc/server': 11.9.0(typescript@5.9.3) @@ -11477,6 +12840,11 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 25.2.2 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/connect@3.4.38': dependencies: '@types/node': 25.2.2 @@ -11492,6 +12860,10 @@ snapshots: '@types/keygrip': 1.0.6 '@types/node': 25.2.2 + '@types/deep-eql@4.0.2': {} + + '@types/doctrine@0.0.9': {} + '@types/esrecurse@4.3.1': {} '@types/estree@1.0.8': {} @@ -11565,6 +12937,8 @@ snapshots: '@types/koa-compose': 3.2.9 '@types/node': 25.2.2 + '@types/mdx@2.0.13': {} + '@types/mime@1.3.5': {} '@types/node@17.0.45': {} @@ -11597,6 +12971,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/resolve@1.20.6': {} + '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 @@ -11618,6 +12994,8 @@ snapshots: '@types/stack-utils@2.0.3': {} + '@types/statuses@2.0.6': {} + '@types/stylis@4.2.7': {} '@types/tar-stream@3.1.4': @@ -11637,6 +13015,10 @@ snapshots: '@types/uuid@8.3.4': {} + '@types/wait-on@5.3.4': + dependencies: + '@types/node': 25.2.2 + '@types/which@3.0.4': {} '@types/yargs-parser@21.0.3': {} @@ -11857,6 +13239,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + '@workos-inc/authkit-nextjs@2.13.0(next@16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@workos-inc/node': 7.82.0(next@16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) @@ -11903,6 +13307,8 @@ snapshots: dependencies: event-target-shim: 5.0.1 + abs-svg-path@0.1.1: {} + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -11921,6 +13327,11 @@ snapshots: agent-base@7.1.4: {} + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -11947,6 +13358,10 @@ snapshots: dependencies: type-fest: 0.21.3 + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -11983,6 +13398,10 @@ snapshots: '@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.5) '@yr/monotone-cubic-spline': 1.0.3 + append-transform@2.0.0: + dependencies: + default-require-extensions: 3.0.1 + archiver-utils@5.0.2: dependencies: glob: 10.5.0 @@ -12006,6 +13425,8 @@ snapshots: - bare-abort-controller - react-native-b4a + archy@1.0.0: {} + arg@4.1.3: {} argparse@1.0.10: @@ -12099,8 +13520,14 @@ snapshots: pvutils: 1.1.5 tslib: 2.8.1 + assertion-error@2.0.1: {} + ast-types-flow@0.0.8: {} + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + async-function@1.0.0: {} async-mutex@0.5.0: @@ -12139,6 +13566,14 @@ snapshots: axe-core@4.11.1: {} + axios@1.13.5: + dependencies: + follow-redirects: 1.15.11(debug@4.4.3) + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axobject-query@4.1.0: {} b4a@1.7.3: {} @@ -12231,10 +13666,16 @@ snapshots: base64-arraybuffer@1.0.2: {} + base64-js@0.0.8: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.9.19: {} + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + basic-ftp@5.1.0: {} before-after-hook@4.0.0: {} @@ -12277,10 +13718,18 @@ snapshots: dependencies: fill-range: 7.1.1 + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + browserify-zlib@0.1.4: dependencies: pako: 0.2.9 + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.19 @@ -12315,8 +13764,19 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + cac@6.7.14: {} + caching-transform@4.0.0: + dependencies: + hasha: 5.2.2 + make-dir: 3.1.0 + package-hash: 4.0.0 + write-file-atomic: 3.0.3 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -12361,6 +13821,14 @@ snapshots: dependencies: react: 19.2.4 + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -12384,6 +13852,8 @@ snapshots: chardet@2.1.1: {} + check-error@2.1.3: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -12398,12 +13868,16 @@ snapshots: chownr@1.1.4: {} + chromatic@12.2.0: {} + ci-info@4.4.0: {} cjs-module-lexer@2.2.0: {} classnames@2.5.1: {} + clean-stack@2.2.0: {} + clean-stack@3.0.1: dependencies: escape-string-regexp: 4.0.0 @@ -12440,6 +13914,8 @@ snapshots: kind-of: 6.0.3 shallow-clone: 3.0.1 + clone@2.1.2: {} + clsx@2.1.1: {} co@4.6.0: {} @@ -12468,6 +13944,11 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.4 + color2k@2.0.3: {} combined-stream@1.0.8: @@ -12476,6 +13957,10 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@12.1.0: {} + + commander@3.0.2: {} + commondir@1.0.1: {} compress-commons@6.0.2: @@ -12490,6 +13975,15 @@ snapshots: concat-map@0.0.1: {} + concurrently@9.2.1: + dependencies: + chalk: 4.1.2 + rxjs: 7.8.2 + shell-quote: 1.8.3 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -12515,18 +14009,24 @@ snapshots: dependencies: simple-wcswidth: 1.1.2 + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} cookie@0.5.0: {} cookie@0.7.2: {} + cookie@1.1.1: {} + core-js-compat@3.48.0: dependencies: browserslist: 4.28.1 core-util-is@1.0.3: {} + corser@2.0.1: {} + crc-32@1.2.2: {} crc32-stream@6.0.0: @@ -12544,6 +14044,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-js@4.2.0: {} + crypto-random-string@2.0.0: {} css-color-keywords@1.0.0: {} @@ -12591,6 +14093,11 @@ snapshots: custom-media-element@1.4.5: {} + cwd@0.10.0: + dependencies: + find-pkg: 0.1.2 + fs-exists-sync: 0.1.0 + damerau-levenshtein@1.0.8: {} data-uri-to-buffer@6.0.2: {} @@ -12662,12 +14169,25 @@ snapshots: deeks@3.1.0: {} + deep-eql@5.0.2: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} deepmerge@4.3.1: {} + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + default-require-extensions@3.0.1: + dependencies: + strip-bom: 4.0.0 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -12676,6 +14196,8 @@ snapshots: define-lazy-prop@2.0.0: {} + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -12692,8 +14214,14 @@ snapshots: detect-node-es@1.1.0: {} + dfa@1.2.0: {} + diff@4.0.4: {} + diffable-html@4.1.0: + dependencies: + htmlparser2: 3.10.1 + dijkstrajs@1.0.3: {} dir-glob@3.0.1: @@ -12708,18 +14236,33 @@ snapshots: dependencies: esutils: 2.0.3 + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} + dom-serializer@0.2.2: + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 + domelementtype@1.3.1: {} + domelementtype@2.3.0: {} + domhandler@2.4.2: + dependencies: + domelementtype: 1.3.1 + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 @@ -12728,6 +14271,11 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 + domutils@1.7.0: + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -12767,12 +14315,16 @@ snapshots: emittery@0.13.1: {} + emoji-regex-xs@1.0.0: {} + emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} + empathic@2.0.0: {} + encoding-japanese@2.2.0: {} end-of-stream@1.4.5: @@ -12784,10 +14336,16 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + entities@1.1.2: {} + + entities@2.2.0: {} + entities@4.5.0: {} entities@6.0.1: {} + environment@1.1.0: {} + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -12895,6 +14453,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es6-error@4.1.1: {} + esbuild-register@3.6.0(esbuild@0.27.2): dependencies: debug: 4.4.3(supports-color@8.1.1) @@ -13108,6 +14668,15 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-plugin-storybook@10.2.8(eslint@10.0.0(jiti@2.6.1))(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3): + dependencies: + '@typescript-eslint/utils': 8.54.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 10.0.0(jiti@2.6.1) + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - supports-color + - typescript + eslint-scope@9.1.0: dependencies: '@types/esrecurse': 4.3.1 @@ -13180,12 +14749,16 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} event-source-polyfill@1.0.31: {} event-target-shim@5.0.1: {} + eventemitter3@4.0.7: {} + eventemitter3@5.0.4: {} events-universal@1.0.1: @@ -13232,6 +14805,14 @@ snapshots: exit-x@0.2.2: {} + exit@0.1.2: {} + + expand-tilde@1.2.2: + dependencies: + os-homedir: 1.0.2 + + expect-playwright@0.8.0: {} + expect@30.2.0: dependencies: '@jest/expect-utils': 30.2.0 @@ -13309,6 +14890,27 @@ snapshots: make-dir: 2.1.0 pkg-dir: 3.0.0 + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-file-up@0.1.3: + dependencies: + fs-exists-sync: 0.1.0 + resolve-dir: 0.1.1 + + find-pkg@0.1.2: + dependencies: + find-file-up: 0.1.3 + + find-process@1.4.11: + dependencies: + chalk: 4.1.2 + commander: 12.1.0 + loglevel: 1.9.2 + find-up-simple@1.0.1: {} find-up@3.0.0: @@ -13350,10 +14952,27 @@ snapshots: optionalDependencies: debug: 4.4.3(supports-color@8.1.1) + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.18 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + for-each@0.3.5: dependencies: is-callable: 1.2.7 + foreground-child@2.0.0: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 3.0.7 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -13383,10 +15002,17 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + fromentries@1.3.2: {} + fs-constants@1.0.0: {} + fs-exists-sync@0.1.0: {} + fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -13494,6 +15120,12 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@13.0.2: + dependencies: + minimatch: 10.1.2 + minipass: 7.1.2 + path-scurry: 2.0.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -13503,6 +15135,18 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + global-modules@0.2.3: + dependencies: + global-prefix: 0.1.5 + is-windows: 0.2.0 + + global-prefix@0.1.5: + dependencies: + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 0.2.0 + which: 1.3.1 + globals@16.4.0: {} globalthis@1.0.4: @@ -13527,6 +15171,8 @@ snapshots: graceful-fs@4.2.11: {} + graphql@16.12.0: {} + groq-js@1.26.0: dependencies: debug: 4.4.3(supports-color@8.1.1) @@ -13575,6 +15221,11 @@ snapshots: dependencies: has-symbols: 1.1.0 + hasha@5.2.2: + dependencies: + is-stream: 2.0.1 + type-fest: 0.8.1 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -13593,6 +15244,8 @@ snapshots: he@1.2.0: {} + headers-polyfill@4.0.3: {} + hermes-estree@0.25.1: {} hermes-parser@0.25.1: @@ -13609,10 +15262,24 @@ snapshots: dependencies: react-is: 16.13.1 + homedir-polyfill@1.0.3: + dependencies: + parse-passwd: 1.0.0 + hosted-git-info@2.8.9: {} hotscript@1.0.13: {} + hsl-to-hex@1.0.0: + dependencies: + hsl-to-rgb-for-reals: 1.1.1 + + hsl-to-rgb-for-reals@1.1.1: {} + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -13642,6 +15309,15 @@ snapshots: css-line-break: 2.1.0 text-segmentation: 1.0.3 + htmlparser2@3.10.1: + dependencies: + domelementtype: 1.3.1 + domhandler: 2.4.2 + domutils: 1.7.0 + entities: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -13656,6 +15332,33 @@ snapshots: transitivePeerDependencies: - supports-color + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.11(debug@4.4.3) + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http-server@14.1.1: + dependencies: + basic-auth: 2.0.1 + chalk: 4.1.2 + corser: 2.0.1 + he: 1.2.0 + html-encoding-sniffer: 3.0.0 + http-proxy: 1.18.1 + mime: 1.6.0 + minimist: 1.2.8 + opener: 1.5.2 + portfinder: 1.0.38 + secure-compare: 3.0.1 + union: 0.5.0 + url-join: 4.0.1 + transitivePeerDependencies: + - debug + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -13667,6 +15370,8 @@ snapshots: humanize-list@1.0.1: {} + hyphen@1.14.1: {} + i18next@23.16.8: dependencies: '@babel/runtime': 7.28.6 @@ -13689,6 +15394,8 @@ snapshots: ignore@7.0.5: {} + image-size@2.0.2: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -13759,6 +15466,8 @@ snapshots: is-arrayish@0.2.1: {} + is-arrayish@0.3.4: {} + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -13809,6 +15518,8 @@ snapshots: is-docker@2.2.1: {} + is-docker@3.0.0: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -13839,6 +15550,10 @@ snapshots: is-hotkey@0.2.0: {} + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + is-interactive@2.0.0: {} is-map@2.0.3: {} @@ -13902,6 +15617,8 @@ snapshots: is-unicode-supported@2.1.0: {} + is-url@1.2.4: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -13913,10 +15630,18 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-windows@0.2.0: {} + + is-windows@1.0.2: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + isarray@1.0.0: {} isarray@2.0.5: {} @@ -13948,6 +15673,19 @@ snapshots: istanbul-lib-coverage@3.2.2: {} + istanbul-lib-hook@3.0.0: + dependencies: + append-transform: 2.0.0 + + istanbul-lib-instrument@4.0.3: + dependencies: + '@babel/core': 7.29.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 @@ -13958,12 +15696,29 @@ snapshots: transitivePeerDependencies: - supports-color + istanbul-lib-processinfo@2.0.3: + dependencies: + archy: 1.0.0 + cross-spawn: 7.0.6 + istanbul-lib-coverage: 3.2.2 + p-map: 3.0.0 + rimraf: 3.0.2 + uuid: 8.3.2 + istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -13998,6 +15753,10 @@ snapshots: filelist: 1.0.4 picocolors: 1.1.1 + jay-peg@1.1.1: + dependencies: + restructure: 3.0.2 + jest-changed-files@30.2.0: dependencies: execa: 5.1.1 @@ -14139,6 +15898,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + jest-junit@16.0.0: + dependencies: + mkdirp: 1.0.4 + strip-ansi: 6.0.1 + uuid: 8.3.2 + xml: 1.0.1 + jest-leak-detector@30.2.0: dependencies: '@jest/get-type': 30.1.0 @@ -14173,6 +15939,22 @@ snapshots: optionalDependencies: jest-resolve: 30.2.0 + jest-process-manager@0.4.0: + dependencies: + '@types/wait-on': 5.3.4 + chalk: 4.1.2 + cwd: 0.10.0 + exit: 0.1.2 + find-process: 1.4.11 + prompts: 2.4.2 + signal-exit: 3.0.7 + spawnd: 5.0.0 + tree-kill: 1.2.2 + wait-on: 7.2.0 + transitivePeerDependencies: + - debug + - supports-color + jest-regex-util@30.0.1: {} jest-resolve-dependencies@30.2.0: @@ -14247,6 +16029,10 @@ snapshots: transitivePeerDependencies: - supports-color + jest-serializer-html@7.1.0: + dependencies: + diffable-html: 4.1.0 + jest-snapshot@30.2.0: dependencies: '@babel/core': 7.29.0 @@ -14291,6 +16077,17 @@ snapshots: leven: 3.1.0 pretty-format: 30.2.0 + jest-watch-typeahead@3.0.1(jest@30.2.0(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3))): + dependencies: + ansi-escapes: 7.3.0 + chalk: 5.6.2 + jest: 30.2.0(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3)) + jest-regex-util: 30.0.1 + jest-watcher: 30.2.0 + slash: 5.1.0 + string-length: 6.0.0 + strip-ansi: 7.1.2 + jest-watcher@30.2.0: dependencies: '@jest/test-result': 30.2.0 @@ -14325,6 +16122,24 @@ snapshots: jiti@2.6.1: {} + joi@17.13.3: + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + + joi@18.0.2: + dependencies: + '@hapi/address': 5.1.1 + '@hapi/formula': 3.0.2 + '@hapi/hoek': 11.0.7 + '@hapi/pinpoint': 2.0.1 + '@hapi/tlds': 1.1.5 + '@hapi/topo': 6.0.2 + '@standard-schema/spec': 1.1.0 + jose@5.10.0: {} jose@5.6.3: {} @@ -14490,6 +16305,8 @@ snapshots: kind-of@6.0.3: {} + kleur@3.0.3: {} + knip@5.83.1(@types/node@25.2.2)(typescript@5.9.3): dependencies: '@nodelib/fs.walk': 1.2.8 @@ -14594,6 +16411,11 @@ snapshots: lilconfig@3.1.3: {} + linebreak@1.1.0: + dependencies: + base64-js: 0.0.8 + unicode-trie: 2.0.0 + lines-and-columns@1.2.4: {} linkify-it@5.0.0: @@ -14628,6 +16450,8 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.flattendeep@4.4.0: {} + lodash.memoize@4.1.2: {} lodash@4.17.23: {} @@ -14641,10 +16465,14 @@ snapshots: is-unicode-supported: 2.1.0 yoctocolors: 2.1.2 + loglevel@1.9.2: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 + loupe@3.2.1: {} + lru-cache@10.4.3: {} lru-cache@11.2.5: {} @@ -14731,6 +16559,8 @@ snapshots: transitivePeerDependencies: - react + media-engine@1.0.3: {} + media-tracks@0.3.4: {} mendoza@3.0.8: {} @@ -14756,6 +16586,8 @@ snapshots: dependencies: mime-db: 1.54.0 + mime@1.6.0: {} + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -14788,6 +16620,10 @@ snapshots: mkdirp-classic@0.5.3: {} + mkdirp@1.0.4: {} + + module-alias@2.3.4: {} + motion-dom@12.33.0: dependencies: motion-utils: 12.29.2 @@ -14805,6 +16641,38 @@ snapshots: ms@2.1.3: {} + msw-storybook-addon@2.0.6(msw@2.12.10(@types/node@25.2.2)(typescript@5.9.3)): + dependencies: + is-node-process: 1.2.0 + msw: 2.12.10(@types/node@25.2.2)(typescript@5.9.3) + + msw@2.12.10(@types/node@25.2.2)(typescript@5.9.3): + dependencies: + '@inquirer/confirm': 5.1.21(@types/node@25.2.2) + '@mswjs/interceptors': 0.41.2 + '@open-draft/deferred-promise': 2.2.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.12.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.10.1 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.0 + type-fest: 5.4.4 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + + mute-stream@2.0.0: {} + mute-stream@3.0.0: {} mux-embed@5.16.1: {} @@ -14900,6 +16768,10 @@ snapshots: node-int64@0.4.0: {} + node-preload@0.2.1: + dependencies: + process-on-spawn: 1.1.0 + node-releases@2.0.27: {} nodemailer@7.0.11: {} @@ -14913,6 +16785,10 @@ snapshots: normalize-path@3.0.0: {} + normalize-svg-path@1.1.0: + dependencies: + svg-arc-to-cubic-bezier: 3.2.0 + npm-run-path@3.1.0: dependencies: path-key: 3.1.1 @@ -14927,6 +16803,38 @@ snapshots: nwsapi@2.2.23: {} + nyc@15.1.0: + dependencies: + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + caching-transform: 4.0.0 + convert-source-map: 1.9.0 + decamelize: 1.2.0 + find-cache-dir: 3.3.2 + find-up: 4.1.0 + foreground-child: 2.0.0 + get-package-type: 0.1.0 + glob: 7.2.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-hook: 3.0.0 + istanbul-lib-instrument: 4.0.3 + istanbul-lib-processinfo: 2.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + make-dir: 3.1.0 + node-preload: 0.2.1 + p-map: 3.0.0 + process-on-spawn: 1.1.0 + resolve-from: 5.0.0 + rimraf: 3.0.2 + signal-exit: 3.0.7 + spawn-wrap: 2.0.0 + test-exclude: 6.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - supports-color + oauth4webapi@3.8.4: {} object-assign@4.1.1: {} @@ -14989,12 +16897,21 @@ snapshots: dependencies: mimic-function: 5.0.1 + open@10.2.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 is-docker: 2.2.1 is-wsl: 2.2.0 + opener@1.5.2: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -15015,6 +16932,8 @@ snapshots: stdin-discarder: 0.3.1 string-width: 8.1.1 + os-homedir@1.0.2: {} + outvariant@1.4.3: {} own-keys@1.0.1: @@ -15076,6 +16995,10 @@ snapshots: dependencies: p-limit: 4.0.0 + p-map@3.0.0: + dependencies: + aggregate-error: 3.1.0 + p-map@7.0.4: {} p-queue@9.1.0: @@ -15087,10 +17010,19 @@ snapshots: p-try@2.2.0: {} + package-hash@4.0.0: + dependencies: + graceful-fs: 4.2.11 + hasha: 5.2.2 + lodash.flattendeep: 4.4.0 + release-zalgo: 1.0.0 + package-json-from-dist@1.0.1: {} pako@0.2.9: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -15116,6 +17048,10 @@ snapshots: parse-ms@4.0.0: {} + parse-passwd@1.0.0: {} + + parse-svg-path@0.1.2: {} + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -15144,12 +17080,19 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-scurry@2.0.1: + dependencies: + lru-cache: 11.2.5 + minipass: 7.1.2 + path-to-regexp@6.3.0: {} path-type@4.0.0: {} pathe@2.0.3: {} + pathval@2.0.1: {} + peberminta@0.9.0: {} peek-stream@1.1.3: @@ -15194,6 +17137,14 @@ snapshots: transitivePeerDependencies: - react + playwright-core@1.58.2: {} + + playwright@1.58.2: + dependencies: + playwright-core: 1.58.2 + optionalDependencies: + fsevents: 2.3.2 + pluralize-esm@9.0.5: {} pngjs@5.0.0: {} @@ -15202,6 +17153,13 @@ snapshots: dependencies: '@babel/runtime': 7.28.6 + portfinder@1.0.38: + dependencies: + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + possible-typed-array-names@1.1.0: {} postcss-value-parser@4.2.0: {} @@ -15266,8 +17224,17 @@ snapshots: process-nextick-args@2.0.1: {} + process-on-spawn@1.1.0: + dependencies: + fromentries: 1.3.2 + process@0.11.10: {} + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -15280,6 +17247,8 @@ snapshots: proto-list@1.2.4: {} + proxy-from-env@1.1.0: {} + pump@2.0.1: dependencies: end-of-stream: 1.4.5 @@ -15326,6 +17295,10 @@ snapshots: queue-microtask@1.2.3: {} + queue@6.0.2: + dependencies: + inherits: 2.0.4 + quick-lru@5.1.1: {} quick-lru@7.3.0: {} @@ -15360,6 +17333,25 @@ snapshots: dependencies: react: 19.2.4 + react-docgen-typescript@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + react-docgen@8.0.2: + dependencies: + '@babel/core': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + '@types/doctrine': 0.0.9 + '@types/resolve': 1.20.6 + doctrine: 3.0.0 + resolve: 1.22.11 + strip-indent: 4.1.1 + transitivePeerDependencies: + - supports-color + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 @@ -15468,6 +17460,14 @@ snapshots: dependencies: picomatch: 2.3.1 + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -15533,6 +17533,10 @@ snapshots: dependencies: jsesc: 3.1.0 + release-zalgo@1.0.0: + dependencies: + es6-error: 4.1.1 + remeda@2.33.5: {} require-directory@2.1.1: {} @@ -15541,6 +17545,8 @@ snapshots: require-main-filename@2.0.0: {} + requires-port@1.0.0: {} + reselect@5.1.1: {} resend@6.9.1(@react-email/render@2.0.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)): @@ -15554,6 +17560,11 @@ snapshots: dependencies: resolve-from: 5.0.0 + resolve-dir@0.1.1: + dependencies: + expand-tilde: 1.2.2 + global-modules: 0.2.3 + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -15577,10 +17588,18 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 + restructure@3.0.2: {} + retry@0.13.1: {} + rettime@0.10.1: {} + reusify@1.1.0: {} + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + rimraf@5.0.10: dependencies: glob: 10.5.0 @@ -15618,6 +17637,8 @@ snapshots: rrweb-cssom@0.8.0: {} + run-applescript@7.1.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -15845,6 +17866,8 @@ snapshots: dependencies: xmlchars: 2.2.0 + scheduler@0.25.0-rc-603e6108-20241029: {} + scheduler@0.27.0: {} scroll-into-view-if-needed@3.1.0: @@ -15853,6 +17876,8 @@ snapshots: scrollmirror@1.2.4: {} + secure-compare@3.0.1: {} + selderee@0.11.0: dependencies: parseley: 0.12.1 @@ -15939,6 +17964,8 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.8.3: {} + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -15971,10 +17998,18 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.4: + dependencies: + is-arrayish: 0.3.4 + simple-wcswidth@1.1.2: {} + sisteransi@1.0.5: {} + slash@3.0.0: {} + slash@5.1.0: {} + slate-dom@0.119.0(slate@0.120.0): dependencies: '@juggle/resize-observer': 3.4.0 @@ -16019,6 +18054,24 @@ snapshots: space-separated-tokens@2.0.2: {} + spawn-wrap@2.0.0: + dependencies: + foreground-child: 2.0.0 + is-windows: 1.0.2 + make-dir: 3.1.0 + rimraf: 3.0.2 + signal-exit: 3.0.7 + which: 2.0.2 + + spawnd@5.0.0: + dependencies: + exit: 0.1.2 + signal-exit: 3.0.7 + tree-kill: 1.2.2 + wait-port: 0.2.14 + transitivePeerDependencies: + - supports-color + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -16050,6 +18103,8 @@ snapshots: '@stablelib/base64': 1.0.1 fast-sha256: 1.3.0 + statuses@2.0.2: {} + stdin-discarder@0.3.1: {} stop-iteration-iterator@1.1.0: @@ -16057,6 +18112,29 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@storybook/global': 5.0.0 + '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@testing-library/jest-dom': 6.9.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) + '@vitest/expect': 3.2.4 + '@vitest/spy': 3.2.4 + esbuild: 0.27.3 + open: 10.2.0 + recast: 0.23.11 + semver: 7.7.4 + use-sync-external-store: 1.6.0(react@19.2.4) + ws: 8.19.0 + optionalDependencies: + prettier: 3.8.1 + transitivePeerDependencies: + - '@testing-library/dom' + - bufferutil + - react + - react-dom + - utf-8-validate + stream-shift@1.0.3: {} streamx@2.23.0: @@ -16075,6 +18153,10 @@ snapshots: char-regex: 1.0.2 strip-ansi: 6.0.1 + string-length@6.0.0: + dependencies: + strip-ansi: 7.1.2 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -16174,6 +18256,8 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-indent@4.1.1: {} + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -16226,6 +18310,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svg-arc-to-cubic-bezier@3.2.0: {} + svix@1.84.1: dependencies: standardwebhooks: 1.0.0 @@ -16239,6 +18325,8 @@ snapshots: tabbable@6.4.0: {} + tagged-tag@1.0.0: {} + tailwind-merge@3.4.0: {} tailwindcss@4.1.18: {} @@ -16296,13 +18384,21 @@ snapshots: dependencies: readable-stream: 3.6.2 + tiny-inflate@1.0.3: {} + tiny-invariant@1.3.1: {} + tiny-invariant@1.3.3: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + tlds@1.261.0: {} tldts-core@6.1.86: {} @@ -16339,12 +18435,16 @@ snapshots: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 ts-brand@0.2.0: {} + ts-dedent@2.2.0: {} + ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(esbuild@0.27.3)(jest-util@30.2.0)(jest@30.2.0(@types/node@25.2.2)(esbuild-register@3.6.0(esbuild@0.27.3))(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@25.2.2)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 @@ -16432,6 +18532,10 @@ snapshots: type-fest@4.41.0: {} + type-fest@5.4.4: + dependencies: + tagged-tag: 1.0.0 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -16519,10 +18623,24 @@ snapshots: unicode-match-property-value-ecmascript@2.2.1: {} + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + unicode-property-aliases-ecmascript@2.2.0: {} + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + unicorn-magic@0.3.0: {} + union@0.5.0: + dependencies: + qs: 6.14.1 + unique-string@2.0.0: dependencies: crypto-random-string: 2.0.0 @@ -16544,6 +18662,13 @@ snapshots: universal-user-agent@7.0.3: {} + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.4 @@ -16568,6 +18693,8 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + until-async@3.0.2: {} + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -16578,6 +18705,8 @@ snapshots: dependencies: punycode: 2.3.1 + url-join@4.0.1: {} + urlpattern-polyfill@10.1.0: {} use-callback-ref@1.3.3(@types/react@19.2.13)(react@19.2.4): @@ -16650,6 +18779,12 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 + vite-compatible-readable-stream@3.6.1: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + vite-node@3.2.4(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 @@ -16671,6 +18806,32 @@ snapshots: - tsx - yaml + vite-plugin-storybook-nextjs@3.1.12(next@16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(storybook@10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + '@next/env': 16.0.0 + image-size: 2.0.2 + magic-string: 0.30.21 + module-alias: 2.3.4 + next: 16.1.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.8(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + ts-dedent: 2.2.0 + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite-tsconfig-paths: 5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + transitivePeerDependencies: + - supports-color + - typescript + + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + - typescript + vite-tsconfig-paths@6.1.0(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): dependencies: debug: 4.4.3(supports-color@8.1.1) @@ -16705,6 +18866,34 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + wait-on@7.2.0: + dependencies: + axios: 1.13.5 + joi: 17.13.3 + lodash: 4.17.23 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + + wait-on@8.0.5: + dependencies: + axios: 1.13.5 + joi: 18.0.2 + lodash: 4.17.23 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + + wait-port@0.2.14: + dependencies: + chalk: 2.4.2 + commander: 3.0.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + walk-up-path@4.0.0: {} walker@1.0.8: @@ -16725,6 +18914,12 @@ snapshots: webidl-conversions@8.0.1: {} + webpack-virtual-modules@0.6.2: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -16800,6 +18995,10 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -16860,12 +19059,18 @@ snapshots: ws@8.19.0: {} + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + xdg-basedir@4.0.0: {} xdg-basedir@5.1.0: {} xml-name-validator@5.0.0: {} + xml@1.0.1: {} + xmlchars@2.2.0: {} xstate@5.26.0: {} @@ -16919,8 +19124,12 @@ snapshots: yocto-queue@1.2.2: {} + yoctocolors-cjs@2.1.3: {} + yoctocolors@2.1.2: {} + yoga-layout@3.2.1: {} + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 diff --git a/public/og/README.md b/public/og/README.md deleted file mode 100644 index 07e2e7bb..00000000 --- a/public/og/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Static Open Graph Images - -## Status: Legacy - -These static OG images are maintained for specific use cases but are **no longer used for general social media previews**. - -## Files - -- `base.png` - Static conference logo image -- `base.svg` - SVG version of static conference logo - -## Current Usage - -These files are currently referenced in: - -- **OpenBadges Issuer Profile** ([/src/lib/badge/config.ts](../../src/lib/badge/config.ts) and [/src/app/api/badge/issuer/route.ts](../../src/app/api/badge/issuer/route.ts)) - - Used as the issuer organization image in OpenBadges 3.0 metadata - - Appropriate for this use case as it represents the issuing organization (Cloud Native Days Norway) - -## Dynamic OG Images - -All pages now use **dynamic Open Graph images** generated at build/request time: - -- **Homepage** (`/opengraph-image`) - Conference-specific with title, tagline, dates, location, logo -- **Program** (`/program/opengraph-image`) - Program page with conference context -- **CFP** (`/cfp/opengraph-image`) - Call for Papers with CFP deadline -- **Sponsor** (`/sponsor/opengraph-image`) - Sponsorship information -- **Tickets** (`/tickets/opengraph-image`) - Ticket information -- **Speaker Profiles** (`/speaker/[slug]/opengraph-image`) - Individual speaker cards with talk details -- **Badges** (`/badge/[badgeId]/opengraph-image`) - OpenBadges verification previews - -These dynamic images: - -- Automatically update when conference data changes in Sanity CMS -- Support multi-tenant architecture (different images per domain/conference) -- Use consistent branding from [/docs/BRANDING.md](../../docs/BRANDING.md) -- Include conference-specific logos, dates, locations, and content - -## Implementation - -Dynamic OG images use: - -- `@vercel/og` with Edge Runtime -- Shared utilities in [/src/lib/og/](../../src/lib/og/) -- Conference data from Sanity CMS via `getConferenceForDomain()` -- Cache strategy: `cacheLife('hours')` with page-specific cache tags - -## Migration Notes - -If you need to update the static badge issuer image: - -1. Replace `base.png` and `base.svg` in this directory -2. No code changes required (references are stable) -3. Consider whether a dynamic solution would be more appropriate - -For all other social preview use cases, use the dynamic OG image system. diff --git a/sanity/schema.ts b/sanity/schema.ts index 6d6bc6bd..9dfb9226 100644 --- a/sanity/schema.ts +++ b/sanity/schema.ts @@ -3,6 +3,7 @@ import { type SchemaTypeDefinition } from 'sanity' import { fileAttachment, urlAttachment } from './schemaTypes/attachment' import blockContent from './schemaTypes/blockContent' import conference from './schemaTypes/conference' +import contractTemplate from './schemaTypes/contractTemplate' import coSpeakerInvitation from './schemaTypes/coSpeakerInvitation' import dashboardConfig from './schemaTypes/dashboardConfig' import dataProcessingConsent from './schemaTypes/dataProcessingConsent' @@ -57,5 +58,6 @@ export const schema: { types: SchemaTypeDefinition[] } = { sponsorForConference, sponsorActivity, sponsorEmailTemplate, + contractTemplate, ], } diff --git a/sanity/schemaTypes/conference.ts b/sanity/schemaTypes/conference.ts index 2fa2f4ac..a9055c47 100644 --- a/sanity/schemaTypes/conference.ts +++ b/sanity/schemaTypes/conference.ts @@ -81,6 +81,22 @@ export default defineType({ fieldset: 'basicInfo', validation: (Rule) => Rule.required(), }), + defineField({ + name: 'organizerOrgNumber', + title: 'Organizer Org Number', + type: 'string', + fieldset: 'basicInfo', + description: + 'Organization number of the organizer (used in contracts and invoices)', + }), + defineField({ + name: 'organizerAddress', + title: 'Organizer Address', + type: 'string', + fieldset: 'basicInfo', + description: + 'Registered address of the organizer (used in contracts and invoices)', + }), defineField({ name: 'city', title: 'City', diff --git a/sanity/schemaTypes/contractTemplate.ts b/sanity/schemaTypes/contractTemplate.ts new file mode 100644 index 00000000..84320d45 --- /dev/null +++ b/sanity/schemaTypes/contractTemplate.ts @@ -0,0 +1,163 @@ +import { defineField, defineType } from 'sanity' +import { CURRENCY_OPTIONS } from './constants' + +export default defineType({ + name: 'contractTemplate', + title: 'Contract Template', + type: 'document', + fields: [ + defineField({ + name: 'title', + title: 'Title', + type: 'string', + description: 'Internal name for this contract template', + validation: (Rule) => Rule.required(), + }), + defineField({ + name: 'conference', + title: 'Conference', + type: 'reference', + to: [{ type: 'conference' }], + description: 'Conference this template belongs to', + validation: (Rule) => Rule.required(), + }), + defineField({ + name: 'tier', + title: 'Default Tier', + type: 'reference', + to: [{ type: 'sponsorTier' }], + description: + 'Optional: associate this template with a specific sponsor tier', + options: { + filter: ({ document }: { document: any }) => { + if (!document?.conference?._ref) return {} + + return { + filter: 'conference._ref == $conferenceId', + params: { conferenceId: document.conference._ref }, + } + }, + }, + }), + defineField({ + name: 'language', + title: 'Language', + type: 'string', + options: { + list: [ + { title: 'Norwegian (Bokmål)', value: 'nb' }, + { title: 'English', value: 'en' }, + ], + layout: 'radio', + }, + initialValue: 'nb', + validation: (Rule) => Rule.required(), + }), + defineField({ + name: 'currency', + title: 'Default Currency', + type: 'string', + options: { + list: [...CURRENCY_OPTIONS], + layout: 'dropdown', + }, + initialValue: 'NOK', + }), + defineField({ + name: 'sections', + title: 'Contract Sections', + type: 'array', + description: + 'Ordered list of sections that make up the contract document', + of: [ + { + type: 'object', + name: 'contractSection', + title: 'Section', + fields: [ + { + name: 'heading', + title: 'Section Heading', + type: 'string', + validation: (Rule) => Rule.required(), + }, + { + name: 'body', + title: 'Section Body', + type: 'blockContent', + description: + 'Use {{{VARIABLE_NAME}}} for dynamic values (e.g. {{{SPONSOR_NAME}}}, {{{TIER_NAME}}}, {{{CONTRACT_VALUE}}})', + }, + ], + preview: { + select: { + title: 'heading', + }, + }, + }, + ], + validation: (Rule) => Rule.required().min(1), + }), + defineField({ + name: 'headerText', + title: 'Header Text', + type: 'string', + description: 'Text shown in the PDF header (e.g. organization name)', + initialValue: 'Cloud Native Days Norway', + }), + defineField({ + name: 'footerText', + title: 'Footer Text', + type: 'string', + description: + 'Text shown in the PDF footer (e.g. org number, contact info)', + }), + defineField({ + name: 'terms', + title: 'General Terms & Conditions', + type: 'blockContent', + description: + 'General terms and conditions included as Appendix 1 in the contract PDF and displayed on the public sponsor terms page', + }), + defineField({ + name: 'isDefault', + title: 'Default Template', + type: 'boolean', + description: + 'Use this template as the default when no tier-specific template exists', + initialValue: false, + }), + defineField({ + name: 'isActive', + title: 'Active', + type: 'boolean', + description: 'Whether this template is available for use', + initialValue: true, + }), + ], + preview: { + select: { + title: 'title', + conferenceName: 'conference.title', + tierName: 'tier.title', + language: 'language', + isActive: 'isActive', + }, + prepare({ title, conferenceName, tierName, language, isActive }) { + const lang = language === 'nb' ? '🇳🇴' : '🇬🇧' + const status = isActive === false ? ' (inactive)' : '' + const tier = tierName ? ` — ${tierName}` : '' + return { + title: `${lang} ${title}${status}`, + subtitle: `${conferenceName || 'No Conference'}${tier}`, + } + }, + }, + orderings: [ + { + title: 'Title', + name: 'title', + by: [{ field: 'title', direction: 'asc' }], + }, + ], +}) diff --git a/sanity/schemaTypes/sponsor.ts b/sanity/schemaTypes/sponsor.ts index 49a58cf2..0ba037f4 100644 --- a/sanity/schemaTypes/sponsor.ts +++ b/sanity/schemaTypes/sponsor.ts @@ -43,6 +43,20 @@ export default defineType({ ) }, }), + defineField({ + name: 'address', + title: 'Address', + type: 'string', + description: 'Registered company address (used in contracts)', + hidden: ({ currentUser }) => { + return !( + currentUser != null && + currentUser.roles.find( + ({ name }) => name === 'administrator' || name === 'editor', + ) + ) + }, + }), ], preview: { select: { diff --git a/sanity/schemaTypes/sponsorForConference.ts b/sanity/schemaTypes/sponsorForConference.ts index 04988313..92355ffe 100644 --- a/sanity/schemaTypes/sponsorForConference.ts +++ b/sanity/schemaTypes/sponsorForConference.ts @@ -87,6 +87,68 @@ export default defineType({ initialValue: 'none', validation: (Rule) => Rule.required(), }), + defineField({ + name: 'signatureStatus', + title: 'Signature Status', + type: 'string', + description: 'Digital signature status from e-signing provider', + options: { + list: [ + { title: 'Not Started', value: 'not-started' }, + { title: 'Pending', value: 'pending' }, + { title: 'Signed', value: 'signed' }, + { title: 'Rejected', value: 'rejected' }, + { title: 'Expired', value: 'expired' }, + ], + layout: 'dropdown', + }, + initialValue: 'not-started', + }), + defineField({ + name: 'signatureId', + title: 'Signature ID', + type: 'string', + description: 'External ID from e-signing provider (e.g. Posten.no)', + readOnly: true, + }), + defineField({ + name: 'signerEmail', + title: 'Signer Email', + type: 'string', + description: 'Email of the person who should sign the contract', + }), + defineField({ + name: 'contractSentAt', + title: 'Contract Sent Date', + type: 'datetime', + description: 'When the contract was sent for signing', + readOnly: true, + }), + defineField({ + name: 'contractDocument', + title: 'Contract Document', + type: 'file', + description: 'Generated PDF contract document', + options: { + accept: 'application/pdf', + }, + }), + defineField({ + name: 'reminderCount', + title: 'Reminder Count', + type: 'number', + description: 'Number of contract signing reminders sent', + initialValue: 0, + readOnly: true, + validation: (Rule) => Rule.min(0), + }), + defineField({ + name: 'contractTemplate', + title: 'Contract Template', + type: 'reference', + to: [{ type: 'contractTemplate' }], + description: 'Template used to generate the contract', + }), defineField({ name: 'status', title: 'Status', @@ -317,6 +379,28 @@ export default defineType({ ) }, }), + defineField({ + name: 'onboardingToken', + title: 'Onboarding Token', + type: 'string', + description: 'Unique token for sponsor self-service onboarding portal', + readOnly: true, + }), + defineField({ + name: 'onboardingComplete', + title: 'Onboarding Complete', + type: 'boolean', + description: 'Whether the sponsor has completed onboarding', + initialValue: false, + readOnly: true, + }), + defineField({ + name: 'onboardingCompletedAt', + title: 'Onboarding Completed At', + type: 'datetime', + description: 'When the sponsor completed onboarding', + readOnly: true, + }), ], preview: { select: { diff --git a/scripts/test-storybook.sh b/scripts/test-storybook.sh new file mode 100755 index 00000000..4fe45ce1 --- /dev/null +++ b/scripts/test-storybook.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Script to test all Storybook stories +set -e + +echo "Starting Storybook server..." +pnpm storybook --quiet --ci & +STORYBOOK_PID=$! + +# Wait for Storybook to be ready +echo "Waiting for Storybook to be ready..." +max_attempts=30 +attempt=0 +until curl -s http://localhost:6006 > /dev/null; do + attempt=$((attempt + 1)) + if [ $attempt -eq $max_attempts ]; then + echo "Storybook failed to start after $max_attempts attempts" + kill $STORYBOOK_PID 2>/dev/null || true + exit 1 + fi + sleep 1 +done + +echo "Storybook is ready, running tests..." +pnpm storybook:test --maxWorkers=2 || TEST_EXIT_CODE=$? + +# Cleanup +echo "Stopping Storybook server..." +kill $STORYBOOK_PID 2>/dev/null || true + +exit ${TEST_EXIT_CODE:-0} diff --git a/scripts/validate-stories.ts b/scripts/validate-stories.ts new file mode 100644 index 00000000..53d56972 --- /dev/null +++ b/scripts/validate-stories.ts @@ -0,0 +1,124 @@ +#!/usr/bin/env tsx +/** + * Validate that all Storybook story files can be imported without errors + */ + +import { glob } from 'glob' +import { pathToFileURL } from 'node:url' +import path from 'node:path' + +async function validateStories() { + console.log('🔍 Finding all story files...\n') + + const storyFiles = await glob('src/**/*.stories.{ts,tsx}', { + cwd: process.cwd(), + absolute: true, + }) + + console.log(`Found ${storyFiles.length} story files\n`) + + const errors: Array<{ file: string; error: Error }> = [] + const warnings: Array<{ file: string; message: string }> = [] + + for (const file of storyFiles) { + const relativePath = path.relative(process.cwd(), file) + process.stdout.write(`Testing ${relativePath}... `) + + try { + // Import the story file + const fileUrl = pathToFileURL(file).href + const storyModule = await import(fileUrl) + + // Check if it has a default export (meta) + if (!storyModule.default) { + warnings.push({ + file: relativePath, + message: 'Missing default export (story meta)', + }) + console.log('⚠️ WARNING: Missing meta') + continue + } + + // Check if meta has required properties + const meta = storyModule.default + if (!meta.title) { + warnings.push({ + file: relativePath, + message: 'Meta missing title property', + }) + console.log('⚠️ WARNING: Missing title') + continue + } + + // Count stories (exports that aren't default or Meta) + const storyExports = Object.keys(storyModule).filter( + (key) => key !== 'default' && key !== '__esModule', + ) + + if (storyExports.length === 0) { + warnings.push({ + file: relativePath, + message: 'No stories exported', + }) + console.log(`⚠️ WARNING: No stories`) + continue + } + + console.log(`✅ ${storyExports.length} stories`) + } catch (error) { + errors.push({ + file: relativePath, + error: error as Error, + }) + console.log('❌ ERROR') + } + } + + // Print summary + console.log('\n' + '='.repeat(60)) + console.log('SUMMARY') + console.log('='.repeat(60) + '\n') + + console.log(`Total files: ${storyFiles.length}`) + console.log( + `✅ Passed: ${storyFiles.length - errors.length - warnings.length}`, + ) + console.log(`⚠️ Warnings: ${warnings.length}`) + console.log(`❌ Errors: ${errors.length}`) + + if (warnings.length > 0) { + console.log('\n' + '='.repeat(60)) + console.log('WARNINGS') + console.log('='.repeat(60) + '\n') + + for (const { file, message } of warnings) { + console.log(`⚠️ ${file}`) + console.log(` ${message}\n`) + } + } + + if (errors.length > 0) { + console.log('\n' + '='.repeat(60)) + console.log('ERRORS') + console.log('='.repeat(60) + '\n') + + for (const { file, error } of errors) { + console.log(`❌ ${file}`) + console.log(` ${error.message}`) + if (error.stack) { + const stackLines = error.stack.split('\n').slice(1, 4) + stackLines.forEach((line) => console.log(` ${line.trim()}`)) + } + console.log() + } + + process.exit(1) + } + + console.log('\n✨ All stories validated successfully!\n') +} + +validateStories().catch((error) => { + console.error('Fatal error:', error) + process.exit(1) +}) diff --git a/src/__mocks__/sponsor-data.ts b/src/__mocks__/sponsor-data.ts new file mode 100644 index 00000000..21782a2e --- /dev/null +++ b/src/__mocks__/sponsor-data.ts @@ -0,0 +1,182 @@ +import type { + SponsorForConferenceExpanded, + SponsorTag, + SponsorStatus, + ContractStatus, + InvoiceStatus, + SignatureStatus, +} from '@/lib/sponsor-crm/types' +import type { + ContactPerson, + BillingInfo, + SponsorTier, +} from '@/lib/sponsor/types' +import type { + ContractReadiness, + MissingField, +} from '@/lib/sponsor-crm/contract-readiness' + +/** + * Mock data factories for sponsor-related components in Storybook + */ + +export function mockContactPerson( + overrides: Partial = {}, +): ContactPerson { + return { + _key: `contact-${Math.random().toString(36).substr(2, 9)}`, + name: 'Jane Smith', + email: 'jane.smith@example.com', + phone: '+47 12 34 56 78', + role: 'Marketing Manager', + isPrimary: false, + ...overrides, + } +} + +export function mockBillingInfo( + overrides: Partial = {}, +): BillingInfo { + return { + email: 'billing@example.com', + reference: 'PO-2026-001', + comments: 'Invoice quarterly', + ...overrides, + } +} + +export function mockSponsorTier( + overrides: Partial = {}, +): SponsorTier { + return { + _id: 'tier-ingress', + _createdAt: '2026-01-01T00:00:00Z', + _updatedAt: '2026-01-01T00:00:00Z', + title: 'Ingress', + tagline: 'Premium sponsorship tier', + tierType: 'standard' as const, + price: [ + { + _key: 'price-nok', + amount: 100000, + currency: 'NOK', + }, + ], + soldOut: false, + mostPopular: false, + ...overrides, + } as SponsorTier +} + +export function mockSponsor( + overrides: Partial = {}, +): SponsorForConferenceExpanded { + return { + _id: 'sfc-123', + _createdAt: '2026-01-15T10:00:00Z', + _updatedAt: '2026-02-10T14:30:00Z', + sponsor: { + _id: 'sponsor-123', + name: 'Acme Corporation', + website: 'https://acme.example.com', + logo: '...', + logoBright: '...', + orgNumber: '123456789', + address: 'Tech Street 42, 5020 Bergen', + }, + conference: { + _id: 'conf-2026', + title: 'Cloud Native Days Norway 2026', + organizer: 'Cloud Native Bergen', + organizerOrgNumber: '987654321', + organizerAddress: 'Event Plaza 1, 5003 Bergen', + city: 'Bergen', + venueName: 'Bergen Conference Center', + venueAddress: 'Conference Way 10, 5010 Bergen', + startDate: '2026-06-10', + endDate: '2026-06-11', + sponsorEmail: 'sponsor@cloudnativedays.no', + }, + tier: mockSponsorTier() as SponsorTier & { + tierType: 'standard' | 'special' + }, + addons: [], + contractStatus: 'verbal-agreement' as ContractStatus, + signatureStatus: 'not-started' as SignatureStatus, + status: 'negotiating' as SponsorStatus, + contractValue: 100000, + contractCurrency: 'NOK', + invoiceStatus: 'not-sent' as InvoiceStatus, + contactPersons: [mockContactPerson({ isPrimary: true })], + billing: mockBillingInfo(), + tags: ['warm-lead', 'returning-sponsor'] as SponsorTag[], + notes: 'Very interested in premium package', + onboardingComplete: false, + ...overrides, + } +} + +export function mockReadinessReady(): ContractReadiness { + return { + ready: true, + missing: [], + } +} + +export function mockReadinessMissing( + fields?: MissingField[], +): ContractReadiness { + const defaultMissing: MissingField[] = fields || [ + { + field: 'sponsor.orgNumber', + label: 'Organization number', + source: 'sponsor', + }, + { field: 'sponsor.address', label: 'Address', source: 'sponsor' }, + { + field: 'conference.organizerOrgNumber', + label: 'Organizer org number', + source: 'organizer', + }, + { field: 'tier', label: 'Sponsor tier', source: 'pipeline' }, + ] + + return { + ready: false, + missing: defaultMissing, + } +} + +export const mockSponsors = { + prospect: mockSponsor({ + status: 'prospect', + contractStatus: 'none', + tags: ['cold-outreach'], + }), + contacted: mockSponsor({ + status: 'contacted', + contractStatus: 'none', + contactInitiatedAt: '2026-01-20T09:00:00Z', + tags: ['warm-lead'], + }), + negotiating: mockSponsor({ + status: 'negotiating', + contractStatus: 'verbal-agreement', + tags: ['warm-lead', 'high-priority'], + }), + closedWon: mockSponsor({ + status: 'closed-won', + contractStatus: 'contract-signed', + signatureStatus: 'signed', + invoiceStatus: 'paid', + contractSignedAt: '2026-02-01T12:00:00Z', + invoicePaidAt: '2026-02-15T10:30:00Z', + tags: ['returning-sponsor'], + }), + closedLost: mockSponsor({ + status: 'closed-lost', + contractStatus: 'none', + tags: ['previously-declined'], + notes: 'Budget constraints for 2026', + }), +} diff --git a/src/app/(admin)/admin/marketing/page.tsx b/src/app/(admin)/admin/marketing/page.tsx index b40be92a..5efc1b96 100644 --- a/src/app/(admin)/admin/marketing/page.tsx +++ b/src/app/(admin)/admin/marketing/page.tsx @@ -6,7 +6,7 @@ import { formatConferenceDateLong } from '@/lib/time' import { Status } from '@/lib/proposal/types' import { SpeakerShare } from '@/components/SpeakerShare' import { SponsorThankYou } from '@/components/SponsorThankYou' -import { DownloadSpeakerImage } from '@/components/branding/DownloadSpeakerImage' +import { DownloadableImage } from '@/components/common/DownloadableImage' import { AdminPageHeader } from '@/components/admin' import { MarketingTabs } from '@/components/admin/MarketingTabs' import { MemeGeneratorWithDownload } from '@/components/admin/MemeGeneratorWithDownload' @@ -328,7 +328,7 @@ export default async function MarketingPage() { {/* Conference Promotional Tab */}
-
- + {/* Photo Gallery Tab */} @@ -456,7 +456,7 @@ export default async function MarketingPage() {
{speakersWithTalks.map(({ speaker, talks }) => (
-
-
+
))}
@@ -512,7 +512,7 @@ export default async function MarketingPage() { return (
-
-
+
) })} diff --git a/src/app/(admin)/admin/sponsors/contracts/[id]/page.tsx b/src/app/(admin)/admin/sponsors/contracts/[id]/page.tsx new file mode 100644 index 00000000..c90977a5 --- /dev/null +++ b/src/app/(admin)/admin/sponsors/contracts/[id]/page.tsx @@ -0,0 +1,34 @@ +import { getConferenceForCurrentDomain } from '@/lib/conference/sanity' +import { ErrorDisplay } from '@/components/admin' +import { ContractTemplateEditorPage } from '@/components/admin/sponsor/ContractTemplateEditorPage' + +export default async function EditContractTemplatePage({ + params, +}: { + params: Promise<{ id: string }> +}) { + const { id } = await params + + const { conference, error: conferenceError } = + await getConferenceForCurrentDomain({}) + + if (conferenceError) { + return ( + + ) + } + + if (!conference) { + return ( + + ) + } + + return +} diff --git a/src/app/(admin)/admin/sponsors/contracts/new/page.tsx b/src/app/(admin)/admin/sponsors/contracts/new/page.tsx new file mode 100644 index 00000000..7e69d813 --- /dev/null +++ b/src/app/(admin)/admin/sponsors/contracts/new/page.tsx @@ -0,0 +1,28 @@ +import { getConferenceForCurrentDomain } from '@/lib/conference/sanity' +import { ErrorDisplay } from '@/components/admin' +import { ContractTemplateEditorPage } from '@/components/admin/sponsor/ContractTemplateEditorPage' + +export default async function NewContractTemplatePage() { + const { conference, error: conferenceError } = + await getConferenceForCurrentDomain({}) + + if (conferenceError) { + return ( + + ) + } + + if (!conference) { + return ( + + ) + } + + return +} diff --git a/src/app/(admin)/admin/sponsors/contracts/page.tsx b/src/app/(admin)/admin/sponsors/contracts/page.tsx new file mode 100644 index 00000000..c75c992d --- /dev/null +++ b/src/app/(admin)/admin/sponsors/contracts/page.tsx @@ -0,0 +1,28 @@ +import { getConferenceForCurrentDomain } from '@/lib/conference/sanity' +import { ErrorDisplay } from '@/components/admin' +import { ContractTemplateListPage } from '@/components/admin/sponsor/ContractTemplateListPage' + +export default async function AdminContractTemplates() { + const { conference, error: conferenceError } = + await getConferenceForCurrentDomain({}) + + if (conferenceError) { + return ( + + ) + } + + if (!conference) { + return ( + + ) + } + + return +} diff --git a/src/app/(main)/branding/page.tsx b/src/app/(main)/branding/page.tsx deleted file mode 100644 index c23eddc3..00000000 --- a/src/app/(main)/branding/page.tsx +++ /dev/null @@ -1,4351 +0,0 @@ -import { Metadata } from 'next' -import { Container } from '@/components/Container' -import { DiamondIcon } from '@/components/DiamondIcon' -import Image from 'next/image' -import { cacheLife } from 'next/cache' - -// Import cloud native icons for the pattern examples -import KubernetesIcon from '@/images/icons/kubernetes-icon-white.svg' -import PrometheusIcon from '@/images/icons/prometheus-icon-white.svg' -import IstioIcon from '@/images/icons/istio-icon-white.svg' -import HelmIcon from '@/images/icons/helm-icon-white.svg' - -import { - CloudIcon, - ServerIcon, - CubeIcon, - CircleStackIcon, - CommandLineIcon, - ShieldCheckIcon, - ChartBarIcon, - CogIcon, - BoltIcon, - GlobeAltIcon, - LinkIcon, - ArrowPathIcon, - QueueListIcon, - WrenchScrewdriverIcon, - EyeIcon, - RocketLaunchIcon, - LightBulbIcon, -} from '@heroicons/react/24/outline' -import { - CloudIcon as CloudIconSolid, - ServerIcon as ServerIconSolid, - ShieldCheckIcon as ShieldCheckIconSolid, -} from '@heroicons/react/24/solid' - -// Import branding components and data -import { - ColorSwatch, - TypographyShowcase, - IconShowcase, - InteractivePatternPreview, - BrandingHeroSection, - BrandingExampleHeroSection, - PatternExample, - ButtonShowcase, - DownloadSpeakerImage, - ExpandableEmailTemplate, -} from '@/components/branding' -import { colorPalette, typography } from '@/lib/branding/data' - -import { TalkPromotionCard } from '@/components/TalkPromotionCard' -import { SpeakerPromotionCard } from '@/components/SpeakerPromotionCard' -import { SpeakerShare } from '@/components/SpeakerShare' -import { TypewriterEffect } from '@/components/TypewriterEffect' -import { - ProposalAcceptTemplate, - ProposalRejectTemplate, - BroadcastTemplate, - BaseEmailTemplate, - CoSpeakerInvitationTemplate, - CoSpeakerResponseTemplate, -} from '@/components/email' -import { portableTextToHTML } from '@/lib/email/portableTextToHTML' -import { CallToAction } from '@/components/CallToAction' -import { - Format, - Level, - ProposalExisting, - Status, - Language, - Audience, -} from '@/lib/proposal/types' -import { SpeakerWithTalks } from '@/lib/speaker/types' -import { getConferenceForDomain } from '@/lib/conference/sanity' -import { headers } from 'next/headers' - -export const metadata: Metadata = { - title: 'Brand Guidelines - Cloud Native Days', - description: - 'Brand guidelines and design system for Cloud Native Days conferences', -} - -async function CachedBrandingContent({ domain }: { domain: string }) { - 'use cache' - cacheLife('max') - - const conferenceData = await getConferenceForDomain(domain, { - featuredSpeakers: true, - organizers: true, - }) - const { conference } = conferenceData - - // Use featured speakers if available, otherwise fall back to organizers - const displaySpeakers = - conference?.featuredSpeakers && conference.featuredSpeakers.length > 0 - ? conference.featuredSpeakers - : (conference?.organizers ?? []) - - // Helper to create mock ProposalExisting objects for design examples - function mockTalk(params: { - id: string - title: string - format: Format - level?: Level - description?: string - speakerNames?: string[] - }): ProposalExisting { - const now = new Date().toISOString() - const speakers = (params.speakerNames || ['Example Speaker']).map( - (name, idx) => ({ - _id: `sp-${params.id}-${idx}`, - _rev: '1', - _createdAt: now, - _updatedAt: now, - name, - email: `${name.toLowerCase().replace(/[^a-z]/g, '')}@example.com`, - title: 'Engineer', - }), - ) as SpeakerWithTalks[] - return { - _id: params.id, - _rev: '1', - _type: 'proposal', - _createdAt: now, - _updatedAt: now, - status: Status.accepted, - title: params.title, - description: params.description - ? [ - { - _type: 'block', - _key: `b-${params.id}`, - style: 'normal', - children: [ - { - _type: 'span', - _key: `s-${params.id}`, - text: params.description, - marks: [], - }, - ], - markDefs: [], - }, - ] - : [], - language: Language.english, - format: params.format, - level: params.level || Level.intermediate, - audiences: [Audience.developer], - outline: '', - tos: true, - speakers, - conference: { _ref: 'conf-mock', _type: 'reference' as const }, - } - } - - return ( -
- {/* Hero Section */} - - - {/* Navigation Menu */} - - - {/* Brand Story */} -
- -
-

- Our Brand Story -

-
-
-

- Cloud Native Days embodies the spirit of the global tech - community: innovative yet grounded, collaborative yet - independent, modern yet respectful of tradition. -

-

- Our visual identity draws inspiration from Nordic - landscapes—the meeting of mountains and sea, the interplay of - mist and clarity, the harmony of natural and urban elements. -

-

- We celebrate the "nerdy and proud" developer culture - while maintaining accessibility and inclusivity for all - members of our community. -

-
-
-

- Brand Values -

-
    -
  • - - - Open Source Spirit - -
  • -
  • - - - Technical Excellence - -
  • -
  • - - - Community First - -
  • -
  • - - - Accessibility & Inclusion - -
  • -
-
-
- - {/* Design Principles */} -
-

- Design Principles -

-

- These principles guide every design decision and ensure our - brand remains consistent, accessible, and true to our community - values. -

- -
- {[ - { - title: 'Developer-First', - description: - 'Every design choice considers the developer experience and technical audience.', - icon: CommandLineIcon, - }, - { - title: 'Accessible by Design', - description: - 'We prioritize accessibility and inclusive design in all brand applications.', - icon: EyeIcon, - }, - { - title: 'Nordic Minimalism', - description: - 'Clean, functional design that lets content shine without unnecessary complexity.', - icon: BoltIcon, - }, - { - title: 'Community Driven', - description: - 'Our brand reflects the collaborative spirit of the open source community.', - icon: GlobeAltIcon, - }, - ].map((principle) => ( -
-
- -
-

- {principle.title} -

-

- {principle.description} -

-
- ))} -
-
-
-
-
- - {/* Color Palette */} -
- -
-

- Color Palette -

-

- Our colors reflect Nordic natural beauty—from the deep blues of - Norwegian fjords to the fresh greens of nordic forests, balanced - with modern tech-inspired accents. -

-
- - {Object.entries(colorPalette).map(([category, colors]) => ( -
-

- {category} Colors -

-
- {colors.map((color) => ( - - ))} -
-
- ))} - - {/* Background Utilities */} -
-

- Background Utilities -

-

- Our background system includes solid colors and gradients that - work seamlessly across all brand applications and maintain proper - contrast ratios. -

- - {/* Gradient Backgrounds */} -
-

- Gradient Backgrounds -

-
-
-
-
-
- Aqua Gradient -
-

- Primary hero gradient -

- - bg-aqua-gradient - -
-
- -
-
-
-
- Brand Gradient -
-

- Enhanced brand gradient -

- - bg-brand-gradient - -
-
- -
-
-
-
- Nordic Gradient -
-

- Accent gradient -

- - bg-nordic-gradient - -
-
-
-
- - {/* Solid Color Backgrounds */} -
-

- Solid Color Backgrounds -

-
- {/* Primary Colors */} -
-
-
- Cloud Blue -
- - bg-brand-cloud-blue - -
- -
-
-
- Fresh Green -
- - bg-brand-fresh-green - -
- -
-
-
- Nordic Purple -
- - bg-brand-nordic-purple - -
- -
-
-
- Sunbeam Yellow -
- - bg-brand-sunbeam-yellow - -
- - {/* Neutral Colors */} -
-
-
- Sky Mist -
- - bg-brand-sky-mist - -
- -
-
-
- Glacier White -
- - bg-brand-glacier-white - -
- -
-
-
- Slate Gray -
- - bg-brand-slate-gray - -
- -
-
-
- Frosted Steel -
- - bg-brand-frosted-steel - -
-
-
- - {/* Usage Guidelines */} -
-

- Background Usage Guidelines -

-
-
-
- When to Use Gradients -
-
    -
  • - - Hero sections and primary call-to-actions -
  • -
  • - - Section dividers and visual breaks -
  • -
  • - - Digital badges and highlighting -
  • -
-
-
-
- When to Use Solid Colors -
-
    -
  • - - Content sections and cards -
  • -
  • - - UI components and interactive elements -
  • -
  • - - Status indicators and alerts -
  • -
-
-
-
-
-
-
- - {/* Typography */} -
- -
-

- Typography -

-

- Our typography system balances developer-friendly monospace fonts - with clean, readable sans-serifs to create a distinctive yet - accessible visual voice. -

-
- -
-

- Primary Fonts (Headings & Branding) -

-
- {typography.primary.map((font) => ( - - ))} -
-
- -
-

- Secondary Fonts (Body & UI Text) -

-
- {typography.secondary.map((font) => ( - - ))} -
-
-
-
- - {/* Typewriter Effect */} -
- -
-

- Typewriter Effect -

-

- An accessible animated typing effect for hero taglines. Cycles - through words with a blinking cursor, respecting user motion - preferences. -

-
- -
- {/* Live Demo */} -
-

- Live Demo -

-
-

- -

-
-
- - {/* Speed Variations */} -
-

- Speed Variations -

-
-
-

- Fast (60ms typing) -

-

- -

-
-
-

- Slow (150ms typing) -

-

- -

-
-
-
- - {/* Static (Animation Disabled) */} -
-

- Animation Disabled -

-
-

- animation=false (or prefers-reduced-motion) -

-

- -

-
-
- - {/* Accessibility Features */} -
-

- Accessibility Features -

-
    -
  • - - - Screen readers: Full text always available - via aria-label and sr-only span - -
  • -
  • - - - SEO: Complete text in DOM from the start - for crawlers - -
  • -
  • - - - Reduced motion: Automatically respects - prefers-reduced-motion preference - -
  • -
  • - - - Animation toggle: Can be disabled via - animation=false prop - -
  • -
-
- - {/* UX Considerations */} -
-

- UX Considerations -

-
    -
  • - - Keep words short - users don't wait for long sentences -
  • -
  • - - Don't rely on the effect alone - support with static - content -
  • -
  • - - Consider if animation adds value or is just decoration -
  • -
-
- - {/* Code Example */} -
-

- Usage -

-
-
-                  {``}
-                
-
-
- - {/* Auto-detection Note */} -
-

- Auto-detection in Hero -

-

- The Hero component automatically uses the typewriter effect when - the conference tagline starts with “Real ”. This - allows easy toggling by simply changing the tagline in Sanity - CMS. -

-
-
-
-
- - {/* Icon Library */} -
- -
-

- Icon Library -

-

- A comprehensive set of cloud native and Kubernetes-inspired icons - designed to align with our brand principles and represent key - technology concepts. -

-
- -
- {/* Platform Icons */} -
-

- Platform Icons -

-
- - } - usage="Cloud sections, infrastructure topics, platform overviews" - /> - - } - usage="Compute sections, server management, infrastructure diagrams" - /> - - } - usage="Containerization topics, Docker sections, packaging concepts" - /> - - } - usage="Queue management, task processing, workflow systems" - /> -
-
- - {/* Data & Storage Icons */} -
-

- Data & Storage Icons -

-
- - } - usage="Database sections, storage topics, persistence patterns" - /> - - } - usage="Developer tools, CLI sections, terminal operations" - /> - - } - usage="Configuration topics, settings panels, system management" - /> - - } - usage="DevTools, utilities, maintenance, system operations" - /> -
-
- - {/* Operations Icons */} -
-

- Operations Icons -

-
- - } - usage="Security sections, compliance topics, trust and safety" - /> - - } - usage="Observability topics, monitoring sections, analytics dashboards" - /> - - } - usage="Performance topics, speed optimization, rapid deployment" - /> - - } - usage="Observability, monitoring, system insights, visibility" - /> -
-
- - {/* Network & Connectivity Icons */} -
-

- Network & Connectivity Icons -

-
- - } - usage="Global topics, multi-region, international deployment" - /> - - } - usage="Service mesh, microservices communication, API connections" - /> - - } - usage="CI/CD sections, automation topics, workflow management" - /> - - } - usage="Deployment topics, launch processes, go-live activities" - /> -
-
- - {/* Icon Styles & Usage */} -
-

- Icon Styles & Usage -

-
- - - - -
- } - usage="General UI, content sections, navigation, feature lists" - /> - - - - -
- } - usage="Status indicators, CTAs, highlights, success states" - /> -
-
- - {/* Usage Guidelines */} -
-

- Heroicons Usage Guidelines -

-
-
-

- Sizing & Scale -

-
-
- - - 16px (w-4 h-4) - Inline with text - -
-
- - - 24px (w-6 h-6) - Standard UI elements - -
-
- - - 32px (w-8 h-8) - Section headers - -
-
- - - 48px (w-12 h-12) - Hero sections - -
-
-
-
-

- Color Application -

-
-

- Heroicons inherit text color and work with our full brand - palette: -

-
- - - - - -
-

- Use brand colors to create visual hierarchy and context -

-
-
-
- -
-

- Code Examples -

-
-
-

- Import from Heroicons: -

- - {`import { CloudIcon, ServerIcon } from '@heroicons/react/24/outline'`} - -
-
-

- Usage with Tailwind sizing: -

- - {``} - -
-
-
-
- - - - - {/* Cloud Native Pattern System */} -
- -
-

- Cloud Native Pattern System -

-

- Our animated background patterns incorporate authentic cloud - native project logos with intelligent focus/diffusion effects. - Smaller icons appear sharp and vibrant (in focus), while larger - icons become more diffuse and subtle, creating natural visual - depth that enhances readability and engagement. -

-
- -
- {/* Interactive Pattern Preview - Pattern Controls */} -
-

- Interactive Pattern Preview -

-
- -
-
- - {/* Configuration Guidelines */} -
-

- Configuration Guidelines -

-
-
-
-

- Icon Size -

-
    -
  • - - Content sections: 15-35px icons -
  • -
  • - - Hero sections: 25-70px icons -
  • -
  • - - Background fills: 20-50px icons -
  • -
-
-
-

- Icon Count -

-
    -
  • - - Subtle: 10-30 icons for content backgrounds -
  • -
  • - - Balanced: 30-60 icons for hero sections -
  • -
  • - - Dense: 60-120 icons for dramatic effects -
  • -
-
-
-
-

- Focus/Diffusion System -

-
    -
  • - - Small icons (20-30px): High opacity, vibrant colors, sharp - focus -
  • -
  • - - Medium icons (30-50px): Balanced opacity and slight blur -
  • -
  • - - Large icons (50-70px): Lower opacity, subtle colors, soft - blur -
  • -
-
    -
  • - - Adjust opacity (0.08-0.15) based on content readability -
  • -
  • - - Use slow movement animation for engaging backgrounds -
  • -
  • - - Disable animation for static contexts or better - performance -
  • -
  • - - Combine with gradient backgrounds for optimal contrast -
  • -
-
-
-
-
- - {/* Pattern Elements Section - Moved below main grid */} -
-

- Pattern Elements -

-
-
-
- Kubernetes -
-

- Container Orchestration -

-

- Kubernetes, containerd, and etcd - the foundation of modern - container orchestration. -

-
- -
-
- Prometheus -
-

- Observability & Monitoring -

-

- Prometheus, Jaeger, and Falco for comprehensive system - observability and security. -

-
- -
-
- Istio -
-

- Service Mesh & Networking -

-

- Istio, Envoy, and Cilium for secure, observable - service-to-service communication. -

-
- -
-
- Helm -
-

- Packaging & GitOps -

-

- Helm, Argo, and Crossplane for application packaging and - deployment automation. -

-
-
-
-
-
- - {/* Configuration Examples Section */} -
- -
-

- Configuration Examples -

-

- See how different settings create unique visual effects for - various use cases -

-
- -
- {/* Light variant with custom sizing */} - - - {/* Default hero pattern - improved with larger icons */} - - - {/* Dense dramatic pattern */} - -
- - {/* Technical Details */} -
-

- Focus/Diffusion Technology -

-
-
-
- - S - -
-
- Small Icons (Sharp Focus) -
-

- Higher opacity, vibrant colors, no blur. Draw attention as - foreground elements. -

-
-
-
- - M - -
-
- Medium Icons (Balanced) -
-

- Moderate opacity and subtle blur. Provide visual texture - without distraction. -

-
-
-
- - L - -
-
- Large Icons (Soft Diffusion) -
-

- Lower opacity, muted colors, soft blur. Create atmospheric - background depth. -

-
-
-
-
-
- {/* Button Showcase */} -
- -
-

- Button Showcase -

-

- Our button system provides consistent, accessible interactions - across all brand applications with clear visual hierarchy. -

-
- - - - {/* Icon Usage Examples */} -
-

- Icons in UI Components -

-
- {/* Feature Grid Example */} -
-

- Platform Features -

-
-
- - - Cloud Native - -
-
- - - Containers - -
-
- - - Security - -
-
- - - Monitoring - -
-
-
- - {/* Navigation Example */} - -
-
- - {/* Icon Style Comparison */} -
-

- Outline vs Solid Icons -

-
- {/* Outline Style */} -
-

- Outline Style (Default) -

-
-
-
- - - Infrastructure - -
- - Available - -
-
-
- - - Compute - -
- - Running - -
-
-
- - - Security - -
- - Active - -
-
-
- - {/* Solid Style */} -
-

- Solid Style (Emphasis) -

-
-
-
- - - Infrastructure - -
- - Healthy - -
-
-
- - - Compute - -
- - Optimized - -
-
-
- - - Security - -
- - Protected - -
-
-
-
-
-
-
- - {/* Hero Examples */} -
- -
-

- Hero Examples -

-

- Hero sections showcase our brand's visual impact through - compelling combinations of typography, color, and cloud native - patterns. -

-
- - -
-
- - {/* Speaker Examples */} -
- -
-

- Speaker Examples -

-

- Showcase conference speakers with flexible, brand-consistent - layouts. From keynote heroes to compact grids, these examples - demonstrate various ways to highlight our community experts using - real conference data. -

-
- -
- {/* Featured Speaker */} - {displaySpeakers.length > 0 && ( -
-
-

- Featured Speaker -

-

- Streamlined presentation for featured speakers with - essential information and clean visual design. Perfect for - homepage highlights and key announcements. -

-
- - -
- )} - - {/* Three Featured Speakers */} - {displaySpeakers.length >= 3 && ( -
-
-

- Three Featured Speakers -

-

- Perfect for highlighting key speakers with balanced visual - weight. Ideal for homepage features and conference - announcements. -

-
- -
- {displaySpeakers.slice(0, 3).map((speaker) => ( - - ))} -
-
- )} - - {/* Compact Speaker List */} - {displaySpeakers.length >= 4 && ( -
-
-

- Compact Speaker List -

-

- Space-efficient format for agenda pages and speaker - directories. Shows essential information with talk details - prominently featured. -

-
- -
- {displaySpeakers.slice(0, 6).map((speaker, index) => ( - - ))} -
-
- )} - - {/* Speaker Share Component Showcase */} - {displaySpeakers.length >= 2 && ( -
-
-

- Speaker Share Component Showcase -

-

- The SpeakerShare component creates branded social media - cards that speakers can use to promote their participation. - Features QR codes for easy profile access, responsive - design, and optional Cloud Native pattern backgrounds. -

-
-

- - - Interactive Download Feature! - -
- Click “Download as PNG” below any speaker card - to save high-quality social media images. The download may - take a few seconds to process as it waits for all content - (including QR codes) to load properly. -

-
-
- - {/* Full Size Variants - Speaker Share vs Speaker Spotlight */} -
-

- Component Variants (Full Size) -

-

- Compare the two main variants: speaker-share for speakers to - promote themselves, and speaker-spotlight for conference - organizers to highlight speakers. -

- -
- {/* Speaker Share Variant */} -
-
-
- Speaker Share -
-

- “I'm speaking at” message -

-
- - - -
- - {/* Speaker Spotlight Variant */} -
-
-
- Speaker Spotlight -
-

- “Featured Speaker” message -

-
- - - -
-
-
- - {/* Size Variations */} -
-

- Size Variations & Responsive Design -

-

- The component uses container queries to maintain perfect - square proportions and readability across all sizes. Always - maintains aspect ratio for optimal social media sharing. -

- - {/* Large Grid */} -
-
- Large (For Feature Sections) -
-
- - - -
-
- - {/* Small Grid */} -
-
- Small (For Compact Grids) -
-
- {displaySpeakers.slice(0, 6).map((speaker, index) => ( - - ))} -
-
- - {/* Extra Small Grid */} -
-
- Extra Small (Thumbnail Size) -
-
- {displaySpeakers.slice(0, 10).map((speaker, index) => ( - - ))} -
-
-
- - {/* Technical Features */} -
-

- Technical Features & Capabilities -

- -
-
-
- QR Code Integration -
-
    -
  • • Automatically generated for each speaker
  • -
  • • Links to speaker profile page
  • -
  • • High contrast for reliable scanning
  • -
  • • Optimized for mobile cameras
  • -
  • • Error correction for damaged prints
  • -
-
- -
-
- Responsive Design -
-
    -
  • • Container queries for perfect scaling
  • -
  • • Fluid typography and spacing
  • -
  • • Aspect ratio preservation
  • -
  • • Optimized for social media platforms
  • -
  • • Works from thumbnails to hero images
  • -
-
- -
-
- Cloud Native Pattern -
-
    -
  • • 50+ authentic project logos
  • -
  • • Intelligent depth layering
  • -
  • • Smooth animations and movement
  • -
  • • Performance optimized
  • -
  • • Works with both variants
  • -
-
-
- -
-
- Usage Guidelines -
-
-
-
- Speaker Share Variant -
-
    -
  • - • For speakers to share on their own social media -
  • -
  • - • “I'm speaking at” messaging -
  • -
  • • Personal branding focus
  • -
  • • Include speaker's primary talk
  • -
-
-
-
- Speaker Spotlight Variant -
-
    -
  • - • For conference organizers to promote speakers -
  • -
  • • “Featured Speaker” messaging
  • -
  • • Conference branding focus
  • -
  • • Highlight keynote and featured speakers
  • -
-
-
-
- -
-
- Development API -
-
-
-
- Basic Usage -
- - {``} - -
-
-
- With Cloud Native Pattern -
- - {``} - -
-
-
-
-
- )} - - {/* Speaker Directory */} - {displaySpeakers.length >= 8 && ( -
-
-

- Speaker Directory -

-

- Comprehensive speaker listing for conference programs and - attendee guides. Maximizes information density while - maintaining readability. -

-
- -
- {displaySpeakers.slice(0, 10).map((speaker) => ( - - ))} -
-
- )} - - {/* Mixed Layout Example */} - {displaySpeakers.length >= 6 && ( -
-
-

- Mixed Layout -

-

- Combines different speaker presentation styles for dynamic, - engaging layouts. Perfect for conference websites that need - visual variety and clear hierarchy. -

-
- -
- {/* Featured speaker at top */} -
- -
- - {/* Three card speakers */} -
- {displaySpeakers.slice(1, 4).map((speaker) => ( - - ))} -
- - {/* Compact list for additional speakers */} -
- {displaySpeakers.slice(4, 10).map((speaker) => ( - - ))} -
-
-
- )} - - {/* Design Guidelines */} -
-

- Speaker Display Guidelines -

- -
-
-

- Layout Recommendations -

-
    -
  • - - - Featured Layout: Compact yet impactful - design for keynote speakers and main announcements - -
  • -
  • - - - 3-Speaker Grid: Perfect for homepage - highlights and featured speaker sections - -
  • -
  • - - - 6-Speaker Grid: Ideal for complete - conference lineups and speaker pages - -
  • -
  • - - - Compact Format: Use for agenda pages - and speaker directories - -
  • -
  • - - - Speaker Share Images: Branded 4:5 ratio - images for speakers to share “I’m speaking - at [event]” with QR codes - -
  • -
  • - - - Speaker Spotlight Images:{' '} - Conference-branded 4:5 ratio promotional images with QR - codes for speaker promotion - -
  • -
-
- -
-

- Content Hierarchy -

-
    -
  • - - - Name: Primary focus with largest text - size - -
  • -
  • - - - Title & Company: Secondary information - for context - -
  • -
  • - - - Talk Information: Shows format badges - and talk titles when available - -
  • -
  • - - - Biography: Included in featured and - card layouts for depth - -
  • -
  • - - - Keynote Badge: Special highlighting for - keynote speakers - -
  • -
-
-
- -
-

- QR Code Integration -

-
-
    -
  • • Automatically generated for social image variants
  • -
  • • Links to full speaker profile and talk details
  • -
  • • High contrast design for reliable scanning
  • -
  • • Optimized size for mobile camera scanning
  • -
-
    -
  • • White background with dark QR pattern
  • -
  • • Rounded corners to match design language
  • -
  • • Positioned for easy access without UI overlap
  • -
  • - • Error correction level ensures scanning reliability -
  • -
-
-
- -
-

- Accessibility & Performance -

-
-
    -
  • • High contrast ratios for all text elements
  • -
  • • Keyboard navigation support throughout
  • -
  • • Screen reader optimized alt text and labels
  • -
  • • Focus indicators on all interactive elements
  • -
-
    -
  • • Optimized images with proper sizing
  • -
  • • Lazy loading for large speaker grids
  • -
  • • Responsive layouts for all screen sizes
  • -
  • • Progressive enhancement for slower connections
  • -
-
-
-
-
-
-
- - {/* Talk Examples */} -
- -
-

- Talk Examples -

-

- Talk cards and promotions showcase conference presentations with - format-specific styling and clear visual hierarchy. -

-
- -
- {/* Talk Card Examples */} -
-

- Talk Card Examples -

-

- Talk cards showcase conference presentations with - format-specific styling and branding elements. The - TalkPromotionCard component features modular architecture with - guaranteed footer positioning. -

- -
- {/* Card Variants */} -
-

- Card Variants -

-
- - - - - -
-
- - {/* Format Showcase */} -
-

- Talk Format Showcase -

-
- - - - - - - -
-
-
-
- - {/* Talk Promotion Examples */} -
-

- Talk Promotion Examples -

-

- Promotional components for highlighting featured talks and - driving engagement. -

- -
- {/* Banner Promotion */} -
-

- Banner Promotion -

- -
- - {/* Card and Social Promotions */} -
-

- Card & Social Promotions -

-
- - - - - -
-
-
-
- - {/* Component Features and Migration Guide */} -
-

- Component Features & Architecture -

-

- The TalkPromotionCard component features a modular - header-body-footer architecture with guaranteed footer - positioning using flexbox. This component provides improved - maintainability and consistent styling across all variants. -

- -
- {/* Component Features */} -
-

- Component Features -

-
-
-
- Architecture Improvements -
-
    -
  • - - - Modular Structure: Separated - TalkHeader, TalkBody, and TalkFooter components for - better maintainability - -
  • -
  • - - - Guaranteed Footer Positioning: Uses - flexbox with{' '} - - mt-auto - {' '} - for perfect footer alignment - -
  • -
  • - - - Grid Integration: Works seamlessly - with{' '} - - auto-rows-fr - {' '} - for equal height cards - -
  • -
  • - - - TypeScript Support: Full type - safety with comprehensive prop interfaces - -
  • -
-
-
-
- Variant System -
-
    -
  • - - - Default: Balanced presentation with - full talk information and description - -
  • -
  • - - - Featured: Enhanced styling with - larger text and prominent visual treatment - -
  • -
  • - - - Compact: Space-efficient design for - listings and dense grids - -
  • -
  • - - - Consistent Styling: All variants - maintain footer alignment and responsive behavior - -
  • -
-
-
- -
-
- Migration Benefits -
-
-
-
- Better Maintainability -
-

- Separated concerns make it easier to update styling, - add features, and fix bugs -

-
-
-
- Consistent Layout -
-

- Flexbox architecture ensures footers always align - properly regardless of content length -

-
-
-
- Future-Proof Design -
-

- Modular structure allows for easy extension and - customization as requirements evolve -

-
-
-
-
-
-
- - {/* Alert Example */} -
-

- Alert/Notice Example -

-
-
- -
-

- Early Bird Special Ending Soon! -

-

- Register before January 31st to secure your spot at 40% - off the regular price. Limited seats available for this - community-driven event. -

-
-
-
-
-
-
-
- - {/* Call to Action Examples */} -
- -
-

- Call to Action Examples -

-

- Call to action components drive engagement and conversions across - the conference website. These reusable components can be - customized for different contexts while maintaining consistent - branding and accessibility standards. -

-
- -
- {/* Standard Call to Action */} -
-

- Standard Call to Action -

-

- The default configuration encourages both speaker submissions - and ticket reservations with balanced messaging. -

-
- -
-
- - {/* Organizers Context */} -
-

- Organizers Context -

-

- When used in organizer-facing contexts, the messaging and button - styling adapt to focus on community engagement. -

-
- -
-
- - {/* Speaker Focus */} -
-

- Speaker Submission Focus -

-

- Configuration that emphasizes speaker submissions while hiding - ticket reservations for CFP-focused pages. -

-
- -
-
- - {/* Ticket Focus */} -
-

- Ticket Reservation Focus -

-

- Configuration that focuses solely on ticket sales when the CFP - period has ended. -

-
- -
-
- - {/* Custom Messaging */} -
-

- Custom Messaging -

-

- Fully customizable title and description for specific campaigns - or landing pages. -

-
- -
-
- - {/* Component Documentation */} -
-

- Component Features & Usage -

-
-
-

- Customizable Props -

-
    -
  • - - - - isOrganizers - {' '} - - Changes messaging and button styles - -
  • -
  • - - - - title - {' '} - - Custom headline text - -
  • -
  • - - - - description - {' '} - - Custom description text - -
  • -
  • - - - - showSpeakerSubmission - {' '} - - Toggle CFP button - -
  • -
  • - - - - showTicketReservation - {' '} - - Toggle ticket button - -
  • -
-
-
-

- Design Features -

-
    -
  • - - Gradient background with brand colors -
  • -
  • - - - Responsive button layout (stacked to horizontal) - -
  • -
  • - - Accessible ARIA labels and semantic markup -
  • -
  • - - Icons from Heroicons for visual clarity -
  • -
  • - - Conditional urgency messaging -
  • -
-
-
- -
-

- Usage Guidelines -

-
-
-
- Placement -
-

- Use at natural break points in content flow, typically - after speaker showcases or information sections. -

-
-
-
- Frequency -
-

- Limit to 1-2 instances per page to avoid overwhelming - users while maintaining conversion opportunities. -

-
-
-
- Context -
-

- Adapt button visibility and messaging based on page - purpose (CFP pages vs. general conference info). -

-
-
-
-
-
-
-
- - {/* Email Templates */} -
- -
-

- Email Templates -

-

- Professional email templates for all conference communications. - These templates maintain consistent branding, provide clear - communication, and ensure accessibility across different email - clients. Our template system includes automated proposal - responses, speaker communications, and community updates. -

-
- - {/* Template Architecture Overview */} -
-

- Template Architecture -

-
-
-
-
- - 1 - -
-

- Base Template -

-

- Foundation layout with consistent branding, responsive - design, and email client compatibility. -

-
-
-
- - 2 - -
-

- Specialized Templates -

-

- Purpose-built templates for proposals, speaker - communications, and community broadcasts. -

-
-
-
- - 3 - -
-

- Automated System -

-

- Integrated with Resend service for reliable delivery, rate - limiting, and audience management. -

-
-
-
-
- - {/* Proposal Response Templates */} -
-

- Proposal Response Templates -

-

- Automated responses for Call for Papers submissions with - appropriate tone and clear next steps for both accepted and - rejected proposals. -

- -
- {/* Proposal Acceptance Email */} - - - - - {/* Proposal Rejection Email */} - - - -
-
- - {/* Speaker Communication Templates */} -
-

- Speaker Communication Templates -

-

- Direct communication templates for speaker outreach and - speaker-specific broadcasts with rich content support. -

- -
- {/* Speaker Email */} - - - {/* Rich PortableText Content Example */} -
-

- Dear Demo Testson, Sample McExample, and Fictitious - Speaker, -

- -

- Since you're presenting together, we wanted to - share some{' '} - - important guidelines - {' '} - for coordinating your presentation. -

- -

- đź“‹ Key Points to Consider -

- -
    -
  • - Please coordinate who will handle which sections -
  • -
  • - Ensure your combined presentation fits within the - allocated{' '} - - 45-minute slot - {' '} - including Q&A -
  • -
  • - Practice smooth transitions between speakers -
  • -
- -

- We've also arranged for a{' '} - - shared rehearsal space - {' '} - the day before the conference. -

- -

- ⚙️ Technical Requirements -

- -
    -
  1. - Each speaker should test their laptop with our AV - equipment -
  2. -
  3. - Have backup slides on a USB drive -
  4. -
  5. - Consider using a shared slide deck for seamless - transitions -
  6. -
- -

- Looking forward to your presentation! -

- -

- Best regards, -
- Conference Team -

-
- - {/* Proposal Section */} -
-

- Your Proposal -

-

- Building Cloud Native Applications with - Microservices -

-

- Submitted for {conference.title} -

-
-

- All speakers: -

-
    -
  • - Demo Testson - (demo.testson@fictional-examples.com) -
  • -
  • - Sample McExample - (sample.mcexample@demo-samples.com) -
  • -
  • - Fictitious Speaker - (fictitious.speaker@example-demo.com) -
  • -
-
-
- - {/* Action Button */} - - - ), - }} - /> -
- - {/* Speaker Broadcast Email */} - - -
-
- } - /> -
-
-
- - {/* Co-Speaker Templates */} -
-

- 🚀 Pair Programming for Presentations -

-

- Just like scaling microservices, great talks scale better with - collaboration! Our co-speaker invitation system orchestrates - seamless partnerships between speakers, enabling distributed - expertise and fault-tolerant presentations. Deploy these templates - to invite, coordinate, and celebrate speaker collaborations. -

- -
- {/* Co-Speaker Invitation Email */} - - - - - {/* Co-Speaker Response Email - Accepted */} - - - - - {/* Co-Speaker Response Declined Example */} - - - -
- - {/* Co-Speaker Template Guidelines */} -
-

- Co-Speaker Template Guidelines -

-
-
-
- Security & Token Management -
-
    -
  • - - - Secure Tokens: HMAC-SHA256 signed - tokens with 14-day expiration for invitation security - -
  • -
  • - - - Email Verification: Case-insensitive - email matching ensures invitations reach the correct - recipient - -
  • -
  • - - - One-Time Use: Tokens become invalid - after response to prevent replay attacks - -
  • -
  • - - - Test Mode: Development environment - supports testing without sending actual emails - -
  • -
-
- -
-
- User Experience Design -
-
    -
  • - - - Clear Context: Invitations include full - proposal details and inviter information - -
  • -
  • - - - Expiration Awareness: Real-time - expiration checking with countdown displays - -
  • -
  • - - - Response Feedback: Immediate - confirmation and next steps for both parties - -
  • -
  • - - - Mobile Optimized: Responsive design - ensures accessibility across all devices - -
  • -
-
-
- -
-
- Integration with Existing Systems -
-
-
-
- Authentication -
-

- Integrates with NextAuth.js for LinkedIn and GitHub OAuth2 - authentication flows -

-
-
-
- Sanity CMS -
-

- Co-speaker invitations stored as documents with full audit - trail and status tracking -

-
-
-
- Email Service -
-

- Powered by Resend with retry logic, rate limiting, and - delivery tracking -

-
-
-
-
-
- - {/* General Communication Templates */} -
-

- General Communication Templates -

-

- Flexible templates for community announcements and general - broadcasts with customizable content and unsubscribe management. -

- -
- {/* Broadcast Email Template */} - - -

- We're thrilled to announce that early bird tickets - for {conference.title} are now available! -

- -

- 🎟️ Ticket Information -

-
    -
  • - Early Bird Price: 299 NOK (Regular: - 499 NOK) -
  • -
  • - Available Until: March 31st, 2025 -
  • -
  • - Includes: Full conference access, - lunch, and networking reception -
  • -
- -

- 🎤 Confirmed Speakers -

-

- We have an amazing lineup including experts from Google, - Microsoft, and the Cloud Native Computing Foundation. -

- - -
- } - /> - - - {/* Base Email Template Preview */} - - -

- Thank you for joining our cloud native community! This is - the foundation template that provides consistent branding - and structure for all our email communications. -

-

- This template includes responsive design, social links, - event details, and accessibility features that work across - all major email clients. -

-
-
-
- - - {/* Template Comparison Grid */} -
-

- Template Feature Comparison -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Template - - - - Purpose - - - - Key Features - - - - Automation - -
- - Base Template - - - - Foundation for all emails - - -
    -
  • Responsive layout
  • -
  • Brand consistency
  • -
  • Social links
  • -
  • Event details
  • -
-
- - Manual - -
- - Proposal Accept - - - - CFP acceptance notifications - - -
    -
  • Celebration tone
  • -
  • Confirmation button
  • -
  • Next steps
  • -
  • Organizer comments
  • -
-
- - Automated - -
- - Proposal Reject - - - - CFP rejection notifications - - -
    -
  • Professional tone
  • -
  • Constructive feedback
  • -
  • Future opportunities
  • -
  • Community connection
  • -
-
- - Automated - -
- - Single Speaker - - - - Individual speaker outreach - - -
    -
  • Personal messaging
  • -
  • Proposal context
  • -
  • Direct communication
  • -
  • Action buttons
  • -
-
- - Admin Tool - -
- - Multi-Speaker - - - - CC all speakers on proposal - - -
    -
  • CC all participants
  • -
  • Shared context
  • -
  • Collaboration focused
  • -
  • Speaker list display
  • -
-
- - Admin Tool - -
- - Speaker Broadcast - - - - Speaker group communications - - -
    -
  • Rich content support
  • -
  • Group messaging
  • -
  • Speaker-specific info
  • -
  • Event updates
  • -
-
- - Admin Tool - -
- - General Broadcast - - - - Community announcements - - -
    -
  • HTML content support
  • -
  • Unsubscribe management
  • -
  • General audience
  • -
  • Marketing campaigns
  • -
-
- - Admin Tool - -
- - Co-Speaker Invitation - - - - Invite speakers to collaborate - - -
    -
  • Secure token system
  • -
  • Talk context details
  • -
  • Accept/decline options
  • -
  • Professional tone
  • -
-
- - Automated - -
- - Co-Speaker Response - - - - Notify of invitation response - - -
    -
  • Accept/decline status
  • -
  • Professional language
  • -
  • Next steps guidance
  • -
  • Relationship preservation
  • -
-
- - Automated - -
-
-
- - {/* Technical Implementation */} -
-

- Technical Implementation -

-
-
-

- Email Service Integration -

-
    -
  • • Resend service for reliable delivery
  • -
  • • Rate limiting and retry logic
  • -
  • • Audience management and segmentation
  • -
  • • Bounce and unsubscribe handling
  • -
  • • Template validation and testing
  • -
-
-
-

- Design Standards -

-
    -
  • • Responsive table-based layouts
  • -
  • • Email client compatibility testing
  • -
  • • Accessible color contrast ratios
  • -
  • • Consistent typography hierarchy
  • -
  • • Brand-aligned visual elements
  • -
-
-
- -
-

- Development Guidelines -

-
-
-
- Component Architecture -
-

- Modular React components with TypeScript props for type - safety and reusability across different email contexts. -

-
-
-
- Testing Strategy -
-

- Comprehensive testing across email clients, accessibility - validation, and content rendering verification. -

-
-
-
- Maintenance -
-

- Centralized configuration, shared components, and - documentation for consistent updates and brand evolution. -

-
-
-
-
-
-
- - ) -} - -export default async function BrandingPage() { - const headersList = await headers() - const domain = headersList.get('host') || '' - - return -} diff --git a/src/app/(main)/sponsor/onboarding/[token]/page.tsx b/src/app/(main)/sponsor/onboarding/[token]/page.tsx new file mode 100644 index 00000000..808e240e --- /dev/null +++ b/src/app/(main)/sponsor/onboarding/[token]/page.tsx @@ -0,0 +1,17 @@ +import { SponsorOnboardingForm } from '@/components/sponsor/SponsorOnboardingForm' + +interface OnboardingPageProps { + params: Promise<{ token: string }> +} + +export default async function SponsorOnboardingPage({ + params, +}: OnboardingPageProps) { + const { token } = await params + + return ( +
+ +
+ ) +} diff --git a/src/app/(main)/sponsor/terms/page.tsx b/src/app/(main)/sponsor/terms/page.tsx new file mode 100644 index 00000000..0d9abb82 --- /dev/null +++ b/src/app/(main)/sponsor/terms/page.tsx @@ -0,0 +1,113 @@ +import { Container } from '@/components/Container' +import { Button } from '@/components/Button' +import { getConferenceForDomain } from '@/lib/conference/sanity' +import { getTermsForConference } from '@/lib/sponsor-crm/contract-templates' +import { PortableText } from '@portabletext/react' +import { portableTextComponents } from '@/lib/portabletext/components' +import { cacheLife, cacheTag } from 'next/cache' +import { headers } from 'next/headers' + +export const metadata = { + title: 'Sponsorship Terms & Conditions - Cloud Native Days Norway', + description: + 'General terms and conditions for sponsorship of Cloud Native Days Norway', +} + +async function CachedTermsContent({ domain }: { domain: string }) { + 'use cache' + cacheLife('days') + cacheTag('content:sponsor-terms') + + const { conference, error: confError } = await getConferenceForDomain(domain) + + if (confError || !conference) { + return ( +
+

+ Unable to load terms +

+

+ We're experiencing technical difficulties. Please try again + later. +

+ +
+ ) + } + + const { + terms, + conferenceName, + error: termsError, + } = await getTermsForConference(conference._id) + + if (termsError || !terms) { + return ( +
+ +
+

+ Sponsorship Terms & Conditions +

+

+ Terms and conditions for {conference.title} sponsorship are not + yet available. Please contact us at{' '} + + {conference.sponsorEmail} + {' '} + for more information. +

+
+ +
+
+
+
+ ) + } + + return ( +
+ +
+

+ General Terms & Conditions +

+

+ {conferenceName || conference.title} Sponsorship Agreement +

+ +
+ +
+ +
+

+ For questions about these terms, please contact{' '} + + {conference.sponsorEmail} + +

+
+
+
+
+ ) +} + +export default async function SponsorTermsPage() { + const headersList = await headers() + const domain = headersList.get('host') || '' + + return +} diff --git a/src/components/BackLink.stories.tsx b/src/components/BackLink.stories.tsx new file mode 100644 index 00000000..71f0e63f --- /dev/null +++ b/src/components/BackLink.stories.tsx @@ -0,0 +1,91 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { BackLink } from './BackButton' + +const meta = { + title: 'Components/Layout/BackLink', + component: BackLink, + parameters: { + layout: 'padded', + docs: { + description: { + component: + 'A smart back navigation component that uses browser history when available, with a fallback URL. Available in link and button variants.', + }, + }, + }, + tags: ['autodocs'], + argTypes: { + variant: { + control: 'select', + options: ['link', 'button'], + description: 'Visual style - link (minimal) or button (prominent)', + }, + fallbackUrl: { + control: 'text', + description: 'URL to navigate to if no browser history exists', + }, + children: { + control: 'text', + description: 'Button text content', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Link: Story = { + args: { + variant: 'link', + children: 'Back', + fallbackUrl: '/', + }, +} + +export const Button: Story = { + args: { + variant: 'button', + children: 'Back', + fallbackUrl: '/', + }, +} + +export const CustomText: Story = { + args: { + variant: 'link', + children: 'Return to speakers', + fallbackUrl: '/speakers', + }, +} + +export const InContext: Story = { + render: () => ( +
+
+ + Back to speakers + +

+ Speaker Detail Page +

+

+ The back link navigates using browser history when available. +

+
+ +
+
+

+ Form Page +

+ + Cancel + +
+

+ Button variant is more prominent for actions like cancel. +

+
+
+ ), +} diff --git a/src/components/Button.stories.tsx b/src/components/Button.stories.tsx new file mode 100644 index 00000000..76ab5075 --- /dev/null +++ b/src/components/Button.stories.tsx @@ -0,0 +1,232 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { expect, fn, userEvent, within } from 'storybook/test' +import { Button } from './Button' +import { CalendarIcon, UserIcon } from '@heroicons/react/24/outline' + +const meta = { + title: 'Components/Layout/Button', + component: Button, + parameters: { + layout: 'padded', + docs: { + description: { + component: + 'Consistent, accessible button system with clear visual hierarchy. Supports multiple variants (primary, secondary, success, warning, info, outline, icon) following the brand color system. Available in four sizes (sm, md, lg, icon) with loading states and icon support. Maintains WCAG 2.1 AA compliance with proper focus states.', + }, + }, + }, + tags: ['autodocs'], + argTypes: { + variant: { + control: 'select', + options: [ + 'primary', + 'secondary', + 'success', + 'warning', + 'info', + 'outline', + 'icon', + ], + description: 'Visual style variant following brand color system', + }, + size: { + control: 'select', + options: ['sm', 'md', 'lg', 'icon'], + description: 'Button size - use "icon" size for icon-only buttons', + }, + disabled: { + control: 'boolean', + description: 'Disable button interaction', + }, + children: { + control: 'text', + description: 'Button content - text or JSX elements', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Primary: Story = { + args: { + variant: 'primary', + children: 'Primary Button', + }, +} + +export const Secondary: Story = { + args: { + variant: 'secondary', + children: 'Secondary Button', + }, +} + +export const Success: Story = { + args: { + variant: 'success', + children: 'Success Button', + }, +} + +export const Warning: Story = { + args: { + variant: 'warning', + children: 'Warning Button', + }, +} + +export const Info: Story = { + args: { + variant: 'info', + children: 'Info Button', + }, +} + +export const Outline: Story = { + args: { + variant: 'outline', + children: 'Outline Button', + }, +} + +export const WithIcon: Story = { + args: { + variant: 'primary', + children: ( + <> + + Schedule Event + + ), + }, +} + +export const IconOnly: Story = { + args: { + variant: 'icon', + size: 'icon', + children: , + }, +} + +export const Small: Story = { + args: { + variant: 'primary', + size: 'sm', + children: 'Small Button', + }, +} + +export const Medium: Story = { + args: { + variant: 'primary', + size: 'md', + children: 'Medium Button', + }, +} + +export const Large: Story = { + args: { + variant: 'primary', + size: 'lg', + children: 'Large Button', + }, +} + +export const Disabled: Story = { + args: { + variant: 'primary', + disabled: true, + children: 'Disabled Button', + }, +} + +// Interaction Tests +export const ClickInteraction: Story = { + args: { + variant: 'primary', + children: 'Click Me', + onClick: fn(), + }, + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement) + const button = canvas.getByRole('button', { name: /click me/i }) + + // Test button is visible and enabled + await expect(button).toBeVisible() + await expect(button).toBeEnabled() + + // Test click interaction + await userEvent.click(button) + await expect(args.onClick).toHaveBeenCalledTimes(1) + + // Test double click + await userEvent.click(button) + await expect(args.onClick).toHaveBeenCalledTimes(2) + }, +} + +export const DisabledInteraction: Story = { + args: { + variant: 'primary', + disabled: true, + children: 'Disabled Button', + onClick: fn(), + }, + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement) + const button = canvas.getByRole('button', { name: /disabled button/i }) + + // Test button is visible but disabled + await expect(button).toBeVisible() + await expect(button).toBeDisabled() + + // Attempt to click - should not trigger onClick + await userEvent.click(button) + await expect(args.onClick).not.toHaveBeenCalled() + }, +} + +export const KeyboardNavigation: Story = { + args: { + variant: 'primary', + children: 'Press Enter', + onClick: fn(), + }, + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement) + const button = canvas.getByRole('button', { name: /press enter/i }) + + // Focus the button + button.focus() + await expect(button).toHaveFocus() + + // Press Enter to activate + await userEvent.keyboard('{Enter}') + await expect(args.onClick).toHaveBeenCalledTimes(1) + + // Press Space to activate + await userEvent.keyboard(' ') + await expect(args.onClick).toHaveBeenCalledTimes(2) + }, +} + +export const HoverState: Story = { + args: { + variant: 'primary', + children: 'Hover Over Me', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + const button = canvas.getByRole('button', { name: /hover over me/i }) + + // Test hover interaction (visual change verified by Chromatic) + await userEvent.hover(button) + await expect(button).toBeVisible() + + await userEvent.unhover(button) + await expect(button).toBeVisible() + }, +} diff --git a/src/components/ClickableSpeakerNames.stories.tsx b/src/components/ClickableSpeakerNames.stories.tsx new file mode 100644 index 00000000..b67381db --- /dev/null +++ b/src/components/ClickableSpeakerNames.stories.tsx @@ -0,0 +1,225 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { ClickableSpeakerNames } from './ClickableSpeakerNames' +import { Speaker, Flags } from '@/lib/speaker/types' + +const mockSpeakers: Speaker[] = [ + { + _id: 'speaker-1', + _rev: '1', + _createdAt: '2024-01-01T00:00:00Z', + _updatedAt: '2024-01-01T00:00:00Z', + name: 'Alice Johnson', + email: 'alice@example.com', + slug: 'alice-johnson', + title: 'Senior Engineer at Google', + flags: [Flags.localSpeaker], + }, + { + _id: 'speaker-2', + _rev: '1', + _createdAt: '2024-01-01T00:00:00Z', + _updatedAt: '2024-01-01T00:00:00Z', + name: 'Bob Smith', + email: 'bob@example.com', + slug: 'bob-smith', + title: 'DevOps Lead at Microsoft', + flags: [Flags.firstTimeSpeaker], + }, + { + _id: 'speaker-3', + _rev: '1', + _createdAt: '2024-01-01T00:00:00Z', + _updatedAt: '2024-01-01T00:00:00Z', + name: 'Carol Williams', + email: 'carol@example.com', + slug: 'carol-williams', + title: 'Platform Architect at AWS', + flags: [], + }, + { + _id: 'speaker-4', + _rev: '1', + _createdAt: '2024-01-01T00:00:00Z', + _updatedAt: '2024-01-01T00:00:00Z', + name: 'David Chen', + email: 'david@example.com', + slug: 'david-chen', + title: 'CTO at Startup Inc', + flags: [Flags.diverseSpeaker], + }, +] + +const meta = { + title: 'Systems/Speakers/ClickableSpeakerNames', + component: ClickableSpeakerNames, + parameters: { + layout: 'padded', + docs: { + description: { + component: + 'Renders speaker names as clickable links to their profile pages. Handles multiple speakers with proper separators (commas and ampersands). Supports first name only mode for compact displays and a maxVisible option to limit displayed names.', + }, + }, + }, + tags: ['autodocs'], + decorators: [ + (Story: React.ComponentType) => ( +
+ +
+ ), + ], + argTypes: { + showFirstNameOnly: { + control: 'boolean', + description: 'Show only first names when multiple speakers', + }, + maxVisible: { + control: { type: 'number', min: 1, max: 10 }, + description: 'Maximum number of names to display', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const SingleSpeaker: Story = { + args: { + speakers: [mockSpeakers[0]], + }, +} + +export const TwoSpeakers: Story = { + args: { + speakers: mockSpeakers.slice(0, 2), + }, + parameters: { + docs: { + description: { + story: 'Two speakers are joined with an ampersand (&).', + }, + }, + }, +} + +export const ThreeSpeakers: Story = { + args: { + speakers: mockSpeakers.slice(0, 3), + }, + parameters: { + docs: { + description: { + story: + 'Multiple speakers use commas with an ampersand before the last name.', + }, + }, + }, +} + +export const ManySpeakers: Story = { + args: { + speakers: mockSpeakers, + }, +} + +export const FirstNameOnly: Story = { + args: { + speakers: mockSpeakers.slice(0, 3), + showFirstNameOnly: true, + }, + parameters: { + docs: { + description: { + story: + 'Useful for compact displays where full names would be too long.', + }, + }, + }, +} + +export const MaxVisible: Story = { + args: { + speakers: mockSpeakers, + maxVisible: 2, + }, + parameters: { + docs: { + description: { + story: 'Limits displayed names and shows "+N more" for remaining.', + }, + }, + }, +} + +export const CustomStyling: Story = { + args: { + speakers: mockSpeakers.slice(0, 2), + className: 'text-lg font-semibold', + linkClassName: + 'text-brand-cloud-blue hover:text-brand-cloud-blue/80 hover:underline', + separatorClassName: 'text-gray-400', + }, + parameters: { + docs: { + description: { + story: 'Custom CSS classes can be applied to links and separators.', + }, + }, + }, +} + +export const InContext: Story = { + args: { + speakers: mockSpeakers, + }, + render: () => ( +
+
+

+ In a talk card: +

+
+ +
+
+ +
+

+ Compact mode (first names only): +

+
+ +
+
+ +
+

+ With maxVisible limit: +

+
+ +
+
+
+ ), + parameters: { + docs: { + description: { + story: + 'Examples of how ClickableSpeakerNames looks in various contexts.', + }, + }, + }, +} diff --git a/src/components/CollapsibleDescription.stories.tsx b/src/components/CollapsibleDescription.stories.tsx new file mode 100644 index 00000000..d3babf6a --- /dev/null +++ b/src/components/CollapsibleDescription.stories.tsx @@ -0,0 +1,116 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { CollapsibleDescription } from './CollapsibleDescription' + +const meta = { + title: 'Components/Data Display/CollapsibleDescription', + component: CollapsibleDescription, + parameters: { + layout: 'padded', + }, + tags: ['autodocs'], + argTypes: { + paragraphs: { + control: 'object', + description: 'Array of paragraph strings to display', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const SingleParagraph: Story = { + args: { + paragraphs: [ + 'Cloud Native Days Norway is a premier conference for the cloud native community in the Nordic region. We bring together developers, operators, and business leaders to share knowledge and best practices.', + ], + }, +} + +export const TwoParagraphs: Story = { + args: { + paragraphs: [ + 'Cloud Native Days Norway is a premier conference for the cloud native community in the Nordic region.', + 'Join us for two days of inspiring talks, hands-on workshops, and networking opportunities with industry experts.', + ], + }, +} + +export const MultipleParagraphs: Story = { + args: { + paragraphs: [ + 'Cloud Native Days Norway is a premier conference for the cloud native community in the Nordic region. We bring together developers, operators, and business leaders to share knowledge and best practices.', + 'Our conference features world-class speakers from leading technology companies, sharing insights on Kubernetes, containers, serverless, and more.', + 'Whether you are just getting started with cloud native technologies or you are a seasoned expert, there is something for everyone at Cloud Native Days Norway.', + 'Join us and be part of the movement that is transforming how we build and deploy software across the globe.', + ], + }, +} + +export const Documentation: Story = { + args: { + paragraphs: ['Demo paragraph'], + }, + render: () => ( +
+

+ CollapsibleDescription Component +

+

+ A responsive description component that shows only the first paragraph + on mobile with a "Show more" button. On desktop, all + paragraphs are visible by default. +

+ +
+

+ Responsive Behavior +

+
    +
  • + Mobile: Shows first paragraph, others hidden behind + "Show more" +
  • +
  • + Desktop (sm+): All paragraphs visible, no toggle + needed +
  • +
  • + Single paragraph: No toggle shown, content always + visible +
  • +
+
+ +
+

+ Example - Single Paragraph +

+
+ +
+
+ +
+

+ Example - Multiple Paragraphs +

+
+
+

+ Resize your browser to see the mobile "Show more" button + appear. +

+
+
+ ), +} diff --git a/src/components/Container.stories.tsx b/src/components/Container.stories.tsx new file mode 100644 index 00000000..7e8ef649 --- /dev/null +++ b/src/components/Container.stories.tsx @@ -0,0 +1,60 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { Container } from './Container' + +const meta = { + title: 'Components/Layout/Container', + component: Container, + parameters: { + layout: 'fullscreen', + docs: { + description: { + component: + 'A responsive container component that centers content and applies consistent horizontal padding. Uses max-w-7xl (1280px) with responsive padding.', + }, + }, + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + children: ( +
+

+ Content is centered with max-w-7xl and responsive padding +

+
+ ), + }, +} + +export const WithMultipleSections: Story = { + args: { + children: ( +
+
Section 1
+
Section 2
+
+ Section 3 +
+
+ ), + }, +} + +export const FullWidthBackground: Story = { + render: () => ( +
+ +
+

+ Container constrains content while background extends full-width +

+
+
+
+ ), +} diff --git a/src/components/Form.stories.tsx b/src/components/Form.stories.tsx new file mode 100644 index 00000000..643494e3 --- /dev/null +++ b/src/components/Form.stories.tsx @@ -0,0 +1,380 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { fn } from 'storybook/test' +import { useState } from 'react' +import { + Input, + LinkInput, + ErrorText, + HelpText, + Textarea, + Dropdown, + Checkbox, + Multiselect, +} from './Form' + +const meta = { + title: 'Components/Forms/Form Elements', + tags: ['autodocs'], + parameters: { + layout: 'padded', + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta +type Story = StoryObj + +// --- Input --- + +export const InputDefault: Story = { + name: 'Input', + render: () => { + const Wrapper = () => { + const [value, setValue] = useState('John Doe') + return ( + + ) + } + return + }, +} + +export const InputEmpty: Story = { + name: 'Input (Empty)', + render: () => { + const Wrapper = () => { + const [value, setValue] = useState('') + return ( + + ) + } + return + }, +} + +export const InputReadOnly: Story = { + name: 'Input (Read Only)', + render: () => , +} + +// --- Textarea --- + +export const TextareaDefault: Story = { + name: 'Textarea', + render: () => { + const Wrapper = () => { + const [value, setValue] = useState( + 'A talk about building resilient microservices with Kubernetes.', + ) + return ( +