After users pick their persona (role), they now land on a new Brand step that collects the same fields available in Settings → Workspace → Brand: website, description, tone, voice notes, and content language. When they continue, every AI-generated post for this workspace already has sensible defaults — before the user's first post is ever drafted. Flow: Role (persona) → Brand (new) → Connections → Subscription → Completed. A 'Skip for now' button on the brand step advances to Connections without touching the workspace (defaults stay at their seed values). Backend: - Setup enum gets a new Brand case slotted between Role and Connections with matching stepNumber updates. - OnboardingController::brand() renders the form pre-filled from the current workspace. storeBrand() validates via a new StoreBrandRequest form request and writes the fields onto the workspace, then advances setup. skipBrand() just advances. - storeRole() redirects to brand instead of account. enforceStep() knows how to redirect users whose setup is Brand. - Three new routes: GET /onboarding/brand, POST /onboarding/brand, POST /onboarding/brand/skip. Frontend: - New Brand.vue page mirrors the Settings brand form but inside the onboarding AuthLayout. Tone + language sit side by side, both selects take full width. Translations added to en, pt-BR, and es. - Wayfinder regenerated so the page can import storeBrand / skipBrand. Tests: - Renamed 'redirects to step2' → 'redirects to brand step' and assert new setup. - Added six new tests covering brand step auth, redirects, render, successful store, validation of tone and content_language, and skip. - Updated UserSetupTest for the new enum case + reshuffled step numbers.
26 lines
726 B
PHP
26 lines
726 B
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Requests\App\Onboarding;
|
|
|
|
use Illuminate\Foundation\Http\FormRequest;
|
|
|
|
class StoreBrandRequest extends FormRequest
|
|
{
|
|
public function authorize(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function rules(): array
|
|
{
|
|
return [
|
|
'brand_website' => ['nullable', 'url', 'max:255'],
|
|
'brand_description' => ['nullable', 'string', 'max:2000'],
|
|
'brand_tone' => ['required', 'string', 'in:professional,casual,friendly,bold,inspirational,humorous,educational'],
|
|
'brand_voice_notes' => ['nullable', 'string', 'max:2000'],
|
|
'content_language' => ['required', 'string', 'in:en,pt-BR,es'],
|
|
];
|
|
}
|
|
}
|