sed -n '1,260p' app/Models/User.php && sed -n '1,220p' app/Models/Store.php && sed -n '1,220p' app/Models/StoreUser.php

StoreUser.php

<?php

namespace App\Models;

use App\Enums\StoreUserRole;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Laravel\Fortify\TwoFactorAuthenticatable;

class User extends Authenticatable implements MustVerifyEmail
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable, TwoFactorAuthenticatable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'status',
        'password',
        'last_login_at',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password_hash',
        'two_factor_secret',
        'two_factor_recovery_codes',
        'remember_token',
    ];

    /**
     * @return BelongsToMany<Store, $this>
     */
    public function stores(): BelongsToMany
    {
        return $this->belongsToMany(Store::class, 'store_users')
            ->using(StoreUser::class)
            ->withPivot('role');
    }

    public function roleForStore(Store $store): ?StoreUserRole
    {
        return $this->roleForStoreId($store->getKey());
    }

    public function roleForStoreId(int $storeId): ?StoreUserRole
    {
        $role = $this->stores()
            ->whereKey($storeId)
            ->first()
            ?->pivot
            ?->role;

        if ($role instanceof StoreUserRole) {
            return $role;
        }

        return is_string($role) ? StoreUserRole::tryFrom($role) : null;
    }

    public function getAuthPassword(): ?string
    {
        return $this->password_hash;
    }

    public function setPasswordAttribute(string $value): void
    {
        $this->attributes['password_hash'] = Hash::needsRehash($value) ? Hash::make($value) : $value;
    }

    public function getPasswordAttribute(): ?string
    {
        return $this->password_hash;
    }

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'last_login_at' => 'datetime',
            'two_factor_confirmed_at' => 'datetime',
        ];
    }

    /**
     * Get the user's initials
     */
    public function initials(): string
    {
        return Str::of($this->name)
            ->explode(' ')
            ->take(2)
            ->map(fn ($word) => Str::substr($word, 0, 1))
            ->implode('');
    }
}
<?php

namespace App\Models;

use App\Enums\StoreStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Store extends Model
{
    /** @use HasFactory<\Database\Factories\StoreFactory> */
    use HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'organization_id',
        'name',
        'handle',
        'status',
        'default_currency',
        'default_locale',
        'timezone',
    ];

    /**
     * @return BelongsTo<Organization, $this>
     */
    public function organization(): BelongsTo
    {
        return $this->belongsTo(Organization::class);
    }

    /**
     * @return HasMany<StoreDomain, $this>
     */
    public function domains(): HasMany
    {
        return $this->hasMany(StoreDomain::class);
    }

    /**
     * @return BelongsToMany<User, $this>
     */
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'store_users')
            ->using(StoreUser::class)
            ->withPivot('role');
    }

    /**
     * @return HasOne<StoreSettings, $this>
     */
    public function settings(): HasOne
    {
        return $this->hasOne(StoreSettings::class);
    }

    /**
     * @return HasMany<Theme, $this>
     */
    public function themes(): HasMany
    {
        return $this->hasMany(Theme::class);
    }

    /**
     * @return HasMany<Page, $this>
     */
    public function pages(): HasMany
    {
        return $this->hasMany(Page::class);
    }

    /**
     * @return HasMany<NavigationMenu, $this>
     */
    public function navigationMenus(): HasMany
    {
        return $this->hasMany(NavigationMenu::class);
    }

    /**
     * @return HasMany<Cart, $this>
     */
    public function carts(): HasMany
    {
        return $this->hasMany(Cart::class);
    }

    /**
     * @return HasMany<Checkout, $this>
     */
    public function checkouts(): HasMany
    {
        return $this->hasMany(Checkout::class);
    }

    /**
     * @return HasMany<ShippingZone, $this>
     */
    public function shippingZones(): HasMany
    {
        return $this->hasMany(ShippingZone::class);
    }

    /**
     * @return HasOne<TaxSettings, $this>
     */
    public function taxSettings(): HasOne
    {
        return $this->hasOne(TaxSettings::class);
    }

    /**
     * @return HasMany<Discount, $this>
     */
    public function discounts(): HasMany
    {
        return $this->hasMany(Discount::class);
    }

    /**
     * @return HasMany<Order, $this>
     */
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }

    /**
     * @return HasMany<AppInstallation, $this>
     */
    public function appInstallations(): HasMany
    {
        return $this->hasMany(AppInstallation::class);
    }

    /**
     * @return HasMany<WebhookSubscription, $this>
     */
    public function webhookSubscriptions(): HasMany
    {
        return $this->hasMany(WebhookSubscription::class);
    }

    public function isSuspended(): bool
    {
        return $this->status === StoreStatus::Suspended;
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'status' => StoreStatus::class,
        ];
    }
}
<?php

namespace App\Models;

use App\Enums\StoreUserRole;
use Illuminate\Database\Eloquent\Relations\Pivot;

class StoreUser extends Pivot
{
    public $incrementing = false;

    public $timestamps = false;

    public const UPDATED_AT = null;

    protected $table = 'store_users';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'store_id',
        'user_id',
        'role',
    ];

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'role' => StoreUserRole::class,
        ];
    }
}


Output:
Chunk ID: d5f926
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1737
Output:
<?php

namespace App\Models;

use App\Enums\StoreUserRole;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Laravel\Fortify\TwoFactorAuthenticatable;

class User extends Authenticatable implements MustVerifyEmail
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable, TwoFactorAuthenticatable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'status',
        'password',
        'last_login_at',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password_hash',
        'two_factor_secret',
        'two_factor_recovery_codes',
        'remember_token',
    ];

    /**
     * @return BelongsToMany<Store, $this>
     */
    public function stores(): BelongsToMany
    {
        return $this->belongsToMany(Store::class, 'store_users')
            ->using(StoreUser::class)
            ->withPivot('role');
    }

    public function roleForStore(Store $store): ?StoreUserRole
    {
        return $this->roleForStoreId($store->getKey());
    }

    public function roleForStoreId(int $storeId): ?StoreUserRole
    {
        $role = $this->stores()
            ->whereKey($storeId)
            ->first()
            ?->pivot
            ?->role;

        if ($role instanceof StoreUserRole) {
            return $role;
        }

        return is_string($role) ? StoreUserRole::tryFrom($role) : null;
    }

    public function getAuthPassword(): ?string
    {
        return $this->password_hash;
    }

    public function setPasswordAttribute(string $value): void
    {
        $this->attributes['password_hash'] = Hash::needsRehash($value) ? Hash::make($value) : $value;
    }

    public function getPasswordAttribute(): ?string
    {
        return $this->password_hash;
    }

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'last_login_at' => 'datetime',
            'two_factor_confirmed_at' => 'datetime',
        ];
    }

    /**
     * Get the user's initials
     */
    public function initials(): string
    {
        return Str::of($this->name)
            ->explode(' ')
            ->take(2)
            ->map(fn ($word) => Str::substr($word, 0, 1))
            ->implode('');
    }
}
<?php

namespace App\Models;

use App\Enums\StoreStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Store extends Model
{
    /** @use HasFactory<\Database\Factories\StoreFactory> */
    use HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'organization_id',
        'name',
        'handle',
        'status',
        'default_currency',
        'default_locale',
        'timezone',
    ];

    /**
     * @return BelongsTo<Organization, $this>
     */
    public function organization(): BelongsTo
    {
        return $this->belongsTo(Organization::class);
    }

    /**
     * @return HasMany<StoreDomain, $this>
     */
    public function domains(): HasMany
    {
        return $this->hasMany(StoreDomain::class);
    }

    /**
     * @return BelongsToMany<User, $this>
     */
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'store_users')
            ->using(StoreUser::class)
            ->withPivot('role');
    }

    /**
     * @return HasOne<StoreSettings, $this>
     */
    public function settings(): HasOne
    {
        return $this->hasOne(StoreSettings::class);
    }

    /**
     * @return HasMany<Theme, $this>
     */
    public function themes(): HasMany
    {
        return $this->hasMany(Theme::class);
    }

    /**
     * @return HasMany<Page, $this>
     */
    public function pages(): HasMany
    {
        return $this->hasMany(Page::class);
    }

    /**
     * @return HasMany<NavigationMenu, $this>
     */
    public function navigationMenus(): HasMany
    {
        return $this->hasMany(NavigationMenu::class);
    }

    /**
     * @return HasMany<Cart, $this>
     */
    public function carts(): HasMany
    {
        return $this->hasMany(Cart::class);
    }

    /**
     * @return HasMany<Checkout, $this>
     */
    public function checkouts(): HasMany
    {
        return $this->hasMany(Checkout::class);
    }

    /**
     * @return HasMany<ShippingZone, $this>
     */
    public function shippingZones(): HasMany
    {
        return $this->hasMany(ShippingZone::class);
    }

    /**
     * @return HasOne<TaxSettings, $this>
     */
    public function taxSettings(): HasOne
    {
        return $this->hasOne(TaxSettings::class);
    }

    /**
     * @return HasMany<Discount, $this>
     */
    public function discounts(): HasMany
    {
        return $this->hasMany(Discount::class);
    }

    /**
     * @return HasMany<Order, $this>
     */
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }

    /**
     * @return HasMany<AppInstallation, $this>
     */
    public function appInstallations(): HasMany
    {
        return $this->hasMany(AppInstallation::class);
    }

    /**
     * @return HasMany<WebhookSubscription, $this>
     */
    public function webhookSubscriptions(): HasMany
    {
        return $this->hasMany(WebhookSubscription::class);
    }

    public function isSuspended(): bool
    {
        return $this->status === StoreStatus::Suspended;
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'status' => StoreStatus::class,
        ];
    }
}
<?php

namespace App\Models;

use App\Enums\StoreUserRole;
use Illuminate\Database\Eloquent\Relations\Pivot;

class StoreUser extends Pivot
{
    public $incrementing = false;

    public $timestamps = false;

    public const UPDATED_AT = null;

    protected $table = 'store_users';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'store_id',
        'user_id',
        'role',
    ];

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'role' => StoreUserRole::class,
        ];
    }
}


sed -n '1,260p' app/Traits/ChecksStoreRole.php && sed -n '1,120p' app/Enums/StoreUserRole.php

StoreUserRole.php

<?php

namespace App\Traits;

use App\Enums\StoreUserRole;
use App\Models\Store;
use App\Models\User;

trait ChecksStoreRole
{
    public function getStoreRole(User $user, ?int $storeId = null): ?StoreUserRole
    {
        $storeId ??= $this->currentStoreId();

        if (! $storeId) {
            return null;
        }

        return $user->roleForStoreId($storeId);
    }

    /**
     * @param  array<int, StoreUserRole>  $roles
     */
    public function hasRole(User $user, ?int $storeId, array $roles): bool
    {
        $role = $this->getStoreRole($user, $storeId);

        return $role !== null && in_array($role, $roles, true);
    }

    public function isOwnerOnly(User $user, ?int $storeId = null): bool
    {
        return $this->hasRole($user, $storeId, [StoreUserRole::Owner]);
    }

    public function isOwnerOrAdmin(User $user, ?int $storeId = null): bool
    {
        return $this->hasRole($user, $storeId, [StoreUserRole::Owner, StoreUserRole::Admin]);
    }

    public function isOwnerAdminOrStaff(User $user, ?int $storeId = null): bool
    {
        return $this->hasRole($user, $storeId, [StoreUserRole::Owner, StoreUserRole::Admin, StoreUserRole::Staff]);
    }

    public function isAnyRole(User $user, ?int $storeId = null): bool
    {
        return $this->getStoreRole($user, $storeId) !== null;
    }

    protected function currentStoreId(): ?int
    {
        if (! app()->bound('current_store')) {
            return null;
        }

        $store = app('current_store');

        return $store instanceof Store ? $store->getKey() : null;
    }

    protected function storeIdForModel(object $model): ?int
    {
        if (method_exists($model, 'getAttribute')) {
            $storeId = $model->getAttribute('store_id');

            return is_numeric($storeId) ? (int) $storeId : null;
        }

        if (property_exists($model, 'store_id')) {
            return is_numeric($model->store_id) ? (int) $model->store_id : null;
        }

        return null;
    }
}
<?php

namespace App\Enums;

enum StoreUserRole: string
{
    case Owner = 'owner';
    case Admin = 'admin';
    case Staff = 'staff';
    case Support = 'support';
}


sed -n '1,320p' app/Livewire/Admin/Orders/Show.php && sed -n '1,420p' app/Livewire/Admin/Products/Form.php && sed -n '1,180p' app/Livewire/Admin/Collections/Form.php

Form.php

<?php

namespace App\Livewire\Admin\Orders;

use App\Enums\RefundStatus;
use App\Exceptions\InvalidFulfillmentOperationException;
use App\Exceptions\InvalidOrderOperationException;
use App\Exceptions\InvalidRefundOperationException;
use App\Models\Fulfillment;
use App\Models\Order;
use App\Models\OrderLine;
use App\Models\Refund;
use App\Models\Store;
use App\Services\FulfillmentService;
use App\Services\OrderService;
use App\Services\RefundService;
use App\Support\Money;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Show extends Component
{
    #[Locked]
    public int $storeId;

    #[Locked]
    public int $orderId;

    public string $refundAmount = '';

    public string $refundReason = '';

    /**
     * @var array<int, int>
     */
    public array $fulfillmentLineQuantities = [];

    public string $trackingCompany = '';

    public string $trackingNumber = '';

    public string $trackingUrl = '';

    public string $actionMessage = '';

    public function mount(Order $order): void
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        $order = Order::withoutGlobalScopes()
            ->where('store_id', $store->getKey())
            ->whereKey($order->getKey())
            ->first();

        abort_unless($order instanceof Order, 404);

        $this->storeId = $store->getKey();
        $this->orderId = $order->getKey();
        $this->resetFulfillmentLineQuantities($this->order());
    }

    public function confirmBankTransferPayment(OrderService $orders): void
    {
        try {
            $orders->confirmBankTransferPayment($this->order());
            $this->resetFulfillmentLineQuantities($this->order());
            $this->actionMessage = __('Payment confirmed');
            $this->dispatch('toast', type: 'success', message: __('Payment confirmed'));
        } catch (InvalidOrderOperationException $exception) {
            throw ValidationException::withMessages([
                'orderAction' => $exception->getMessage(),
            ]);
        }
    }

    public function processRefund(RefundService $refunds): void
    {
        $this->validate([
            'refundAmount' => ['nullable', 'numeric', 'min:0.01'],
            'refundReason' => ['nullable', 'string', 'max:500'],
        ]);

        $request = [
            'reason' => $this->refundReason !== '' ? $this->refundReason : null,
        ];

        if ($this->refundAmount !== '') {
            $request['amount'] = Money::fromDecimalString($this->refundAmount);
        }

        try {
            $refunds->process($this->order(), $request);
            $this->refundAmount = '';
            $this->refundReason = '';
            $this->actionMessage = __('Refund processed');
            $this->modal('refund-order')->close();
            $this->dispatch('toast', type: 'success', message: __('Refund processed'));
        } catch (InvalidRefundOperationException $exception) {
            throw ValidationException::withMessages([
                'refundAmount' => $exception->getMessage(),
            ]);
        }
    }

    public function createFulfillment(FulfillmentService $fulfillments): void
    {
        $this->validate([
            'fulfillmentLineQuantities' => ['array'],
            'trackingCompany' => ['nullable', 'string', 'max:255'],
            'trackingNumber' => ['nullable', 'string', 'max:255'],
            'trackingUrl' => ['nullable', 'url', 'max:2048'],
        ]);

        $lines = collect($this->fulfillmentLineQuantities)
            ->mapWithKeys(fn (mixed $quantity, int|string $lineId): array => [(int) $lineId => (int) $quantity])
            ->filter(fn (int $quantity): bool => $quantity > 0)
            ->all();

        if ($lines === []) {
            throw ValidationException::withMessages([
                'fulfillment' => __('At least one fulfillment line is required.'),
            ]);
        }

        try {
            $fulfillments->create($this->order(), $lines, [
                'tracking_company' => $this->trackingCompany !== '' ? $this->trackingCompany : null,
                'tracking_number' => $this->trackingNumber !== '' ? $this->trackingNumber : null,
                'tracking_url' => $this->trackingUrl !== '' ? $this->trackingUrl : null,
            ]);

            $this->trackingCompany = '';
            $this->trackingNumber = '';
            $this->trackingUrl = '';
            $this->resetFulfillmentLineQuantities($this->order());
            $this->actionMessage = __('Fulfillment created');
            $this->modal('fulfillment-order')->close();
            $this->dispatch('toast', type: 'success', message: __('Fulfillment created'));
        } catch (InvalidFulfillmentOperationException $exception) {
            throw ValidationException::withMessages([
                'fulfillment' => $exception->getMessage(),
            ]);
        }
    }

    public function …17931 chars truncated…ate\Validation\Rule;
use Livewire\Component;

class Form extends Component
{
    public ?Collection $collection = null;

    public string $title = '';

    public string $handle = '';

    public string $descriptionHtml = '';

    public string $status = 'active';

    public string $productSearch = '';

    public string $actionMessage = '';

    /**
     * @var array<int, int>
     */
    public array $assignedProductIds = [];

    public function mount(?Collection $collection = null): void
    {
        if ($collection?->exists) {
            $store = app('current_store');

            abort_unless($store instanceof Store && (int) $collection->store_id === $store->getKey(), 404);

            $this->collection = $collection->load('products');
            $this->fillFromCollection($this->collection);
        }
    }

    public function updatedTitle(): void
    {
        if ($this->collection === null && $this->handle === '') {
            $this->handle = Str::slug($this->title);
        }
    }

    public function addProduct(int $productId): void
    {
        if (! in_array($productId, $this->assignedProductIds, true)) {
            $this->assignedProductIds[] = $productId;
        }

        $this->productSearch = '';
    }

    public function removeProduct(int $productId): void
    {
        $this->assignedProductIds = array_values(array_filter(
            $this->assignedProductIds,
            fn (int $assignedProductId): bool => $assignedProductId !== $productId,
        ));
    }

    public function save(): void
    {
        $store = app('current_store');
        abort_unless($store instanceof Store, 404);

        $this->validate([
            'title' => ['required', 'string', 'max:255'],
            'handle' => [
                'required',
                'string',
                'max:255',
                Rule::unique('collections', 'handle')
                    ->where('store_id', $store->getKey())
                    ->ignore($this->collection?->getKey()),
            ],
            'descriptionHtml' => ['nullable', 'string', 'max:65535'],
            'status' => ['required', Rule::in(['draft', 'active', 'archived'])],
        ]);

        $attributes = [
            'store_id' => $store->getKey(),
            'title' => $this->title,
            'handle' => Str::slug($this->handle),
            'description_html' => $this->descriptionHtml ?: null,
            'type' => 'manual',
            'status' => $this->status,
        ];

        $collection = $this->collection instanceof Collection
            ? tap($this->collection)->update($attributes)
            : Collection::query()->create($attributes);

        $collection->products()->sync(collect($this->assignedProductIds)
            ->values()
            ->mapWithKeys(fn (int $productId, int $position): array => [$productId => ['position' => $position]])
            ->all());

        $this->collection = $collection->refresh()->load('products');
        $this->fillFromCollection($this->collection);

        $this->actionMessage = 'Collection saved';
        session()->flash('status', 'Collection saved');
        $this->dispatch('toast', type: 'success', message: __('Collection saved'));
    }

    public function searchResults(): SupportCollection
    {
        if (trim($this->productSearch) === '') {
            return collect();
        }

        return Product::query()
            ->whereNotIn('id', $this->assignedProductIds)
            ->where(function (Builder $query): void {
                $query
                    ->where('title', 'like', '%'.$this->productSearch.'%')
                    ->orWhere('handle', 'like', '%'.$this->productSearch.'%');
            })
            ->limit(5)
            ->get();
    }

    public function assignedProducts(): SupportCollection
    {
        if ($this->assignedProductIds === []) {
            return collect();
        }

        return Product::query()
            ->whereIn('id', $this->assignedProductIds)
            ->get()
            ->sortBy(fn (Product $product): int => array_search($product->getKey(), $this->assignedProductIds, true))
            ->values();
    }

    public function render(): mixed
    {
        return view('livewire.admin.collections.form', [
            'searchResults' => $this->searchResults(),
            'assignedProducts' => $this->assignedProducts(),
            'isEditing' => $this->collection !== null,
        ])->layout('layouts.app', [
            'title' => $this->collection ? $this->collection->title : __('Add collection'),
        ]);
    }

    private function fillFromCollection(Collection $collection): void
    {
        $this->title = $collection->title;
        $this->handle = $collection->handle;
        $this->descriptionHtml = (string) $collection->description_html;
        $this->status = $collection->status->value;
        $this->assignedProductIds = $collection->products->pluck('id')->map(fn (int $id): int => $id)->all();
    }
}


find app/Policies -type f -maxdepth 1 -print -exec sed -n '1,180p' {} \;

find app/Policies -type f -maxdepth 1 -print -exec sed -n '1,180p' '{}' "\\;"

app/Policies/CollectionPolicy.php
<?php

namespace App\Policies;

use App\Models\Collection;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class CollectionPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Collection $collection): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($collection));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Collection $collection): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($collection));
    }

    public function delete(User $user, Collection $collection): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($collection));
    }
}
app/Policies/ThemePolicy.php
<?php

namespace App\Policies;

use App\Models\Theme;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class ThemePolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isOwnerOrAdmin($user);
    }

    public function view(User $user, Theme $theme): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($theme));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerOrAdmin($user);
    }

    public function update(User $user, Theme $theme): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($theme));
    }

    public function publish(User $user, Theme $theme): bool
    {
        return $this->update($user, $theme);
    }

    public function delete(User $user, Theme $theme): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($theme));
    }
}
app/Policies/ProductPolicy.php
<?php

namespace App\Policies;

use App\Models\Product;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class ProductPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Product $product): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($product));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Product $product): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($product));
    }

    public function delete(User $user, Product $product): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($product));
    }

    public function archive(User $user, Product $product): bool
    {
        return $this->delete($user, $product);
    }

    public function restore(User $user, Product $product): bool
    {
        return $this->delete($user, $product);
    }
}
app/Policies/FulfillmentPolicy.php
<?php

namespace App\Policies;

use App\Models\Fulfillment;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class FulfillmentPolicy
{
    use ChecksStoreRole;

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Fulfillment $fulfillment): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($fulfillment));
    }

    public function cancel(User $user, Fulfillment $fulfillment): bool
    {
        return $this->update($user, $fulfillment);
    }
}
app/Policies/StorePolicy.php
<?php

namespace App\Policies;

use App\Models\Store;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class StorePolicy
{
    use ChecksStoreRole;

    public function view(User $user, Store $store): bool
    {
        return $this->isAnyRole($user, $store->getKey());
    }

    public function update(User $user, Store $store): bool
    {
        return $this->isOwnerOrAdmin($user, $store->getKey());
    }

    public function delete(User $user, Store $store): bool
    {
        return $this->isOwnerOnly($user, $store->getKey());
    }
}
app/Policies/RefundPolicy.php
<?php

namespace App\Policies;

use App\Models\Refund;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class RefundPolicy
{
    use ChecksStoreRole;

    public function create(User $user): bool
    {
        return $this->isOwnerOrAdmin($user);
    }

    public function view(User $user, Refund $refund): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($refund));
    }
}
app/Policies/CustomerPolicy.php
<?php

namespace App\Policies;

use App\Models\Customer;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class CustomerPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Customer $customer): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($customer));
    }

    public function update(User $user, Customer $customer): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($customer));
    }
}
app/Policies/PagePolicy.php
<?php

namespace App\Policies;

use App\Models\Page;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class PagePolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function view(User $user, Page $page): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($page));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Page $page): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($page));
    }

    public function delete(User $user, Page $page): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($page));
    }
}
app/Policies/OrderPolicy.php
<?php

namespace App\Policies;

use App\Models\Order;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class OrderPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Order $order): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($order));
    }

    public function update(User $user, Order $order): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($order));
    }

    public function cancel(User $user, Order $order): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($order));
    }

    public function createFulfillment(User $user, Order $order): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($order));
    }

    public function createRefund(User $user, Order $order): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($order));
    }
}
app/Policies/DiscountPolicy.php
<?php

namespace App\Policies;

use App\Models\Discount;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class DiscountPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Discount $discount): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($discount));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Discount $discount): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($discount));
    }

    public function delete(User $user, Discount $discount): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($discount));
    }
}


Output:
Chunk ID: 380324
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1991
Output:
app/Policies/CollectionPolicy.php
<?php

namespace App\Policies;

use App\Models\Collection;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class CollectionPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Collection $collection): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($collection));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Collection $collection): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($collection));
    }

    public function delete(User $user, Collection $collection): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($collection));
    }
}
app/Policies/ThemePolicy.php
<?php

namespace App\Policies;

use App\Models\Theme;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class ThemePolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isOwnerOrAdmin($user);
    }

    public function view(User $user, Theme $theme): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($theme));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerOrAdmin($user);
    }

    public function update(User $user, Theme $theme): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($theme));
    }

    public function publish(User $user, Theme $theme): bool
    {
        return $this->update($user, $theme);
    }

    public function delete(User $user, Theme $theme): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($theme));
    }
}
app/Policies/ProductPolicy.php
<?php

namespace App\Policies;

use App\Models\Product;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class ProductPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Product $product): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($product));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Product $product): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($product));
    }

    public function delete(User $user, Product $product): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($product));
    }

    public function archive(User $user, Product $product): bool
    {
        return $this->delete($user, $product);
    }

    public function restore(User $user, Product $product): bool
    {
        return $this->delete($user, $product);
    }
}
app/Policies/FulfillmentPolicy.php
<?php

namespace App\Policies;

use App\Models\Fulfillment;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class FulfillmentPolicy
{
    use ChecksStoreRole;

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Fulfillment $fulfillment): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($fulfillment));
    }

    public function cancel(User $user, Fulfillment $fulfillment): bool
    {
        return $this->update($user, $fulfillment);
    }
}
app/Policies/StorePolicy.php
<?php

namespace App\Policies;

use App\Models\Store;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class StorePolicy
{
    use ChecksStoreRole;

    public function view(User $user, Store $store): bool
    {
        return $this->isAnyRole($user, $store->getKey());
    }

    public function update(User $user, Store $store): bool
    {
        return $this->isOwnerOrAdmin($user, $store->getKey());
    }

    public function delete(User $user, Store $store): bool
    {
        return $this->isOwnerOnly($user, $store->getKey());
    }
}
app/Policies/RefundPolicy.php
<?php

namespace App\Policies;

use App\Models\Refund;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class RefundPolicy
{
    use ChecksStoreRole;

    public function create(User $user): bool
    {
        return $this->isOwnerOrAdmin($user);
    }

    public function view(User $user, Refund $refund): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($refund));
    }
}
app/Policies/CustomerPolicy.php
<?php

namespace App\Policies;

use App\Models\Customer;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class CustomerPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Customer $customer): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($customer));
    }

    public function update(User $user, Customer $customer): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($customer));
    }
}
app/Policies/PagePolicy.php
<?php

namespace App\Policies;

use App\Models\Page;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class PagePolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function view(User $user, Page $page): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($page));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Page $page): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($page));
    }

    public function delete(User $user, Page $page): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($page));
    }
}
app/Policies/OrderPolicy.php
<?php

namespace App\Policies;

use App\Models\Order;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class OrderPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Order $order): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($order));
    }

    public function update(User $user, Order $order): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($order));
    }

    public function cancel(User $user, Order $order): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($order));
    }

    public function createFulfillment(User $user, Order $order): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($order));
    }

    public function createRefund(User $user, Order $order): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($order));
    }
}
app/Policies/DiscountPolicy.php
<?php

namespace App\Policies;

use App\Models\Discount;
use App\Models\User;
use App\Traits\ChecksStoreRole;

class DiscountPolicy
{
    use ChecksStoreRole;

    public function viewAny(User $user): bool
    {
        return $this->isAnyRole($user);
    }

    public function view(User $user, Discount $discount): bool
    {
        return $this->isAnyRole($user, $this->storeIdForModel($discount));
    }

    public function create(User $user): bool
    {
        return $this->isOwnerAdminOrStaff($user);
    }

    public function update(User $user, Discount $discount): bool
    {
        return $this->isOwnerAdminOrStaff($user, $this->storeIdForModel($discount));
    }

    public function delete(User $user, Discount $discount): bool
    {
        return $this->isOwnerOrAdmin($user, $this->storeIdForModel($discount));
    }
}


Output:
Chunk ID: aeb6fb
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 547
Output:
<?php

namespace App\Traits;

use App\Enums\StoreUserRole;
use App\Models\Store;
use App\Models\User;

trait ChecksStoreRole
{
    public function getStoreRole(User $user, ?int $storeId = null): ?StoreUserRole
    {
        $storeId ??= $this->currentStoreId();

        if (! $storeId) {
            return null;
        }

        return $user->roleForStoreId($storeId);
    }

    /**
     * @param  array<int, StoreUserRole>  $roles
     */
    public function hasRole(User $user, ?int $storeId, array $roles): bool
    {
        $role = $this->getStoreRole($user, $storeId);

        return $role !== null && in_array($role, $roles, true);
    }

    public function isOwnerOnly(User $user, ?int $storeId = null): bool
    {
        return $this->hasRole($user, $storeId, [StoreUserRole::Owner]);
    }

    public function isOwnerOrAdmin(User $user, ?int $storeId = null): bool
    {
        return $this->hasRole($user, $storeId, [StoreUserRole::Owner, StoreUserRole::Admin]);
    }

    public function isOwnerAdminOrStaff(User $user, ?int $storeId = null): bool
    {
        return $this->hasRole($user, $storeId, [StoreUserRole::Owner, StoreUserRole::Admin, StoreUserRole::Staff]);
    }

    public function isAnyRole(User $user, ?int $storeId = null): bool
    {
        return $this->getStoreRole($user, $storeId) !== null;
    }

    protected function currentStoreId(): ?int
    {
        if (! app()->bound('current_store')) {
            return null;
        }

        $store = app('current_store');

        return $store instanceof Store ? $store->getKey() : null;
    }

    protected function storeIdForModel(object $model): ?int
    {
        if (method_exists($model, 'getAttribute')) {
            $storeId = $model->getAttribute('store_id');

            return is_numeric($storeId) ? (int) $storeId : null;
        }

        if (property_exists($model, 'store_id')) {
            return is_numeric($model->store_id) ? (int) $model->store_id : null;
        }

        return null;
    }
}
<?php

namespace App\Enums;

enum StoreUserRole: string
{
    case Owner = 'owner';
    case Admin = 'admin';
    case Staff = 'staff';
    case Support = 'support';
}


Output:
Chunk ID: 73bf37
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 6983
Output:
<?php

namespace App\Livewire\Admin\Orders;

use App\Enums\RefundStatus;
use App\Exceptions\InvalidFulfillmentOperationException;
use App\Exceptions\InvalidOrderOperationException;
use App\Exceptions\InvalidRefundOperationException;
use App\Models\Fulfillment;
use App\Models\Order;
use App\Models\OrderLine;
use App\Models\Refund;
use App\Models\Store;
use App\Services\FulfillmentService;
use App\Services\OrderService;
use App\Services\RefundService;
use App\Support\Money;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Show extends Component
{
    #[Locked]
    public int $storeId;

    #[Locked]
    public int $orderId;

    public string $refundAmount = '';

    public string $refundReason = '';

    /**
     * @var array<int, int>
     */
    public array $fulfillmentLineQuantities = [];

    public string $trackingCompany = '';

    public string $trackingNumber = '';

    public string $trackingUrl = '';

    public string $actionMessage = '';

    public function mount(Order $order): void
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        $order = Order::withoutGlobalScopes()
            ->where('store_id', $store->getKey())
            ->whereKey($order->getKey())
            ->first();

        abort_unless($order instanceof Order, 404);

        $this->storeId = $store->getKey();
        $this->orderId = $order->getKey();
        $this->resetFulfillmentLineQuantities($this->order());
    }

    public function confirmBankTransferPayment(OrderService $orders): void
    {
        try {
            $orders->confirmBankTransferPayment($this->order());
            $this->resetFulfillmentLineQuantities($this->order());
            $this->actionMessage = __('Payment confirmed');
            $this->dispatch('toast', type: 'success', message: __('Payment confirmed'));
        } catch (InvalidOrderOperationException $exception) {
            throw ValidationException::withMessages([
                'orderAction' => $exception->getMessage(),
            ]);
        }
    }

    public function processRefund(RefundService $refunds): void
    {
        $this->validate([
            'refundAmount' => ['nullable', 'numeric', 'min:0.01'],
            'refundReason' => ['nullable', 'string', 'max:500'],
        ]);

        $request = [
            'reason' => $this->refundReason !== '' ? $this->refundReason : null,
        ];

        if ($this->refundAmount !== '') {
            $request['amount'] = Money::fromDecimalString($this->refundAmount);
        }

        try {
            $refunds->process($this->order(), $request);
            $this->refundAmount = '';
            $this->refundReason = '';
            $this->actionMessage = __('Refund processed');
            $this->modal('refund-order')->close();
            $this->dispatch('toast', type: 'success', message: __('Refund processed'));
        } catch (InvalidRefundOperationException $exception) {
            throw ValidationException::withMessages([
                'refundAmount' => $exception->getMessage(),
            ]);
        }
    }

    public function createFulfillment(FulfillmentService $fulfillments): void
    {
        $this->validate([
            'fulfillmentLineQuantities' => ['array'],
            'trackingCompany' => ['nullable', 'string', 'max:255'],
            'trackingNumber' => ['nullable', 'string', 'max:255'],
            'trackingUrl' => ['nullable', 'url', 'max:2048'],
        ]);

        $lines = collect($this->fulfillmentLineQuantities)
            ->mapWithKeys(fn (mixed $quantity, int|string $lineId): array => [(int) $lineId => (int) $quantity])
            ->filter(fn (int $quantity): bool => $quantity > 0)
            ->all();

        if ($lines === []) {
            throw ValidationException::withMessages([
                'fulfillment' => __('At least one fulfillment line is required.'),
            ]);
        }

        try {
            $fulfillments->create($this->order(), $lines, [
                'tracking_company' => $this->trackingCompany !== '' ? $this->trackingCompany : null,
                'tracking_number' => $this->trackingNumber !== '' ? $this->trackingNumber : null,
                'tracking_url' => $this->trackingUrl !== '' ? $this->trackingUrl : null,
            ]);

            $this->trackingCompany = '';
            $this->trackingNumber = '';
            $this->trackingUrl = '';
            $this->resetFulfillmentLineQuantities($this->order());
            $this->actionMessage = __('Fulfillment created');
            $this->modal('fulfillment-order')->close();
            $this->dispatch('toast', type: 'success', message: __('Fulfillment created'));
        } catch (InvalidFulfillmentOperationException $exception) {
            throw ValidationException::withMessages([
                'fulfillment' => $exception->getMessage(),
            ]);
        }
    }

    public function markFulfillmentShipped(int $fulfillmentId, FulfillmentService $fulfillments): void
    {
        try {
            $fulfillments->markShipped($this->fulfillment($fulfillmentId));
            $this->actionMessage = __('Fulfillment marked as shipped');
            $this->dispatch('toast', type: 'success', message: __('Fulfillment marked as shipped'));
        } catch (InvalidFulfillmentOperationException $exception) {
            throw ValidationException::withMessages([
                'fulfillment' => $exception->getMessage(),
            ]);
        }
    }

    public function markFulfillmentDelivered(int $fulfillmentId, FulfillmentService $fulfillments): void
    {
        try {
            $fulfillments->markDelivered($this->fulfillment($fulfillmentId));
            $this->actionMessage = __('Fulfillment marked as delivered');
            $this->dispatch('toast', type: 'success', message: __('Fulfillment marked as delivered'));
        } catch (InvalidFulfillmentOperationException $exception) {
            throw ValidationException::withMessages([
                'fulfillment' => $exception->getMessage(),
            ]);
        }
    }

    public function render(): mixed
    {
        $order = $this->order();

        return view('livewire.admin.orders.show', [
            'order' => $order,
            'remainingFulfillmentQuantities' => $this->remainingFulfillmentQuantities($order),
            'refundableAmount' => $this->refundableAmount($order),
        ])->layout('layouts.app', [
            'title' => $order->order_number,
        ]);
    }

    private function order(): Order
    {
        return Order::withoutGlobalScopes()
            ->with([
                'customer',
                'lines.fulfillmentLines',
                'payments',
                'refunds',
                'fulfillments.lines.orderLine',
            ])
            ->where('store_id', $this->storeId)
            ->whereKey($this->orderId)
            ->firstOrFail();
    }

    private function fulfillment(int $fulfillmentId): Fulfillment
    {
        $fulfillment = $this->order()
            ->fulfillments
            ->firstWhere('id', $fulfillmentId);

        abort_unless($fulfillment instanceof Fulfillment, 404);

        return $fulfillment;
    }

    /**
     * @return array<int, int>
     */
    private function remainingFulfillmentQuantities(Order $order): array
    {
        return $order->lines
            ->mapWithKeys(function (OrderLine $line): array {
                $fulfilled = $line->fulfillmentLines->sum('quantity');

                return [$line->getKey() => max(0, $line->quantity - $fulfilled)];
            })
            ->all();
    }

    private function resetFulfillmentLineQuantities(Order $order): void
    {
        $this->fulfillmentLineQuantities = $this->remainingFulfillmentQuantities($order);
    }

    private function refundableAmount(Order $order): int
    {
        $refunded = $order->refunds
            ->reject(fn (Refund $refund): bool => $refund->status === RefundStatus::Failed)
            ->sum('amount');

        return max(0, $order->total_amount - $refunded);
    }
}
<?php

namespace App\Livewire\Admin\Products;

use App\Enums\MediaStatus;
use App\Enums\MediaType;
use App\Enums\ProductStatus;
use App\Enums\VariantStatus;
use App\Jobs\ProcessMediaUpload;
use App\Models\Collection;
use App\Models\InventoryItem;
use App\Models\Product;
use App\Models\ProductMedia;
use App\Models\ProductOption;
use App\Models\ProductOptionValue;
use App\Models\ProductVariant;
use App\Models\Store;
use App\Services\ProductService;
use App\Support\Money;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Livewire\Component;
use Livewire\WithFileUploads;

class Form extends Component
{
    use WithFileUploads;

    public ?Product $product = null;

    public string $title = '';

    public string $descriptionHtml = '';

    public string $status = 'active';

    public string $vendor = '';

    public string $productType = '';

    public string $tags = '';

    public string $handle = '';

    public ?string $publishedAt = null;

    /**
     * @var array<int, int>
     */
    public array $collectionIds = [];

    /**
     * @var array<int, array{name: string, values: string}>
     */
    public array $options = [];

    /**
     * @var array<int, array{id: int|null, label: string, sku: string, price: string, compareAtPrice: string, quantity: int, requiresShipping: bool, options: array<string, string>}>
     */
    public array $variants = [];

    /**
     * @var array<int, array{id: int, url: string, exists: bool, altText: string, position: int, status: string}>
     */
    public array $media = [];

    /**
     * @var array<int, mixed>
     */
    public array $newMedia = [];

    public function mount(?Product $product = null): void
    {
        if ($product?->exists) {
            $store = app('current_store');

            abort_unless($store instanceof Store && (int) $product->store_id === $store->getKey(), 404);

            $this->product = $product->load(['options.values', 'variants.inventoryItem', 'variants.optionValues.option', 'collections', 'media']);
            $this->fillFromProduct($this->product);

            return;
        }

        $this->variants = [[
            'id' => null,
            'label' => 'Default',
            'sku' => '',
            'price' => '0.00',
            'compareAtPrice' => '',
            'quantity' => 0,
            'requiresShipping' => true,
            'options' => [],
        ]];
    }

    public function updatedTitle(): void
    {
        if ($this->product === null && $this->handle === '') {
            $this->handle = Str::slug($this->title);
        }
    }

    public function addOption(): void
    {
        $this->options[] = [
            'name' => '',
            'values' => '',
        ];
    }

    public function removeOption(int $index): void
    {
        unset($this->options[$index]);
        $this->options = array_values($this->options);
    }

    public function generateVariants(): void
    {
        $optionPayload = $this->optionPayload();

        if ($optionPayload === []) {
            $template = $this->variants[0] ?? [];
            $this->variants = [[
                'id' => $template['id'] ?? null,
                'label' => 'Default',
                'sku' => $template['sku'] ?? '',
                'price' => $template['price'] ?? '0.00',
                'compareAtPrice' => $template['compareAtPrice'] ?? '',
                'quantity' => (int) ($template['quantity'] ?? 0),
                'requiresShipping' => (bool) ($template['requiresShipping'] ?? true),
                'options' => [],
            ]];

            return;
        }

        $existingVariants = collect($this->variants)
            ->keyBy(fn (array $variant): string => $this->variantOptionKey($variant['options'] ?? []));
        $template = $this->variants[0] ?? [];

        $this->variants = collect($this->optionCombinations($optionPayload))
            ->map(function (array $options) use ($existingVariants, $template): array {
                $existingVariant = $existingVariants->get($this->variantOptionKey($options));

                return [
                    'id' => $existingVariant['id'] ?? null,
                    'label' => $this->variantLabel($options),
                    'sku' => $existingVariant['sku'] ?? '',
                    'price' => $existingVariant['price'] ?? ($template['price'] ?? '0.00'),
                    'compareAtPrice' => $existingVariant['compareAtPrice'] ?? ($template['compareAtPrice'] ?? ''),
                    'quantity' => (int) ($existingVariant['quantity'] ?? 0),
                    'requiresShipping' => (bool) ($existingVariant['requiresShipping'] ?? ($template['requiresShipping'] ?? true)),
                    'options' => $options,
                ];
            })
            ->values()
            ->all();
    }

    public function save(): void
    {
        $store = app('current_store');
        abort_unless($store instanceof Store, 404);

        $this->ensureVariantMatrixMatchesOptions();

        $this->validate([
            'title' => ['required', 'string', 'max:255'],
            'descriptionHtml' => ['nullable', 'string', 'max:65535'],
            'status' => ['required', Rule::in(['draft', 'active', 'archived'])],
            'vendor' => ['nullable', 'string', 'max:255'],
            'productType' => ['nullable', 'string', 'max:255'],
            'tags' => ['nullable', 'string'],
            'handle' => [
                'required',
                'string',
                'max:255',
                Rule::unique('products', 'handle')
                    ->where('store_id', $store->getKey())
                    ->ignore($this->product?->getKey()),
            ],
            'variants' => ['required', 'array', 'min:1'],
            'variants.*.sku' => ['nullable', 'string', 'max:255'],
            'variants.*.price' => ['required', 'numeric', 'min:0'],
            'variants.*.compareAtPrice' => ['nullable', 'numeric', 'min:0'],
            'variants.*.quantity' => ['required', 'integer', 'min:0'],
            'variants.*.requiresShipping' => ['boolean'],
            'newMedia' => ['array', 'max:10'],
            'newMedia.*' => ['image', 'mimes:jpg,jpeg,png,gif,webp', 'max:5120'],
        ]);

        if (! $this->validateActiveVariantPricing() || ! $this->validateVariantSkus($store)) {
            return;
        }

        $product = DB::transaction(function () use ($store): Product {
            $productService = app(ProductService::class);

            if ($this->product === null) {
                $product = $productService->create($store, [
                    ...$this->productPayload(),
                    'options' => $this->optionPayload(),
                    'variants' => $this->variantPayload($store),
                ]);
            } else {
                $payload = $this->productPayload();
                $requestedStatus = ProductStatus::from((string) $payload['status']);
                unset($payload['status']);

                $product = $productService->update($this->product, $payload);
                $this->syncProductOptions($product);
                $this->syncExistingVariants($product, $store);

                if ($requestedStatus !== $product->refresh()->status) {
                    $productService->transitionStatus($product, $requestedStatus);
                }
            }

            $product->collections()->sync($this->collectionIds);

            return $product->refresh();
        });

        if ($this->newMedia !== []) {
            $this->storeNewMedia($product);
        }

        $this->product = $product->load(['options.values', 'variants.inventoryItem', 'variants.optionValues.option', 'collections', 'media']);
        $this->fillFromProduct($this->product);

        session()->flash('status', 'Product saved');
        $this->dispatch('toast', type: 'success', message: __('Product saved'));
    }

    public function uploadMedia(): void
    {
        $product = $this->productForAction();

        $this->validate([
            'newMedia' => ['required', 'array', 'max:10'],
            'newMedia.*' => ['image', 'mimes:jpg,jpeg,png,gif,webp', 'max:5120'],
        ]);

        $this->storeNewMedia($product);

        $this->product = $product->refresh()->load(['options.values', 'variants.inventoryItem', 'variants.optionValues.option', 'collections', 'media']);
        $this->fillFromProduct($this->product);

        session()->flash('status', 'Media uploaded');
        $this->dispatch('toast', type: 'success', message: __('Media uploaded'));
    }

    public function updateMediaAlt(int $mediaId): void
    {
        $index = $this->mediaIndex($mediaId);

        $this->validate([
            "media.{$index}.altText" => ['nullable', 'string', 'max:255'],
        ]);

        $media = $this->mediaRecord($mediaId);
        $altText = trim((string) $this->media[$index]['altText']);

        $media->forceFill([
            'alt_text' => $altText === '' ? null : $altText,
        ])->save();

        $this->refreshProductMedia();

        session()->flash('status', 'Media updated');
    }

    public function moveMedia(int $mediaId, string $direction): void
    {
        $ids = collect($this->media)->pluck('id')->map(fn (int $id): int => $id)->all();
        $index = array_search($mediaId, $ids, true);

        if ($index === false) {
            return;
        }

        $swapIndex = match ($direction) {
            'up' => $index - 1,
            'down' => $index + 1,
            default => $index,
        };

        if (! isset($ids[$swapIndex])) {
            return;
        }

        [$ids[$index], $ids[$swapIndex]] = [$ids[$swapIndex], $ids[$index]];

        $this->reorderMedia($ids);
    }

    /**
     * @param  array<int, int>  $order
     */
    public function reorderMedia(array $order): void
    {
        $product = $this->productForAction();
        $validIds = ProductMedia::withoutGlobalScopes()
            ->where('product_id', $product->getKey())
            ->whereIn('id', $order)
            ->pluck('id')
            ->all();

        $orderedIds = collect($order)
            ->map(fn (mixed $mediaId): int => (int) $mediaId)
            ->intersect($validIds)
            ->values();

        DB::transaction(function () use ($orderedIds): void {
            foreach ($orderedIds as $position => $mediaId) {
                ProductMedia::withoutGlobalScopes()
                    ->whereKey($mediaId)
                    ->update(['position' => $position]);
            }
        });

        $this->refreshProductMedia();
    }

    public function removeMedia(int $mediaId): void
    {
        $media = $this->mediaRecord($mediaId);

        $media->delete();

        $this->reorderMedia(
            collect($this->media)
                ->pluck('id')
                ->reject(fn (int $id): bool => $id === $mediaId)
                ->values()
                ->all(),
        );

        session()->flash('status', 'Media removed');
    }

    public function deleteProduct(): void
    {
        abort_unless($this->product instanceof Product, 404);

        app(ProductService::class)->transitionStatus($this->product, ProductStatus::Archived);

        session()->flash('status', 'Product saved');
        $this->redirectRoute('admin.products.index', navigate: true);
    }

    public function render(): mixed
    {
        return view('livewire.admin.products.form', [
            'availableCollections' => Collection::query()->orderBy('title')->get(),
            'isEditing' => $this->product !== null,
        ])->layout('layouts.app', [
            'title' => $this->product ? $this->product->title : __('Add product'),
        ]);
    }

    private function fillFromProduct(Product $product): void
    {
        $this->title = $product->title;
        $this->descriptionHtml = (string) $product->description_html;
        $this->status = $product->status->value;
        $this->vendor = (string) $product->vendor;
        $this->productType = (string) $product->product_type;
        $this->tags = implode(', ', $product->tags ?? []);
        $this->handle = $product->handle;
        $this->publishedAt = $product->published_at?->format('Y-m-d\TH:i');
        $this->collectionIds = $product->collections->pluck('id')->map(fn (int $id): int => $id)->all();
        $this->media = $product->media
            ->map(fn (ProductMedia $media): array => [
                'id' => $media->getKey(),
                'url' => Storage::disk('public')->url($media->storage_key),
                'exists' => Storage::disk('public')->exists($media->storage_key),
                'altText' => (string) $media->alt_text,
                'position' => $media->position,
                'status' => $media->status->value,
            ])
            ->values()
            ->all();
        $this->options = $product->options
            ->map(fn (ProductOption $option): array => [
                'name' => $option->name,
                'values' => $option->values->pluck('value')->implode(', '),
            ])
            ->all();
        $this->variants = $product->variants
            ->map(fn (ProductVariant $variant): array => [
                'id' => $variant->getKey(),
                'label' => $variant->optionValues->isEmpty()
                    ? 'Default'
                    : $variant->optionValues->map(fn (ProductOptionValue $value): string => $value->value)->implode(' / '),
                'sku' => (string) $variant->sku,
                'price' => number_format($variant->price_amount / 100, 2, '.', ''),
                'compareAtPrice' => $variant->compare_at_amount ? number_format($variant->compare_at_amount / 100, 2, '.', '') : '',
                'quantity' => $variant->inventoryItem?->quantity_on_hand ?? 0,
                'requiresShipping' => $variant->requires_shipping,
                'options' => $variant->optionValues
                    ->mapWithKeys(fn (ProductOptionValue $value): array => [$value->option?->name ?? 'Option' => $value->value])
                    ->all(),
            ])
            ->values()
            ->all();
    }

    /**
     * @return array<string, mixed>
     */
    private function productPayload(): array
    {
        return [
            'title' => $this->title,
            'description_html' => $this->descriptionHtml ?: null,
<?php

namespace App\Livewire\Admin\Collections;

use App\Models\Collection;
use App\Models\Product;
use App\Models\Store;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection as SupportCollection;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Livewire\Component;

class Form extends Component
{
    public ?Collection $collection = null;

    public string $title = '';

    public string $handle = '';

    public string $descriptionHtml = '';

    public string $status = 'active';

    public string $productSearch = '';

    public string $actionMessage = '';

    /**
     * @var array<int, int>
     */
    public array $assignedProductIds = [];

    public function mount(?Collection $collection = null): void
    {
        if ($collection?->exists) {
            $store = app('current_store');

            abort_unless($store instanceof Store && (int) $collection->store_id === $store->getKey(), 404);

            $this->collection = $collection->load('products');
            $this->fillFromCollection($this->collection);
        }
    }

    public function updatedTitle(): void
    {
        if ($this->collection === null && $this->handle === '') {
            $this->handle = Str::slug($this->title);
        }
    }

    public function addProduct(int $productId): void
    {
        if (! in_array($productId, $this->assignedProductIds, true)) {
            $this->assignedProductIds[] = $productId;
        }

        $this->productSearch = '';
    }

    public function removeProduct(int $productId): void
    {
        $this->assignedProductIds = array_values(array_filter(
            $this->assignedProductIds,
            fn (int $assignedProductId): bool => $assignedProductId !== $productId,
        ));
    }

    public function save(): void
    {
        $store = app('current_store');
        abort_unless($store instanceof Store, 404);

        $this->validate([
            'title' => ['required', 'string', 'max:255'],
            'handle' => [
                'required',
                'string',
                'max:255',
                Rule::unique('collections', 'handle')
                    ->where('store_id', $store->getKey())
                    ->ignore($this->collection?->getKey()),
            ],
            'descriptionHtml' => ['nullable', 'string', 'max:65535'],
            'status' => ['required', Rule::in(['draft', 'active', 'archived'])],
        ]);

        $attributes = [
            'store_id' => $store->getKey(),
            'title' => $this->title,
            'handle' => Str::slug($this->handle),
            'description_html' => $this->descriptionHtml ?: null,
            'type' => 'manual',
            'status' => $this->status,
        ];

        $collection = $this->collection instanceof Collection
            ? tap($this->collection)->update($attributes)
            : Collection::query()->create($attributes);

        $collection->products()->sync(collect($this->assignedProductIds)
            ->values()
            ->mapWithKeys(fn (int $productId, int $position): array => [$productId => ['position' => $position]])
            ->all());

        $this->collection = $collection->refresh()->load('products');
        $this->fillFromCollection($this->collection);

        $this->actionMessage = 'Collection saved';
        session()->flash('status', 'Collection saved');
        $this->dispatch('toast', type: 'success', message: __('Collection saved'));
    }

    public function searchResults(): SupportCollection
    {
        if (trim($this->productSearch) === '') {
            return collect();
        }

        return Product::query()
            ->whereNotIn('id', $this->assignedProductIds)
            ->where(function (Builder $query): void {
                $query
                    ->where('title', 'like', '%'.$this->productSearch.'%')
                    ->orWhere('handle', 'like', '%'.$this->productSearch.'%');
            })
            ->limit(5)
            ->get();
    }

    public function assignedProducts(): SupportCollection
    {
        if ($this->assignedProductIds === []) {
            return collect();
        }

        return Product::query()
            ->whereIn('id', $this->assignedProductIds)
            ->get()
            ->sortBy(fn (Product $product): int => array_search($product->getKey(), $this->assignedProductIds, true))
            ->values();
    }

    public function render(): mixed
    {
        return view('livewire.admin.collections.form', [
            'searchResults' => $this->searchResults(),
            'assignedProducts' => $this->assignedProducts(),
            'isEditing' => $this->collection !== null,
        ])->layout('layouts.app', [
            'title' => $this->collection ? $this->collection->title : __('Add collection'),
        ]);
    }

    private function fillFromCollection(Collection $collection): void
    {
        $this->title = $collection->title;
        $this->handle = $collection->handle;
        $this->descriptionHtml = (string) $collection->description_html;
        $this->status = $collection->status->value;
        $this->assignedProductIds = $collection->products->pluck('id')->map(fn (int $id): int => $id)->all();
    }
}


rg -n "public function (save|delete|remove|create|update|process|confirm|mark|add|upload|store|generate|reorder|move|set|toggle|publish|duplicate|rebuild|createToken|createWebhook|revoke|test|export)" app/Livewire/Admin -S


rg -n "public function (save|delete|remove|create|update|process|confirm|mark|add|upload|store|generate|reorder|move|set|toggle|publish|duplicate|rebuild|createToken|createWebhook|revoke|test|export)" app/Livewire/Admin -S

rg -n 'public function (save|delete|remove|create|update|process|confirm|mark|add|upload|store|generate|reorder|move|set|toggle|publish|duplicate|rebuild|createToken|createWebhook|revoke|test|export)' app/Livewire/Admin -S

Question: public function (save|delete|remove|create|update|process|confirm|mark|add|upload|store|generate|reorder|move|set|toggle|publish|duplicate|rebuild|createToken|createWebhook|revoke|test|export)

app/Livewire/Admin/Developers/Index.php:55:    public function generateToken(WebhookService $webhooks): void
app/Livewire/Admin/Developers/Index.php:73:    public function revokeToken(int $tokenId): void
app/Livewire/Admin/Developers/Index.php:99:    public function saveWebhook(WebhookService $webhooks): void
app/Livewire/Admin/Developers/Index.php:130:    public function deleteWebhook(int $webhookId): void
app/Livewire/Admin/Analytics/Index.php:92:    public function updatedDateRange(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:97:    public function updatedCustomStartDate(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:104:    public function updatedCustomEndDate(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:111:    public function updatedChannelFilter(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:116:    public function updatedDeviceFilter(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:160:    public function exportCsv(AnalyticsService $analytics): void
app/Livewire/Admin/Themes/Editor.php:70:    public function saveFile(): void
app/Livewire/Admin/Themes/Editor.php:92:    public function save(ThemeSettingsService $settings): void
app/Livewire/Admin/Themes/Editor.php:110:    public function publish(ThemeSettingsService $settings): void
app/Livewire/Admin/Themes/Index.php:36:    public function publishTheme(int $themeId, ThemeSettingsService $settings): void
app/Livewire/Admin/Themes/Index.php:62:    public function duplicateTheme(int $themeId): void
app/Livewire/Admin/Themes/Index.php:107:    public function deleteTheme(int $themeId): void
app/Livewire/Admin/Pages/Form.php:53:    public function updatedTitle(string $title): void
app/Livewire/Admin/Pages/Form.php:62:    public function save(NavigationService $navigation): void
app/Livewire/Admin/Pages/Form.php:111:    public function deletePage(NavigationService $navigation): void
app/Livewire/Admin/Pages/Index.php:36:    public function updatedSearch(): void
app/Livewire/Admin/Collections/Form.php:47:    public function updatedTitle(): void
app/Livewire/Admin/Collections/Form.php:54:    public function addProduct(int $productId): void
app/Livewire/Admin/Collections/Form.php:63:    public function removeProduct(int $productId): void
app/Livewire/Admin/Collections/Form.php:71:    public function save(): void
app/Livewire/Admin/Collections/Index.php:19:    public function updatedSearch(): void
app/Livewire/Admin/Collections/Index.php:24:    public function updatedStatusFilter(): void
app/Livewire/Admin/Collections/Index.php:29:    public function deleteCollection(int $id): void
app/Livewire/Admin/Orders/Show.php:65:    public function confirmBankTransferPayment(OrderService $orders): void
app/Livewire/Admin/Orders/Show.php:79:    public function processRefund(RefundService $refunds): void
app/Livewire/Admin/Orders/Show.php:108:    public function createFulfillment(FulfillmentService $fulfillments): void
app/Livewire/Admin/Orders/Show.php:149:    public function markFulfillmentShipped(int $fulfillmentId, FulfillmentService $fulfillments): void
app/Livewire/Admin/Orders/Show.php:162:    public function markFulfillmentDelivered(int $fulfillmentId, FulfillmentService $fulfillments): void
app/Livewire/Admin/Orders/Index.php:46:    public function updatedSearch(): void
app/Livewire/Admin/Orders/Index.php:51:    public function updatedStatusFilter(): void
app/Livewire/Admin/Orders/Index.php:56:    public function updatedFinancialStatusFilter(): void
app/Livewire/Admin/Orders/Index.php:61:    public function updatedFulfillmentStatusFilter(): void
app/Livewire/Admin/Orders/Index.php:66:    public function updatedDateFrom(): void
app/Livewire/Admin/Orders/Index.php:71:    public function updatedDateTo(): void
app/Livewire/Admin/Inventory/Index.php:19:    public function updatedSearch(): void
app/Livewire/Admin/Inventory/Index.php:24:    public function updatedStockFilter(): void
app/Livewire/Admin/Search/Settings.php:49:    public function addSynonymGroup(): void
app/Livewire/Admin/Search/Settings.php:54:    public function removeSynonymGroup(int $index): void
app/Livewire/Admin/Search/Settings.php:60:    public function save(): void
app/Livewire/Admin/Discounts/Form.php:85:    public function save(): void
app/Livewire/Admin/Discounts/Form.php:140:    public function generateCode(): void
app/Livewire/Admin/Discounts/Form.php:152:    public function addProduct(int $productId): void
app/Livewire/Admin/Discounts/Form.php:163:    public function removeProduct(int $productId): void
app/Livewire/Admin/Discounts/Form.php:171:    public function addCollection(int $collectionId): void
app/Livewire/Admin/Discounts/Form.php:182:    public function removeCollection(int $collectionId): void
app/Livewire/Admin/Discounts/Index.php:45:    public function updatedSearch(): void
app/Livewire/Admin/Discounts/Index.php:50:    public function updatedStatusFilter(): void
app/Livewire/Admin/Discounts/Index.php:55:    public function updatedTypeFilter(): void
app/Livewire/Admin/Navigation/Index.php:70:    public function addItem(): void
app/Livewire/Admin/Navigation/Index.php:94:    public function saveItem(): void
app/Livewire/Admin/Navigation/Index.php:155:    public function removeItem(int $index): void
app/Livewire/Admin/Navigation/Index.php:167:    public function moveItemUp(int $index): void
app/Livewire/Admin/Navigation/Index.php:176:    public function moveItemDown(int $index): void
app/Livewire/Admin/Navigation/Index.php:185:    public function reorderItem(string $key, int $position, string $parentKey = 'root'): void
app/Livewire/Admin/Navigation/Index.php:214:    public function saveMenu(NavigationService $navigation): void
app/Livewire/Admin/Products/Form.php:100:    public function updatedTitle(): void
app/Livewire/Admin/Products/Form.php:107:    public function addOption(): void
app/Livewire/Admin/Products/Form.php:115:    public function removeOption(int $index): void
app/Livewire/Admin/Products/Form.php:121:    public function generateVariants(): void
app/Livewire/Admin/Products/Form.php:164:    public function save(): void
app/Livewire/Admin/Products/Form.php:239:    public function uploadMedia(): void
app/Livewire/Admin/Products/Form.php:257:    public function updateMediaAlt(int $mediaId): void
app/Livewire/Admin/Products/Form.php:277:    public function moveMedia(int $mediaId, string $direction): void
app/Livewire/Admin/Products/Form.php:304:    public function reorderMedia(array $order): void
app/Livewire/Admin/Products/Form.php:329:    public function removeMedia(int $mediaId): void
app/Livewire/Admin/Products/Form.php:346:    public function deleteProduct(): void
app/Livewire/Admin/Customers/Show.php:75:    public function saveAddress(): void
app/Livewire/Admin/Customers/Show.php:105:    public function deleteAddress(int $addressId): void
app/Livewire/Admin/Customers/Show.php:118:    public function setDefaultAddress(int $addressId): void
app/Livewire/Admin/Customers/Index.php:34:    public function updatedSearch(): void
app/Livewire/Admin/Products/Index.php:35:    public function updatedSearch(): void
app/Livewire/Admin/Products/Index.php:40:    public function updatedStatusFilter(): void
app/Livewire/Admin/Products/Index.php:45:    public function updatedTypeFilter(): void
app/Livewire/Admin/Products/Index.php:66:    public function toggleSelectAll(): void
app/Livewire/Admin/Settings/Notifications.php:46:    public function save(): void
app/Livewire/Admin/Settings/Index.php:61:    public function save(): void
app/Livewire/Admin/Settings/Index.php:102:    public function addDomain(): void
app/Livewire/Admin/Settings/Index.php:136:    public function setPrimary(int $domainId): void
app/Livewire/Admin/Settings/Index.php:153:    public function removeDomain(int $domainId): void
app/Livewire/Admin/Settings/Taxes.php:43:    public function addManualRate(): void
app/Livewire/Admin/Settings/Taxes.php:52:    public function removeManualRate(int $index): void
app/Livewire/Admin/Settings/Taxes.php:59:    public function save(): void
app/Livewire/Admin/Settings/Shipping.php:79:    public function saveZone(): void
app/Livewire/Admin/Settings/Shipping.php:118:    public function deleteZone(int $zoneId): void
app/Livewire/Admin/Settings/Shipping.php:129:    public function addRate(int $zoneId): void
app/Livewire/Admin/Settings/Shipping.php:153:    public function saveRate(): void
app/Livewire/Admin/Settings/Shipping.php:195:    public function deleteRate(int $rateId): void
app/Livewire/Admin/Settings/Shipping.php:205:    public function toggleRateActive(int $rateId): void
app/Livewire/Admin/Settings/Shipping.php:213:    public function testShippingAddress(ShippingCalculator $shipping): void
app/Livewire/Admin/Dashboard.php:84:    public function updatedDateRange(): void
app/Livewire/Admin/Dashboard.php:89:    public function updatedCustomStartDate(): void
app/Livewire/Admin/Dashboard.php:96:    public function updatedCustomEndDate(): void
app/Livewire/Admin/Settings/Checkout.php:48:    public function save(): void


Output:
Chunk ID: 17e3fc
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2287
Output:
app/Livewire/Admin/Developers/Index.php:55:    public function generateToken(WebhookService $webhooks): void
app/Livewire/Admin/Developers/Index.php:73:    public function revokeToken(int $tokenId): void
app/Livewire/Admin/Developers/Index.php:99:    public function saveWebhook(WebhookService $webhooks): void
app/Livewire/Admin/Developers/Index.php:130:    public function deleteWebhook(int $webhookId): void
app/Livewire/Admin/Analytics/Index.php:92:    public function updatedDateRange(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:97:    public function updatedCustomStartDate(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:104:    public function updatedCustomEndDate(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:111:    public function updatedChannelFilter(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:116:    public function updatedDeviceFilter(AnalyticsService $analytics): void
app/Livewire/Admin/Analytics/Index.php:160:    public function exportCsv(AnalyticsService $analytics): void
app/Livewire/Admin/Themes/Editor.php:70:    public function saveFile(): void
app/Livewire/Admin/Themes/Editor.php:92:    public function save(ThemeSettingsService $settings): void
app/Livewire/Admin/Themes/Editor.php:110:    public function publish(ThemeSettingsService $settings): void
app/Livewire/Admin/Themes/Index.php:36:    public function publishTheme(int $themeId, ThemeSettingsService $settings): void
app/Livewire/Admin/Themes/Index.php:62:    public function duplicateTheme(int $themeId): void
app/Livewire/Admin/Themes/Index.php:107:    public function deleteTheme(int $themeId): void
app/Livewire/Admin/Pages/Form.php:53:    public function updatedTitle(string $title): void
app/Livewire/Admin/Pages/Form.php:62:    public function save(NavigationService $navigation): void
app/Livewire/Admin/Pages/Form.php:111:    public function deletePage(NavigationService $navigation): void
app/Livewire/Admin/Pages/Index.php:36:    public function updatedSearch(): void
app/Livewire/Admin/Collections/Form.php:47:    public function updatedTitle(): void
app/Livewire/Admin/Collections/Form.php:54:    public function addProduct(int $productId): void
app/Livewire/Admin/Collections/Form.php:63:    public function removeProduct(int $productId): void
app/Livewire/Admin/Collections/Form.php:71:    public function save(): void
app/Livewire/Admin/Collections/Index.php:19:    public function updatedSearch(): void
app/Livewire/Admin/Collections/Index.php:24:    public function updatedStatusFilter(): void
app/Livewire/Admin/Collections/Index.php:29:    public function deleteCollection(int $id): void
app/Livewire/Admin/Orders/Show.php:65:    public function confirmBankTransferPayment(OrderService $orders): void
app/Livewire/Admin/Orders/Show.php:79:    public function processRefund(RefundService $refunds): void
app/Livewire/Admin/Orders/Show.php:108:    public function createFulfillment(FulfillmentService $fulfillments): void
app/Livewire/Admin/Orders/Show.php:149:    public function markFulfillmentShipped(int $fulfillmentId, FulfillmentService $fulfillments): void
app/Livewire/Admin/Orders/Show.php:162:    public function markFulfillmentDelivered(int $fulfillmentId, FulfillmentService $fulfillments): void
app/Livewire/Admin/Orders/Index.php:46:    public function updatedSearch(): void
app/Livewire/Admin/Orders/Index.php:51:    public function updatedStatusFilter(): void
app/Livewire/Admin/Orders/Index.php:56:    public function updatedFinancialStatusFilter(): void
app/Livewire/Admin/Orders/Index.php:61:    public function updatedFulfillmentStatusFilter(): void
app/Livewire/Admin/Orders/Index.php:66:    public function updatedDateFrom(): void
app/Livewire/Admin/Orders/Index.php:71:    public function updatedDateTo(): void
app/Livewire/Admin/Inventory/Index.php:19:    public function updatedSearch(): void
app/Livewire/Admin/Inventory/Index.php:24:    public function updatedStockFilter(): void
app/Livewire/Admin/Search/Settings.php:49:    public function addSynonymGroup(): void
app/Livewire/Admin/Search/Settings.php:54:    public function removeSynonymGroup(int $index): void
app/Livewire/Admin/Search/Settings.php:60:    public function save(): void
app/Livewire/Admin/Discounts/Form.php:85:    public function save(): void
app/Livewire/Admin/Discounts/Form.php:140:    public function generateCode(): void
app/Livewire/Admin/Discounts/Form.php:152:    public function addProduct(int $productId): void
app/Livewire/Admin/Discounts/Form.php:163:    public function removeProduct(int $productId): void
app/Livewire/Admin/Discounts/Form.php:171:    public function addCollection(int $collectionId): void
app/Livewire/Admin/Discounts/Form.php:182:    public function removeCollection(int $collectionId): void
app/Livewire/Admin/Discounts/Index.php:45:    public function updatedSearch(): void
app/Livewire/Admin/Discounts/Index.php:50:    public function updatedStatusFilter(): void
app/Livewire/Admin/Discounts/Index.php:55:    public function updatedTypeFilter(): void
app/Livewire/Admin/Navigation/Index.php:70:    public function addItem(): void
app/Livewire/Admin/Navigation/Index.php:94:    public function saveItem(): void
app/Livewire/Admin/Navigation/Index.php:155:    public function removeItem(int $index): void
app/Livewire/Admin/Navigation/Index.php:167:    public function moveItemUp(int $index): void
app/Livewire/Admin/Navigation/Index.php:176:    public function moveItemDown(int $index): void
app/Livewire/Admin/Navigation/Index.php:185:    public function reorderItem(string $key, int $position, string $parentKey = 'root'): void
app/Livewire/Admin/Navigation/Index.php:214:    public function saveMenu(NavigationService $navigation): void
app/Livewire/Admin/Products/Form.php:100:    public function updatedTitle(): void
app/Livewire/Admin/Products/Form.php:107:    public function addOption(): void
app/Livewire/Admin/Products/Form.php:115:    public function removeOption(int $index): void
app/Livewire/Admin/Products/Form.php:121:    public function generateVariants(): void
app/Livewire/Admin/Products/Form.php:164:    public function save(): void
app/Livewire/Admin/Products/Form.php:239:    public function uploadMedia(): void
app/Livewire/Admin/Products/Form.php:257:    public function updateMediaAlt(int $mediaId): void
app/Livewire/Admin/Products/Form.php:277:    public function moveMedia(int $mediaId, string $direction): void
app/Livewire/Admin/Products/Form.php:304:    public function reorderMedia(array $order): void
app/Livewire/Admin/Products/Form.php:329:    public function removeMedia(int $mediaId): void
app/Livewire/Admin/Products/Form.php:346:    public function deleteProduct(): void
app/Livewire/Admin/Customers/Show.php:75:    public function saveAddress(): void
app/Livewire/Admin/Customers/Show.php:105:    public function deleteAddress(int $addressId): void
app/Livewire/Admin/Customers/Show.php:118:    public function setDefaultAddress(int $addressId): void
app/Livewire/Admin/Customers/Index.php:34:    public function updatedSearch(): void
app/Livewire/Admin/Products/Index.php:35:    public function updatedSearch(): void
app/Livewire/Admin/Products/Index.php:40:    public function updatedStatusFilter(): void
app/Livewire/Admin/Products/Index.php:45:    public function updatedTypeFilter(): void
app/Livewire/Admin/Products/Index.php:66:    public function toggleSelectAll(): void
app/Livewire/Admin/Settings/Notifications.php:46:    public function save(): void
app/Livewire/Admin/Settings/Index.php:61:    public function save(): void
app/Livewire/Admin/Settings/Index.php:102:    public function addDomain(): void
app/Livewire/Admin/Settings/Index.php:136:    public function setPrimary(int $domainId): void
app/Livewire/Admin/Settings/Index.php:153:    public function removeDomain(int $domainId): void
app/Livewire/Admin/Settings/Taxes.php:43:    public function addManualRate(): void
app/Livewire/Admin/Settings/Taxes.php:52:    public function removeManualRate(int $index): void
app/Livewire/Admin/Settings/Taxes.php:59:    public function save(): void
app/Livewire/Admin/Settings/Shipping.php:79:    public function saveZone(): void
app/Livewire/Admin/Settings/Shipping.php:118:    public function deleteZone(int $zoneId): void
app/Livewire/Admin/Settings/Shipping.php:129:    public function addRate(int $zoneId): void
app/Livewire/Admin/Settings/Shipping.php:153:    public function saveRate(): void
app/Livewire/Admin/Settings/Shipping.php:195:    public function deleteRate(int $rateId): void
app/Livewire/Admin/Settings/Shipping.php:205:    public function toggleRateActive(int $rateId): void
app/Livewire/Admin/Settings/Shipping.php:213:    public function testShippingAddress(ShippingCalculator $shipping): void
app/Livewire/Admin/Dashboard.php:84:    public function updatedDateRange(): void
app/Livewire/Admin/Dashboard.php:89:    public function updatedCustomStartDate(): void
app/Livewire/Admin/Dashboard.php:96:    public function updatedCustomEndDate(): void
app/Livewire/Admin/Settings/Checkout.php:48:    public function save(): void


rg -n "support|staff|owner|admin|Role|permission|matrix|Product|Order|Refund|Fulfillment" specs/06-AUTH-AND-SECURITY.md specs/03-ADMIN-UI.md -S