Remove old Vuexy wrapper components (AppTextField, AppSelect, AppTextarea, FlashMessages, NotificationBell)

All pages now use native Vuetify components directly. Flash messages are handled
by the ToastStack component via Pinia store. Notifications use NotificationPanel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Dev
2026-03-14 17:10:23 -04:00
parent dd1a5d7ffc
commit 40c1ecc6fe
90 changed files with 20113 additions and 457 deletions

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class EmailTemplate extends Model
{
use HasFactory;
protected $fillable = [
'slug',
'name',
'subject',
'body',
'available_variables',
'is_active',
];
protected function casts(): array
{
return [
'available_variables' => 'array',
'is_active' => 'boolean',
];
}
public static function getTemplate(string $slug): ?self
{
return static::query()
->where('slug', $slug)
->where('is_active', true)
->first();
}
/**
* Render a template by replacing {{variable_name}} placeholders with actual values.
*
* @param array<string, string> $variables
* @return array{subject: string, body: string}|null
*/
public static function render(string $slug, array $variables): ?array
{
$template = static::getTemplate($slug);
if (! $template) {
return null;
}
$subject = $template->subject;
$body = $template->body;
foreach ($variables as $key => $value) {
$placeholder = '{{'.$key.'}}';
$subject = str_replace($placeholder, (string) $value, $subject);
$body = str_replace($placeholder, (string) $value, $body);
}
return [
'subject' => $subject,
'body' => $body,
];
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class TaxRate extends Model
{
use HasFactory;
protected $fillable = [
'name',
'country_code',
'region_code',
'rate',
'type',
'priority',
'is_active',
];
protected function casts(): array
{
return [
'rate' => 'decimal:2',
'is_active' => 'boolean',
'priority' => 'integer',
];
}
/**
* Scope to only active tax rates.
*/
public function scopeActive(Builder $query): Builder
{
return $query->where('is_active', true);
}
/**
* Scope to filter by country code.
*/
public function scopeForCountry(Builder $query, string $code): Builder
{
return $query->where('country_code', strtoupper($code));
}
/**
* Scope to filter by country and region.
*/
public function scopeForRegion(Builder $query, string $country, ?string $region): Builder
{
$query->where('country_code', strtoupper($country));
if ($region !== null) {
$query->where(function (Builder $q) use ($region): void {
$q->where('region_code', $region)
->orWhereNull('region_code');
});
}
return $query;
}
/**
* Get applicable tax rates for a given country and optional region, ordered by priority.
*
* @return Collection<int, TaxRate>
*/
public static function getApplicableRates(string $countryCode, ?string $regionCode = null): Collection
{
$query = static::query()
->active()
->where('country_code', strtoupper($countryCode));
if ($regionCode !== null) {
$query->where(function (Builder $q) use ($regionCode): void {
$q->where('region_code', $regionCode)
->orWhereNull('region_code');
});
} else {
$query->whereNull('region_code');
}
return $query->orderBy('priority')->get();
}
}