added countries support

This commit is contained in:
Mark Gregory
2026-06-15 09:05:15 +01:00
parent e91b4251fd
commit 63a9981c7c
21 changed files with 653 additions and 284 deletions
@@ -0,0 +1,9 @@
<x-layouts.app>
<div class="mx-auto max-w-[1000px] space-y-6" dusk="countries-edit">
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white"><p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Country support</p><h1 class="mt-2 text-3xl font-semibold tracking-tight">Edit {{ $country->flag_emoji }} {{ $country->effective_name }}</h1><p class="mt-2 max-w-3xl text-sm text-blue-100/90">ISO codes are locked. Only support status, display name and sort order can be changed.</p></div></div>
<form method="POST" action="{{ route('countries.update', $country) }}" class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">@csrf @method('PUT')
<div class="grid gap-5"><div><label class="text-sm font-semibold" for="display_name">Display name override</label><input id="display_name" name="display_name" value="{{ old('display_name', $country->display_name) }}" class="mt-2 min-h-11 w-full rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" />@error('display_name')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror</div><div><label class="text-sm font-semibold" for="sort_order">Sort order</label><input id="sort_order" name="sort_order" type="number" min="0" value="{{ old('sort_order', $country->sort_order) }}" class="mt-2 min-h-11 w-full rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" />@error('sort_order')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror</div><label class="flex items-center gap-3 rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950"><input type="hidden" name="is_active" value="0" /><input type="checkbox" name="is_active" value="1" @checked(old('is_active', $country->is_active)) class="rounded border-zinc-300 text-blue-600 focus:ring-blue-500" /><span><span class="block text-sm font-semibold">Active country</span><span class="block text-xs text-zinc-500">Inactive countries remain in the ISO table but are hidden from normal selection lists.</span></span></label></div>
<div class="mt-6 flex flex-wrap gap-2"><button type="submit" class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Save country</button><a href="{{ route('countries.show', $country) }}" wire:navigate class="rounded-xl bg-zinc-100 px-4 py-2.5 text-sm font-semibold text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700">Cancel</a></div>
</form>
</div>
</x-layouts.app>
@@ -1,9 +1,25 @@
@extends('countries::layouts.master')
@section('content')
<h1>Hello World</h1>
<p>
This view is loaded from module: {!! config('countries.name') !!}
</p>
@endsection
<x-layouts.app>
<div class="mx-auto max-w-[1800px] space-y-6" dusk="countries-index">
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Reference data</p>
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Countries</h1>
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">ISO 3166 country records used by addresses, tenants, billing, carriers, customs and integrations.</p>
</div>
</div>
@if (session('success'))<div class="rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">{{ session('success') }}</div>@endif
<div class="grid gap-4 md:grid-cols-3">
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="text-sm text-zinc-500">Total countries</div><div class="mt-2 text-3xl font-semibold">{{ number_format($totalCount) }}</div></div>
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="text-sm text-zinc-500">Active</div><div class="mt-2 text-3xl font-semibold text-emerald-700 dark:text-emerald-300">{{ number_format($activeCount) }}</div></div>
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="text-sm text-zinc-500">Inactive</div><div class="mt-2 text-3xl font-semibold text-amber-700 dark:text-amber-300">{{ number_format($inactiveCount) }}</div></div>
</div>
<form method="GET" action="{{ route('countries.index') }}" class="flex flex-col gap-3 rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 md:flex-row md:items-center">
<input name="search" value="{{ $search }}" placeholder="Search by country, ISO code or numeric code" class="min-h-11 flex-1 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" />
<select name="status" class="min-h-11 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"><option value="all" @selected($status === 'all')>All</option><option value="active" @selected($status === 'active')>Active</option><option value="inactive" @selected($status === 'inactive')>Inactive</option></select>
<button type="submit" class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Filter</button>
</form>
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="overflow-x-auto"><table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800"><thead class="bg-zinc-50 dark:bg-zinc-950"><tr><th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Country</th><th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Alpha-2</th><th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Alpha-3</th><th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Numeric</th><th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Status</th><th class="px-4 py-3 text-right text-xs font-bold uppercase tracking-wide text-zinc-500">Actions</th></tr></thead><tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
@forelse ($countries as $country)<tr class="hover:bg-blue-50/50 dark:hover:bg-blue-950/20"><td class="px-4 py-3"><div class="flex items-center gap-3"><span class="text-xl">{{ $country->flag_emoji }}</span><div><div class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $country->effective_name }}</div><div class="text-xs text-zinc-500">{{ $country->official_name ?: $country->name }}</div></div></div></td><td class="px-4 py-3 font-mono text-sm text-blue-700 dark:text-blue-300">{{ $country->alpha2 }}</td><td class="px-4 py-3 font-mono text-sm">{{ $country->alpha3 }}</td><td class="px-4 py-3 font-mono text-sm">{{ $country->numeric }}</td><td class="px-4 py-3">@if ($country->is_active)<span class="rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-bold text-emerald-800 dark:bg-emerald-950 dark:text-emerald-200">Active</span>@else<span class="rounded-full bg-amber-100 px-2.5 py-1 text-xs font-bold text-amber-800 dark:bg-amber-950 dark:text-amber-200">Inactive</span>@endif</td><td class="px-4 py-3 text-right"><a href="{{ route('countries.show', $country) }}" wire:navigate class="rounded-lg bg-blue-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-700">Open</a> <a href="{{ route('countries.edit', $country) }}" wire:navigate class="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700">Edit</a></td></tr>@empty<tr><td colspan="6" class="px-4 py-10 text-center text-sm text-zinc-500">No countries matched your search.</td></tr>@endforelse
</tbody></table></div><div class="border-t border-zinc-200 px-4 py-3 dark:border-zinc-800">{{ $countries->links() }}</div></div>
</div>
</x-layouts.app>
@@ -0,0 +1,7 @@
<x-layouts.app>
<div class="mx-auto max-w-[1200px] space-y-6" dusk="countries-show">
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white"><p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">ISO country</p><h1 class="mt-2 text-3xl font-semibold tracking-tight">{{ $country->flag_emoji }} {{ $country->effective_name }}</h1><p class="mt-2 max-w-3xl text-sm text-blue-100/90">{{ $country->official_name ?: $country->name }}</p><div class="mt-4 flex gap-2"><a href="{{ route('countries.edit', $country) }}" wire:navigate class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-blue-700">Edit support</a><a href="{{ route('countries.index') }}" wire:navigate class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Back</a></div></div></div>
@if (session('success'))<div class="rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">{{ session('success') }}</div>@endif
<div class="grid gap-4 md:grid-cols-2">@foreach (['Alpha-2' => $country->alpha2, 'Alpha-3' => $country->alpha3, 'Numeric' => $country->numeric, 'Canonical name' => $country->name, 'Official name' => $country->official_name ?: '—', 'Common name' => $country->common_name ?: '—', 'Display override' => $country->display_name ?: '—', 'Active' => $country->is_active ? 'Yes' : 'No'] as $label => $value)<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="text-xs font-bold uppercase tracking-wide text-zinc-500">{{ $label }}</div><div class="mt-2 text-base font-semibold text-zinc-900 dark:text-zinc-100">{{ $value }}</div></div>@endforeach</div>
</div>
</x-layouts.app>