Tighten Care free tier and bill Pro/Enterprise per branch.
Deploy Ladill Care / deploy (push) Successful in 29s

Free keeps core records, appointments, and basic workflows; Pro unlocks lab, pharmacy, billing, and Queue. Both paid tiers bill per branch with unlimited branches. Enterprise adds multi-dept workflows, analytics, AI-assisted healthcare integration, and priority support (not Afia).
This commit is contained in:
isaacclad
2026-07-15 14:14:35 +00:00
parent 78be5c803d
commit f9aa4a679f
19 changed files with 414 additions and 193 deletions
+28 -18
View File
@@ -1,6 +1,7 @@
<x-app-layout title="Plans" heading="Care plans">
@php
$proPrice = number_format($proPriceMinor / 100, 0);
$proPerBranch = number_format($proPricePerBranchMinor / 100, 0);
$proTotal = number_format($proPriceMinor / 100, 0);
$enterprisePerBranch = number_format($enterprisePricePerBranchMinor / 100, 0);
$enterpriseTotal = number_format($enterprisePriceMinor / 100, 0);
@endphp
@@ -50,13 +51,22 @@
{{-- Pro --}}
<div class="flex flex-col rounded-2xl border border-slate-200 bg-white p-6 {{ $planKey === 'pro' ? 'ring-2 ring-indigo-500' : '' }}">
<p class="text-xs font-semibold uppercase tracking-wide text-indigo-600">Pro</p>
<x-plan-tier-price :currency="$currency" :monthly-minor="$proPriceMinor" />
<p class="mt-1 text-sm text-slate-500" x-show="billing === 'monthly'">Unlimited branches</p>
<x-plan-tier-price
:currency="$currency"
:monthly-minor="$proPricePerBranchMinor"
:monthly-display="$proPerBranch"
monthly-suffix="/branch/mo"
/>
<p class="mt-1 text-sm text-slate-500" x-show="billing === 'monthly'">
Unlimited branches · {{ $branchCount }} active = {{ $currency }} {{ $proTotal }}/mo
</p>
<ul class="mt-5 flex-1 space-y-2 text-sm text-slate-700">
<li>Everything in Free</li>
<li>Full clinical workflows</li>
<li>Laboratory & pharmacy</li>
<li>Encounter billing</li>
<li>Queue integration</li>
<li>Ladill Queue integration</li>
<li>Unlimited branches (billed per branch)</li>
</ul>
<div class="mt-6">
@if ($planKey === 'pro')
@@ -65,19 +75,23 @@
@if ($planExpiresAt)
until <strong>{{ $planExpiresAt->format('d M Y') }}</strong>
@endif
@if ($billedBranches > 0)
· billed for {{ $billedBranches }} {{ str('branch')->plural($billedBranches) }}
@endif
.
</p>
@elseif ($canManage)
<form x-show="billing === 'monthly'" method="post" action="{{ route('care.pro.subscribe') }}">
@csrf
<button class="btn-primary w-full">Upgrade to Pro monthly wallet</button>
<button class="btn-primary w-full">Upgrade to Pro {{ $currency }} {{ $proTotal }}/mo wallet</button>
</form>
<template x-for="months in @js($prepaidMonths)" :key="months">
<form x-show="billing == months" method="post" action="{{ route('care.pro.subscribe-prepaid') }}">
@csrf
<input type="hidden" name="plan" value="pro">
<input type="hidden" name="months" :value="months">
<button type="submit" class="btn-primary w-full" x-text="'Pay ' + months + ' months via Paystack'"></button>
<button type="submit" class="btn-primary w-full"
x-text="'Pay ' + months + ' months via Paystack — {{ $currency }} ' + ({{ $proPriceMinor }} * months / 100).toLocaleString()"></button>
</form>
</template>
@else
@@ -92,23 +106,19 @@
<x-plan-tier-price
:currency="$currency"
:monthly-minor="$enterprisePricePerBranchMinor"
:prepaid-minor="$enterprisePriceMinor"
:monthly-display="$enterprisePerBranch"
monthly-suffix="/branch/mo"
dark
/>
<p class="mt-1 text-sm text-slate-300" x-show="billing === 'monthly'">
@if ($branchCount > 0)
{{ $branchCount }} active {{ str('branch')->plural($branchCount) }} = {{ $currency }} {{ $enterpriseTotal }}/mo
@else
Per-branch billing for multi-site
@endif
Unlimited branches · {{ $branchCount }} active = {{ $currency }} {{ $enterpriseTotal }}/mo
</p>
<ul class="mt-5 flex-1 space-y-2 text-sm text-slate-200">
<li>Everything in Pro</li>
<li>Multi-department workflows</li>
<li>Consolidated reporting</li>
<li>Dedicated onboarding (setup fee)</li>
<li>Custom multi-department workflow integration</li>
<li>Advanced data analytics</li>
<li>AI-assisted healthcare integration</li>
<li>Priority support & dedicated onboarding</li>
</ul>
<div class="mt-6 space-y-2">
@if ($planKey === 'enterprise')
@@ -117,8 +127,8 @@
@if ($planExpiresAt)
until <strong class="text-white">{{ $planExpiresAt->format('d M Y') }}</strong>
@endif
@if ($enterpriseBilledBranches > 0)
· billed for {{ $enterpriseBilledBranches }} {{ str('branch')->plural($enterpriseBilledBranches) }}
@if ($billedBranches > 0)
· billed for {{ $billedBranches }} {{ str('branch')->plural($billedBranches) }}
@endif
.
</p>
@@ -140,7 +150,7 @@
</form>
</template>
@else
<p class="text-sm text-slate-300">Add at least {{ $enterpriseMinBranches }} active branches, or choose Pro for a single site.</p>
<p class="text-sm text-slate-300">Ask an admin to upgrade, or contact sales for custom onboarding.</p>
@endif
<a href="{{ $salesUrl }}" target="_blank" rel="noopener" class="block text-center text-xs text-amber-200 underline hover:text-white">Contact sales for setup & SLA</a>
@else
+14 -5
View File
@@ -111,11 +111,20 @@
</div>
</x-settings.card>
<x-settings.card title="Integrations" description="Manage service queues and counters in Care. You must also enable Care integration in Ladill Queue settings.">
<label class="flex items-center gap-2 text-sm">
<input type="checkbox" name="queue_integration_enabled" value="1" @checked(data_get($organization->settings, 'queue_integration_enabled'))>
Enable Ladill Queue integration
</label>
<x-settings.card title="Integrations" description="Connect Ladill Queue for service counters. Requires Care Pro or Enterprise — and Care must also be enabled in Queue settings.">
@if (! empty($canUseQueueIntegration))
<label class="flex items-center gap-2 text-sm">
<input type="checkbox" name="queue_integration_enabled" value="1" @checked(data_get($organization->settings, 'queue_integration_enabled'))>
Enable Ladill Queue integration
</label>
@else
<div class="rounded-xl border border-amber-100 bg-amber-50 px-4 py-3 text-sm text-amber-900">
<p class="font-medium">Queue integration is a Pro feature</p>
<p class="mt-1 text-amber-800">Free includes core patient records, appointments &amp; consults, and basic workflows. Upgrade to connect service queues and counters.</p>
<a href="{{ route('care.pro.index') }}" class="mt-3 inline-flex text-sm font-semibold text-indigo-700 hover:text-indigo-900">View Care Pro plans </a>
</div>
<input type="hidden" name="queue_integration_enabled" value="0">
@endif
</x-settings.card>
<div class="flex justify-end">
+26 -23
View File
@@ -30,34 +30,37 @@
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.25 4.5h13.5a.75.75 0 0 1 .75.75v15.75a.75.75 0 0 1-.75.75H5.25a.75.75 0 0 1-.75-.75V5.25a.75.75 0 0 1 .75-.75Z" />'];
}
if ($permissions->can($member, 'lab.view')) {
$nav[] = ['name' => 'Laboratory', 'route' => route('care.lab.queue.index'), 'active' => request()->routeIs('care.lab.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714a2.25 2.25 0 0 0 .659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 0 1-1.59.659H9.06a2.25 2.25 0 0 1-1.591-.659L5 14.5m14 0V17a2.25 2.25 0 0 1-2.25 2.25H7.25A2.25 2.25 0 0 1 5 17v-2.5" />'];
}
// Lab, pharmacy, billing, and Queue service counters are Pro/Enterprise modules.
if (! empty($hasPaidPlan)) {
if ($permissions->can($member, 'lab.view')) {
$nav[] = ['name' => 'Laboratory', 'route' => route('care.lab.queue.index'), 'active' => request()->routeIs('care.lab.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714a2.25 2.25 0 0 0 .659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 0 1-1.59.659H9.06a2.25 2.25 0 0 1-1.591-.659L5 14.5m14 0V17a2.25 2.25 0 0 1-2.25 2.25H7.25A2.25 2.25 0 0 1 5 17v-2.5" />'];
}
if ($permissions->can($member, 'prescriptions.view')) {
$nav[] = ['name' => 'Pharmacy queue', 'route' => route('care.prescriptions.queue'), 'active' => request()->routeIs('care.prescriptions.*') && ! request()->routeIs('care.pharmacy.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714a2.25 2.25 0 0 0 .659 1.591L19 14.5" />'];
}
if ($permissions->can($member, 'prescriptions.view')) {
$nav[] = ['name' => 'Pharmacy queue', 'route' => route('care.prescriptions.queue'), 'active' => request()->routeIs('care.prescriptions.*') && ! request()->routeIs('care.pharmacy.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714a2.25 2.25 0 0 0 .659 1.591L19 14.5" />'];
}
if ($permissions->can($member, 'pharmacy.view')) {
$nav[] = ['name' => 'Inventory', 'route' => route('care.pharmacy.drugs.index'), 'active' => request()->routeIs('care.pharmacy.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="m20.25 7.5-.625 10.632a2.25 2.25 0 0 1-2.247 2.118H6.622a2.25 2.25 0 0 1-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5a1.125 1.125 0 0 0-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" />'];
}
if ($permissions->can($member, 'pharmacy.view')) {
$nav[] = ['name' => 'Inventory', 'route' => route('care.pharmacy.drugs.index'), 'active' => request()->routeIs('care.pharmacy.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="m20.25 7.5-.625 10.632a2.25 2.25 0 0 1-2.247 2.118H6.622a2.25 2.25 0 0 1-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5a1.125 1.125 0 0 0-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" />'];
}
if ($permissions->can($member, 'bills.view')) {
$nav[] = ['name' => 'Billing', 'route' => route('care.bills.index'), 'active' => request()->routeIs('care.bills.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 18.75a60.07 60.07 0 0 1 15.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 0 1 3 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 0 0-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 0 1-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 0 0 3 15h-.375M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />'];
}
if ($permissions->can($member, 'bills.view')) {
$nav[] = ['name' => 'Billing', 'route' => route('care.bills.index'), 'active' => request()->routeIs('care.bills.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 18.75a60.07 60.07 0 0 1 15.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 0 1 3 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 0 0-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 0 1-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 0 0 3 15h-.375M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />'];
}
if ($permissions->can($member, 'reports.finance.view')) {
$nav[] = ['name' => 'Reports', 'route' => route('care.reports.index'), 'active' => request()->routeIs('care.reports.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z" />'];
}
if ($permissions->can($member, 'reports.finance.view')) {
$nav[] = ['name' => 'Reports', 'route' => route('care.reports.index'), 'active' => request()->routeIs('care.reports.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z" />'];
}
if ($organization && data_get($organization->settings, 'queue_integration_enabled') && $permissions->can($member, 'service_queues.console')) {
$nav[] = ['name' => 'Service queues', 'route' => route('care.service-queues.index'), 'active' => request()->routeIs('care.service-queues.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 0 1 0 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 0 1 0-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375Z" />'];
if ($organization && data_get($organization->settings, 'queue_integration_enabled') && $permissions->can($member, 'service_queues.console')) {
$nav[] = ['name' => 'Service queues', 'route' => route('care.service-queues.index'), 'active' => request()->routeIs('care.service-queues.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 0 1 0 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 0 1 0-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375Z" />'];
}
}
$adminNav = [];