feat: module-driven onboarding system + simplified 3-step signup

Add OnboardingProviderProtocol so modules declare their own post-signup
onboarding steps. The core OnboardingAggregator discovers enabled
providers and exposes a dashboard API (GET /dashboard/onboarding).
A session-scoped banner on the store dashboard shows a checklist that
guides merchants through setup without blocking signup.

Signup is simplified from 4 steps to 3 (Plan → Account → Payment):
store creation is merged into account creation, store language is
captured from the user's browsing language, and platform-specific
template branching is removed.

Includes 47 unit and integration tests covering all new providers,
the aggregator, the API endpoint, and the signup service changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 23:39:42 +01:00
parent f8a2394da5
commit ef9ea29643
26 changed files with 2055 additions and 699 deletions

View File

@@ -337,6 +337,85 @@
{% endmacro %}
{# =============================================================================
Onboarding Banner
Shows a checklist of post-signup onboarding steps from all enabled modules.
Steps are fetched from the API, dismiss is session-scoped (sessionStorage).
Usage:
{{ onboarding_banner() }}
============================================================================= #}
{% macro onboarding_banner() %}
<div x-data="onboardingBanner()"
x-show="visible"
x-cloak
class="mb-4 bg-white dark:bg-gray-800 rounded-lg shadow-xs border border-gray-200 dark:border-gray-700 overflow-hidden">
{# Header with progress #}
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<span x-html="$icon('rocket', 'w-5 h-5 text-purple-600 dark:text-purple-400')"></span>
<h3 class="text-lg font-semibold text-gray-900 dark:text-white" x-text="t('onboarding.banner.title')">Get Started</h3>
<span class="text-sm text-gray-500 dark:text-gray-400"
x-text="`${completedSteps}/${totalSteps}`"></span>
</div>
<button @click="dismiss()"
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
:title="t('onboarding.banner.dismiss')">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
{# Progress bar #}
<div class="mt-3 w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div class="bg-purple-600 h-2 rounded-full transition-all duration-500"
:style="`width: ${progressPercentage}%`"></div>
</div>
</div>
{# Step checklist #}
<div class="divide-y divide-gray-100 dark:divide-gray-700">
<template x-for="step in steps" :key="step.key">
<a :href="step.route"
class="flex items-center px-6 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors group">
{# Completion indicator #}
<div class="flex-shrink-0 mr-4">
<template x-if="step.completed">
<div class="w-6 h-6 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center">
<svg class="w-4 h-4 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
</div>
</template>
<template x-if="!step.completed">
<div class="w-6 h-6 rounded-full border-2 border-gray-300 dark:border-gray-600 group-hover:border-purple-400 transition-colors"></div>
</template>
</div>
{# Step icon + text #}
<div class="flex-shrink-0 mr-3">
<span x-html="$icon(step.icon, 'w-5 h-5 text-gray-400 dark:text-gray-500')" class="block"></span>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 dark:text-white"
:class="{ 'line-through text-gray-400 dark:text-gray-500': step.completed }"
x-text="t(step.title_key)"></p>
<p class="text-xs text-gray-500 dark:text-gray-400 truncate"
x-text="t(step.description_key)"></p>
</div>
{# Arrow #}
<template x-if="!step.completed">
<svg class="w-4 h-4 text-gray-400 group-hover:text-purple-500 transition-colors flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</template>
</a>
</template>
</div>
</div>
{% endmacro %}
{# =============================================================================
Email Settings Warning
Shows warning banner when store email settings are not configured.