Files
ladill-merchant/app/Http/Controllers/Merchant/ProductController.php
T
isaaccladandClaude Opus 4.8 b19e2654a1
Deploy Ladill Merchant / deploy (push) Successful in 30s
Add Products page backed by the CRM products API + storefront catalog picker
- New Products page (merchant.products.*) with full CRUD proxied to the Ladill
  CRM products API via a new CrmClient + config/crm.php (owner-scoped, type=product).
- Sidebar gains a Products entry.
- The new storefront form loads the merchant's catalog: each shop/menu section
  gets an "Add from products…" picker that drops a CRM product in as an item
  (name, price, description). Catalog fetch is resilient — empty if CRM is down.

Wires CRM_API_URL + CRM_API_KEY_MERCHANT on the merchant env (matches CRM).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 07:32:58 +00:00

94 lines
3.0 KiB
PHP

<?php
namespace App\Http\Controllers\Merchant;
use App\Http\Controllers\Controller;
use App\Services\Crm\CrmClient;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
/**
* Products page — fully backed by the Ladill CRM products API. The merchant's
* catalog lives in CRM (owner-scoped); this controller proxies CRUD to it.
*/
class ProductController extends Controller
{
private function crm(): CrmClient
{
return CrmClient::for((string) ladill_account()->public_id);
}
public function index(Request $request): View
{
$search = trim((string) $request->query('search', ''));
$response = $this->crm()->products([
'type' => 'product',
'per_page' => 100,
'search' => $search !== '' ? $search : null,
]);
return view('merchant.products.index', [
'products' => (array) ($response['data'] ?? []),
'search' => $search,
]);
}
public function create(): View
{
return view('merchant.products.create');
}
public function store(Request $request): RedirectResponse
{
$this->crm()->createProduct($this->payload($request));
return redirect()->route('merchant.products.index')->with('success', 'Product created.');
}
public function edit(string $product): View
{
return view('merchant.products.edit', ['product' => $this->crm()->product($product)]);
}
public function update(Request $request, string $product): RedirectResponse
{
$this->crm()->updateProduct($product, $this->payload($request));
return redirect()->route('merchant.products.index')->with('success', 'Product updated.');
}
public function destroy(string $product): RedirectResponse
{
$this->crm()->deleteProduct($product);
return redirect()->route('merchant.products.index')->with('success', 'Product deleted.');
}
/** @return array<string,mixed> */
private function payload(Request $request): array
{
$data = $request->validate([
'name' => ['required', 'string', 'max:255'],
'sku' => ['nullable', 'string', 'max:80'],
'description' => ['nullable', 'string', 'max:5000'],
'unit_price' => ['nullable', 'numeric', 'min:0'],
'currency' => ['nullable', 'string', 'size:3'],
'tax_rate' => ['nullable', 'numeric', 'min:0', 'max:100'],
'active' => ['nullable', 'boolean'],
]);
return [
'name' => $data['name'],
'sku' => $data['sku'] ?? null,
'type' => 'product',
'description' => $data['description'] ?? null,
'unit_price_minor' => (int) round(((float) ($data['unit_price'] ?? 0)) * 100),
'currency' => strtoupper((string) ($data['currency'] ?? config('crm.default_currency', 'GHS'))),
'tax_rate' => (float) ($data['tax_rate'] ?? 0),
'active' => $request->boolean('active'),
];
}
}