feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\App;
|
|
|
|
|
|
|
|
|
|
use App\Actions\Billing\StartSubscriptionCheckout;
|
|
|
|
|
use App\Enums\Plan\Slug;
|
2026-06-22 15:31:44 +00:00
|
|
|
use App\Enums\SocialAccount\Platform as SocialPlatform;
|
2026-06-25 23:49:04 +00:00
|
|
|
use App\Enums\User\Goal;
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
use App\Enums\User\Persona;
|
2026-07-18 19:07:11 +00:00
|
|
|
use App\Enums\User\ReferralSource;
|
2026-06-25 23:49:04 +00:00
|
|
|
use App\Http\Requests\App\Onboarding\StoreOnboardingGoalsRequest;
|
2026-07-18 19:07:11 +00:00
|
|
|
use App\Http\Requests\App\Onboarding\StoreOnboardingReferralSourceRequest;
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
use App\Http\Requests\App\Onboarding\StoreOnboardingRequest;
|
2026-06-22 15:31:44 +00:00
|
|
|
use App\Http\Resources\App\SocialAccountResource;
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
use App\Models\Account;
|
|
|
|
|
use App\Models\Plan;
|
|
|
|
|
use App\Services\PostHogService;
|
|
|
|
|
use Illuminate\Http\RedirectResponse;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Inertia\Inertia;
|
|
|
|
|
use Inertia\Response;
|
|
|
|
|
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
|
|
|
|
|
|
|
|
|
class OnboardingController extends Controller
|
|
|
|
|
{
|
|
|
|
|
public function index(Request $request): Response|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
|
|
|
|
|
if ($user->account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Inertia::render('onboarding/Index', [
|
2026-06-22 00:28:47 +00:00
|
|
|
'personas' => array_map(fn (Persona $persona): string => $persona->value, Persona::cases()),
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
'selected' => $user->persona?->value,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 15:31:44 +00:00
|
|
|
public function store(StoreOnboardingRequest $request, PostHogService $postHog): RedirectResponse
|
|
|
|
|
{
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
2026-06-22 12:26:31 +00:00
|
|
|
|
2026-06-22 15:31:44 +00:00
|
|
|
if ($user->account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
2026-06-22 12:26:31 +00:00
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
$persona = (string) $request->validated('persona');
|
|
|
|
|
|
|
|
|
|
$user->update(['persona' => $persona]);
|
|
|
|
|
|
|
|
|
|
$postHog->identify($user->id, [
|
|
|
|
|
'persona' => $persona,
|
|
|
|
|
]);
|
|
|
|
|
|
2026-06-25 23:49:04 +00:00
|
|
|
return redirect()->route('app.onboarding.goals');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function goals(Request $request): Response|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
|
|
|
|
|
if ($user->account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! $user->persona) {
|
|
|
|
|
return redirect()->route('app.onboarding');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Inertia::render('onboarding/Goals', [
|
|
|
|
|
'goals' => array_map(fn (Goal $goal): string => $goal->value, Goal::cases()),
|
|
|
|
|
'selected' => $user->goals ?? [],
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function storeGoals(StoreOnboardingGoalsRequest $request, PostHogService $postHog): RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
|
|
|
|
|
if ($user->account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! $user->persona) {
|
|
|
|
|
return redirect()->route('app.onboarding');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$goals = array_values($request->validated('goals'));
|
|
|
|
|
|
|
|
|
|
$user->update(['goals' => $goals]);
|
|
|
|
|
|
2026-06-26 00:06:27 +00:00
|
|
|
$postHog->identify($user->id, [
|
|
|
|
|
'goals' => $goals,
|
|
|
|
|
]);
|
2026-06-25 23:49:04 +00:00
|
|
|
|
2026-07-18 19:07:11 +00:00
|
|
|
return redirect()->route('app.onboarding.referral-source');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function referralSource(Request $request): Response|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
|
|
|
|
|
if ($user->account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! $user->persona) {
|
|
|
|
|
return redirect()->route('app.onboarding');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! $user->goals) {
|
|
|
|
|
return redirect()->route('app.onboarding.goals');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Inertia::render('onboarding/ReferralSource', [
|
|
|
|
|
'sources' => array_map(fn (ReferralSource $source): string => $source->value, ReferralSource::cases()),
|
|
|
|
|
'selected' => $user->referral_source?->value,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function storeReferralSource(StoreOnboardingReferralSourceRequest $request, PostHogService $postHog): RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
|
|
|
|
|
if ($user->account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! $user->persona) {
|
|
|
|
|
return redirect()->route('app.onboarding');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! $user->goals) {
|
|
|
|
|
return redirect()->route('app.onboarding.goals');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$referralSource = (string) $request->validated('referral_source');
|
|
|
|
|
|
|
|
|
|
$user->update(['referral_source' => $referralSource]);
|
|
|
|
|
|
|
|
|
|
$postHog->identify($user->id, [
|
|
|
|
|
'referral_source' => $referralSource,
|
|
|
|
|
]);
|
|
|
|
|
|
2026-06-22 15:31:44 +00:00
|
|
|
return redirect()->route('app.onboarding.connect');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function connect(Request $request): Response|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
|
|
|
|
|
if ($user->account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! $user->persona) {
|
|
|
|
|
return redirect()->route('app.onboarding');
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-25 23:49:04 +00:00
|
|
|
if (! $user->goals) {
|
|
|
|
|
return redirect()->route('app.onboarding.goals');
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 19:07:11 +00:00
|
|
|
if (! $user->referral_source) {
|
|
|
|
|
return redirect()->route('app.onboarding.referral-source');
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 15:31:44 +00:00
|
|
|
$workspace = $user->currentWorkspace;
|
|
|
|
|
|
|
|
|
|
if (! $workspace) {
|
|
|
|
|
return redirect()->route('app.workspaces.create');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$accounts = $workspace->socialAccounts()->orderBy('id')->get();
|
|
|
|
|
|
2026-06-25 00:23:27 +00:00
|
|
|
$platforms = collect(SocialPlatform::cases())
|
|
|
|
|
->filter(fn (SocialPlatform $platform): bool => $platform->isConnectable())
|
|
|
|
|
->map(fn (SocialPlatform $platform): array => [
|
|
|
|
|
'value' => $platform->value,
|
|
|
|
|
'label' => $platform->label(),
|
|
|
|
|
'color' => $platform->color(),
|
|
|
|
|
'network' => $platform->network(),
|
|
|
|
|
])->values();
|
2026-06-22 15:31:44 +00:00
|
|
|
|
2026-07-02 12:21:31 +00:00
|
|
|
$plan = Plan::where('slug', Slug::Workspace)->firstOrFail();
|
|
|
|
|
|
2026-06-22 15:31:44 +00:00
|
|
|
return Inertia::render('onboarding/Connect', [
|
|
|
|
|
'platforms' => $platforms,
|
|
|
|
|
'accounts' => SocialAccountResource::collection($accounts)->resolve(),
|
2026-07-02 12:21:31 +00:00
|
|
|
'plan' => [
|
|
|
|
|
'name' => $plan->name,
|
|
|
|
|
'interval' => 'monthly',
|
|
|
|
|
],
|
2026-06-22 15:31:44 +00:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function checkout(Request $request, StartSubscriptionCheckout $checkout): SymfonyResponse|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
if (config('trypost.self_hosted')) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $request->user();
|
|
|
|
|
$account = $user->account;
|
|
|
|
|
|
|
|
|
|
if ($account?->subscribed(Account::SUBSCRIPTION_NAME)) {
|
|
|
|
|
return redirect()->route('app.calendar');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$workspace = $user->currentWorkspace;
|
|
|
|
|
|
|
|
|
|
if (! $workspace || ! $workspace->socialAccounts()->exists()) {
|
|
|
|
|
return redirect()->route('app.onboarding.connect')
|
|
|
|
|
->with('flash.banner', __('onboarding.connect.must_connect'))
|
|
|
|
|
->with('flash.bannerStyle', 'danger');
|
|
|
|
|
}
|
|
|
|
|
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
$plan = Plan::where('slug', Slug::Workspace)->firstOrFail();
|
|
|
|
|
|
|
|
|
|
return $checkout->redirect(
|
|
|
|
|
$account,
|
|
|
|
|
(string) $plan->stripe_monthly_price_id,
|
2026-06-22 15:31:44 +00:00
|
|
|
route('app.onboarding.connect'),
|
feat: per-workspace pricing, onboarding, and billing overhaul
Pricing
- Bill per workspace ($12/mo or $120/yr each); Stripe quantity tracks the
workspace count and syncs on workspace create/delete.
- 2,500 AI credits per workspace, pooled at the account level; monthly reset
on the billing anniversary, annual granted upfront (no rollover).
- One social account per network per workspace; remove all count-based limits
(workspace/social/member) and the legacy plan tiers (single Workspace plan).
Onboarding (cloud only: SELF_HOSTED=false + PostHog)
- Replace the /subscribe plan picker with /onboarding persona selection
(Creator/Freelancer/Startup/Agency/Small business/Other), saved on the user
(users.persona) and mirrored to PostHog, then Stripe Checkout on the monthly
price. 8-day trial so Stripe displays 7.
Billing screen
- Remove the Change Plan dialog (dead with a single plan); add an annual-upgrade
banner for monthly subscribers (swapToYearly).
- Current-plan card shows the workspace count instead of the plan name.
System AI
- Brand analyzer / workspace autofill is always allowed and never debits credits
(system feature, not the user's usage).
Self-hosted (SELF_HOSTED=true) bypasses all billing, credit, limit, network,
and onboarding logic.
2026-06-21 23:40:03 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|