diff --git a/app/Http/Controllers/Care/IssueReportController.php b/app/Http/Controllers/Care/IssueReportController.php new file mode 100644 index 0000000..023bd81 --- /dev/null +++ b/app/Http/Controllers/Care/IssueReportController.php @@ -0,0 +1,70 @@ +user()), 403); + + return view('care.issues.create'); + } + + public function store(Request $request, IdentityTeamClient $identity): RedirectResponse + { + abort_unless(! StaffUx::showBilling($request->user()), 403); + + $validated = $request->validate([ + 'subject' => ['required', 'string', 'max:255'], + 'message' => ['required', 'string', 'max:5000'], + 'priority' => ['required', 'in:low,normal,high'], + ]); + + $user = $request->user(); + $organization = $this->organization($request); + $member = $this->member($request); + + $context = trim(implode("\n", array_filter([ + 'App: Ladill Care', + 'Organization: '.($organization->name ?? '—'), + 'Role: '.($member?->role ?? '—'), + 'Reporter: '.($user->email ?? $user->ownerRef()), + '', + $validated['message'], + ]))); + + try { + $ticket = $identity->createSupportTicket($user->ownerRef(), [ + 'subject' => $validated['subject'], + 'message' => $context, + 'priority' => $validated['priority'], + 'tags' => ['staff_issue', 'app:care'], + ], 'care'); + } catch (\Throwable $e) { + report($e); + + return back() + ->withInput() + ->with('error', 'Could not send your report right now. Please try again in a moment.'); + } + + $ref = (string) ($ticket['ticket_ref'] ?? ''); + + return redirect() + ->route('care.dashboard') + ->with('success', $ref !== '' + ? "Issue reported to Ladill support ({$ref}). Our team will follow up." + : 'Issue reported to Ladill support. Our team will follow up.'); + } +} diff --git a/app/Services/Identity/IdentityTeamClient.php b/app/Services/Identity/IdentityTeamClient.php index fbb3e2a..3ca220f 100644 --- a/app/Services/Identity/IdentityTeamClient.php +++ b/app/Services/Identity/IdentityTeamClient.php @@ -78,6 +78,24 @@ class IdentityTeamClient return (array) $response->json('data', []); } + /** + * @param array{subject: string, message: string, priority?: string, tags?: list} $payload + * @return array + */ + public function createSupportTicket(string $userPublicId, array $payload, string $app = 'care'): array + { + $response = $this->request('post', '/identity/support/tickets', array_filter([ + 'user' => $userPublicId, + 'subject' => $payload['subject'] ?? '', + 'message' => $payload['message'] ?? '', + 'priority' => $payload['priority'] ?? 'normal', + 'app' => $app, + 'tags' => $payload['tags'] ?? ['staff_issue'], + ], fn ($value) => $value !== null && $value !== '')); + + return (array) $response->json('data', []); + } + private function request(string $method, string $path, array $query = []): Response { $url = rtrim((string) config('identity.api_url'), '/').$path; @@ -87,9 +105,11 @@ class IdentityTeamClient throw new RuntimeException('Identity API is not configured.'); } - $response = Http::withToken($key) - ->timeout(10) - ->{$method}($url, $query); + $http = Http::withToken($key)->timeout(10); + + $response = $method === 'post' + ? $http->post($url, $query) + : $http->{$method}($url, $query); if (! $response->successful()) { throw new RuntimeException($response->json('message') ?: 'Identity API request failed.'); diff --git a/resources/views/care/issues/create.blade.php b/resources/views/care/issues/create.blade.php new file mode 100644 index 0000000..836bdfd --- /dev/null +++ b/resources/views/care/issues/create.blade.php @@ -0,0 +1,43 @@ + +
+
+

Report an issue

+

Send a problem to Ladill support. Account owners manage billing upgrades — staff use this for help with Care.

+
+ + @if (session('error')) +

{{ session('error') }}

+ @endif + +
+ @csrf + +
+ + + @error('subject')

{{ $message }}

@enderror +
+ +
+ + +
+ +
+ + + @error('message')

{{ $message }}

@enderror +
+ + +
+
+
diff --git a/resources/views/partials/sidebar.blade.php b/resources/views/partials/sidebar.blade.php index d13ab58..61083aa 100644 --- a/resources/views/partials/sidebar.blade.php +++ b/resources/views/partials/sidebar.blade.php @@ -115,16 +115,27 @@ @endif - @php $proActive = request()->routeIs('care.pro.*'); @endphp - @if (! empty($hasPaidPlan)) - - - {{ ! empty($isEnterprise) ? 'Care Enterprise' : 'Care Pro' }} - + @php + $proActive = request()->routeIs('care.pro.*'); + $issueActive = request()->routeIs('care.issues.*'); + $isAccountOwner = \App\Support\StaffUx::showBilling(auth()->user()); + @endphp + @if ($isAccountOwner) + @if (! empty($hasPaidPlan)) + + + {{ ! empty($isEnterprise) ? 'Care Enterprise' : 'Care Pro' }} + + @else + + + Upgrade to Pro + + @endif @else - - - Upgrade to Pro + + + Report Issue @endif diff --git a/routes/web.php b/routes/web.php index d7effcb..2603b33 100644 --- a/routes/web.php +++ b/routes/web.php @@ -25,6 +25,7 @@ use App\Http\Controllers\Care\ProController; use App\Http\Controllers\Care\ReportController; use App\Http\Controllers\Care\ServiceQueueController; use App\Http\Controllers\Care\IntegrationsController; +use App\Http\Controllers\Care\IssueReportController; use App\Http\Controllers\Care\SettingsController; use Illuminate\Support\Facades\Route; @@ -152,6 +153,9 @@ Route::middleware(['auth', 'platform.session'])->group(function () { Route::post('/pro/subscribe-prepaid', [ProController::class, 'subscribePrepaid'])->name('care.pro.subscribe-prepaid'); Route::get('/pro/paystack/callback', [ProController::class, 'paystackCallback'])->name('care.pro.paystack.callback'); + Route::get('/report-issue', [IssueReportController::class, 'create'])->name('care.issues.create'); + Route::post('/report-issue', [IssueReportController::class, 'store'])->name('care.issues.store'); + Route::get('/branches', [BranchController::class, 'index'])->name('care.branches.index'); Route::get('/branches/create', [BranchController::class, 'create'])->name('care.branches.create'); Route::post('/branches', [BranchController::class, 'store'])->name('care.branches.store');