Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions api/app/Http/Controllers/Api/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Models\User;
use App\Traits\JsonResponds;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
Expand All @@ -20,7 +21,7 @@ class AuthController extends Controller
{
use JsonResponds;

public function register(Request $request): \Illuminate\Http\Response
public function register(Request $request): Response
{
$data = $request->validate([
'name' => ['required', 'string', 'max:120'],
Expand All @@ -45,7 +46,7 @@ public function register(Request $request): \Illuminate\Http\Response
return $this->created(['user' => $this->userPayload($user)]);
}

public function login(Request $request): \Illuminate\Http\Response
public function login(Request $request): Response
{
$data = $request->validate([
'email' => ['required', 'email'],
Expand All @@ -66,7 +67,7 @@ public function login(Request $request): \Illuminate\Http\Response
return $this->ok(['user' => $this->userPayload($user)]);
}

public function logout(Request $request): \Illuminate\Http\Response
public function logout(Request $request): Response
{
Auth::logout();
$request->session()->invalidate();
Expand All @@ -75,7 +76,7 @@ public function logout(Request $request): \Illuminate\Http\Response
return $this->ok(['message' => 'Logged out.']);
}

public function me(Request $request): \Illuminate\Http\Response
public function me(Request $request): Response
{
return $this->ok(['user' => $this->userPayload($request->user())]);
}
Expand Down
108 changes: 108 additions & 0 deletions api/app/Http/Controllers/Api/ConfigController.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,112 @@ public function zoneDestroy(int $id): Response

return $this->ok(['message' => 'Shipping tier removed.']);
}

// --- Admin: payment method CRUD (DECISIONS.md §3) ---

public function pmIndex(): Response
{
return $this->ok(['data' => PaymentMethod::orderBy('sort_order')->get()]);
}

public function pmStore(Request $request): Response
{
$data = $request->validate([
'type' => ['required', 'in:gcash,bank,cod,card'],
'label' => ['required', 'string', 'max:60'],
'account_name' => ['nullable', 'string', 'max:120'],
'account_number' => ['nullable', 'string', 'max:60'],
'qr_image_url' => ['nullable', 'string', 'url', 'max:500'],
'is_active' => ['boolean'],
'sort_order' => ['integer'],
]);

$method = PaymentMethod::create($data);

return $this->created(['data' => $method, 'message' => 'Payment method created.']);
}

public function pmUpdate(Request $request, int $id): Response
{
$data = $request->validate([
'type' => ['sometimes', 'in:gcash,bank,cod,card'],
'label' => ['sometimes', 'string', 'max:60'],
'account_name' => ['nullable', 'string', 'max:120'],
'account_number' => ['nullable', 'string', 'max:60'],
'qr_image_url' => ['nullable', 'string', 'url', 'max:500'],
'is_active' => ['sometimes', 'boolean'],
'sort_order' => ['sometimes', 'integer'],
]);

$method = PaymentMethod::find($id);
if (! $method) {
return $this->error('Payment method not found.', 404);
}
$method->update($data);

return $this->ok(['data' => $method->fresh(), 'message' => 'Payment method updated.']);
}

public function pmDestroy(int $id): Response
{
$method = PaymentMethod::find($id);
if (! $method) {
return $this->error('Payment method not found.', 404);
}
$method->delete();

return $this->ok(['message' => 'Payment method removed.']);
}

// --- Admin: fulfillment option CRUD (DECISIONS.md §4) ---

public function foIndex(): Response
{
return $this->ok(['data' => FulfillmentOption::orderBy('sort_order')->get()]);
}

public function foStore(Request $request): Response
{
$data = $request->validate([
'mode' => ['required', 'in:pickup,lalamove,grab,pop_up_pickup'],
'label' => ['required', 'string', 'max:60'],
'address' => ['nullable', 'string'],
'is_active' => ['boolean'],
'sort_order' => ['integer'],
]);

$option = FulfillmentOption::create($data);

return $this->created(['data' => $option, 'message' => 'Fulfillment option created.']);
}

public function foUpdate(Request $request, int $id): Response
{
$data = $request->validate([
'mode' => ['sometimes', 'in:pickup,lalamove,grab,pop_up_pickup'],
'label' => ['sometimes', 'string', 'max:60'],
'address' => ['nullable', 'string'],
'is_active' => ['sometimes', 'boolean'],
'sort_order' => ['sometimes', 'integer'],
]);

$option = FulfillmentOption::find($id);
if (! $option) {
return $this->error('Fulfillment option not found.', 404);
}
$option->update($data);

return $this->ok(['data' => $option->fresh(), 'message' => 'Fulfillment option updated.']);
}

public function foDestroy(int $id): Response
{
$option = FulfillmentOption::find($id);
if (! $option) {
return $this->error('Fulfillment option not found.', 404);
}
$option->delete();

return $this->ok(['message' => 'Fulfillment option removed.']);
}
}
10 changes: 5 additions & 5 deletions api/app/Http/Controllers/Api/OrderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ public function batches(Request $request): Response
{
$date = $request->query('date', now()->toDateString());

$batch = DB::table('batches')->where('fulfillment_date', $date)->first();
$batch = DB::table('batches')->whereDate('fulfillment_date', $date)->first();
if (! $batch) {
return $this->ok(['data' => ['date' => $date, 'capacity' => null, 'filled' => 0, 'is_open' => false]]);
}

$filled = DB::table('orders')
->where('fulfillment_date', $date)
->whereDate('fulfillment_date', $date)
->whereNotIn('status', ['cancelled'])
->count();

Expand Down Expand Up @@ -81,7 +81,7 @@ public function store(Request $request): Response
$order = DB::transaction(function () use ($data, $user, $isDelivery) {
// Atomic cutoff + capacity enforcement (race-safe via row lock).
$batch = DB::table('batches')
->where('fulfillment_date', $data['fulfillment_date'])
->whereDate('fulfillment_date', $data['fulfillment_date'])
->lockForUpdate()
->first();
Comment on lines 83 to 86

Expand All @@ -90,7 +90,7 @@ public function store(Request $request): Response
}

$filled = DB::table('orders')
->where('fulfillment_date', $data['fulfillment_date'])
->whereDate('fulfillment_date', $data['fulfillment_date'])
->whereNotIn('status', ['cancelled'])
->count();
Comment on lines 92 to 95

Expand Down Expand Up @@ -218,7 +218,7 @@ public function adminIndex(Request $request): Response
$query->where('status', $status);
}
if ($date = $request->query('date')) {
$query->where('fulfillment_date', $date);
$query->whereDate('fulfillment_date', $date);
}
$orders = $query->orderByDesc('created_at')->get();

Expand Down
7 changes: 4 additions & 3 deletions api/app/Http/Controllers/Api/PrepController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Http\Controllers\Controller;
use App\Traits\JsonResponds;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;

/**
Expand All @@ -15,7 +16,7 @@ class PrepController extends Controller
{
use JsonResponds;

public function index(Request $request): \Illuminate\Http\Response
public function index(Request $request): Response
{
$pdo = DB::connection()->getPdo();

Expand All @@ -29,10 +30,10 @@ public function index(Request $request): \Illuminate\Http\Response
WHERE o.status IN ('confirmed', 'preparing', 'ready', 'out_for_delivery')";

if ($date = $request->query('date')) {
$stmt = $pdo->prepare($sql . " AND o.fulfillment_date = :date GROUP BY ri.ingredient_name, ri.unit, o.fulfillment_date ORDER BY ingredient_name");
$stmt = $pdo->prepare($sql.' AND o.fulfillment_date = :date GROUP BY ri.ingredient_name, ri.unit, o.fulfillment_date ORDER BY ingredient_name');
$stmt->execute(['date' => $date]);
} else {
$stmt = $pdo->prepare($sql . " GROUP BY ri.ingredient_name, ri.unit, o.fulfillment_date ORDER BY ingredient_name");
$stmt = $pdo->prepare($sql.' GROUP BY ri.ingredient_name, ri.unit, o.fulfillment_date ORDER BY ingredient_name');
$stmt->execute();
}

Expand Down
10 changes: 6 additions & 4 deletions api/app/Traits/JsonResponds.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace App\Traits;

use Illuminate\Http\Response;

/**
* Centralised JSON responses.
*
Expand All @@ -10,25 +12,25 @@
*/
trait JsonResponds
{
protected function json(mixed $data, int $status = 200): \Illuminate\Http\Response
protected function json(mixed $data, int $status = 200): Response
{
return response(
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
$status
)->header('Content-Type', 'application/json; charset=utf-8');
}

protected function ok(mixed $data = []): \Illuminate\Http\Response
protected function ok(mixed $data = []): Response
{
return $this->json($data, 200);
}

protected function created(mixed $data): \Illuminate\Http\Response
protected function created(mixed $data): Response
{
return $this->json($data, 201);
}

protected function error(string $message, int $status, array $extra = []): \Illuminate\Http\Response
protected function error(string $message, int $status, array $extra = []): Response
{
return $this->json(array_merge(['message' => $message], $extra), $status);
}
Expand Down
3 changes: 2 additions & 1 deletion api/bootstrap/app.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use App\Http\Middleware\EnsureRole;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
Expand All @@ -12,7 +13,7 @@
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'role' => \App\Http\Middleware\EnsureRole::class,
'role' => EnsureRole::class,
]);

// SPA dev convenience: CSRF disabled for /api/* (session auth still enforced).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('menu_items', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('recipe_ingredients', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('payment_methods', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('fulfillment_options', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('batches', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('orders', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('order_items', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('audit_logs', function (Blueprint $table) {
Expand Down
10 changes: 10 additions & 0 deletions api/routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,14 @@
Route::post('/shipping-zones', [ConfigController::class, 'zoneStore']);
Route::patch('/shipping-zones/{id}', [ConfigController::class, 'zoneUpdate']);
Route::delete('/shipping-zones/{id}', [ConfigController::class, 'zoneDestroy']);

Route::get('/payment-methods', [ConfigController::class, 'pmIndex']);
Route::post('/payment-methods', [ConfigController::class, 'pmStore']);
Route::patch('/payment-methods/{id}', [ConfigController::class, 'pmUpdate']);
Route::delete('/payment-methods/{id}', [ConfigController::class, 'pmDestroy']);

Route::get('/fulfillment-options', [ConfigController::class, 'foIndex']);
Route::post('/fulfillment-options', [ConfigController::class, 'foStore']);
Route::patch('/fulfillment-options/{id}', [ConfigController::class, 'foUpdate']);
Route::delete('/fulfillment-options/{id}', [ConfigController::class, 'foDestroy']);
});
Loading