Files
baodev0andClaude Opus 5.5 46aa138eec feat(admin): IP pool allocation, billing exports, service attachments
- IP pool: sheet import (xlsx/csv) matched by address, bulk allocate/revoke
  with notification email (ip_allocated/ip_revoked), ip_pool_logs history.
- Orders/invoices: Excel export on branded template (BrandedReportWriter),
  shared billing filters, order item service info in list responses.
- Web design quote Excel export; activity log export.
- Service attachments with copy-to-invoice; deployment invoice attachments.
- Customer email attachments stored on disk and attached by path.
- Admin password change endpoint; user.fullname backfill migration;
  orders.user_id backfill from logs.
- Disable deploy workflow until CI/CD is set up.
- Ignore stored customer email and service attachment uploads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 19:58:36 +07:00

88 lines
2.4 KiB
PHP

<?php
namespace App\Enums\WebDesign;
enum WebDesignPlatform: string
{
case WORDPRESS_ELEMENTOR = 'wordpress_elementor';
case WORDPRESS_HEADLESS = 'wordpress_headless';
case LARAVEL = 'laravel';
case NESTJS = 'nestjs';
case SPRING_BOOT = 'spring_boot';
public function label(): string
{
return match ($this) {
self::WORDPRESS_ELEMENTOR => 'WordPress + Elementor',
self::WORDPRESS_HEADLESS => 'WordPress Headless',
self::LARAVEL => 'Laravel',
self::NESTJS => 'NestJS',
self::SPRING_BOOT => 'Spring Boot',
};
}
/**
* Các framework frontend cho phép theo từng nền tảng.
* Mảng rỗng = không có lựa chọn frontend riêng.
*/
public function frontendOptions(): array
{
return match ($this) {
self::WORDPRESS_ELEMENTOR => [],
self::WORDPRESS_HEADLESS => ['nextjs', 'astro', 'vue', 'react', 'nuxt', 'angular'],
self::LARAVEL, self::NESTJS => ['react', 'nextjs', 'nuxt', 'vue'],
self::SPRING_BOOT => ['react', 'nextjs', 'nuxt', 'vue', 'angular'],
};
}
public function defaultFrontend(): ?string
{
return match ($this) {
self::WORDPRESS_ELEMENTOR => null,
self::WORDPRESS_HEADLESS => 'nextjs',
self::LARAVEL, self::NESTJS => 'react',
self::SPRING_BOOT => 'react',
};
}
/**
* Từ gói Laravel trở lên có thể custom admin.
*/
public function supportsCustomAdmin(): bool
{
return match ($this) {
self::WORDPRESS_ELEMENTOR, self::WORDPRESS_HEADLESS => false,
self::LARAVEL, self::NESTJS, self::SPRING_BOOT => true,
};
}
/**
* Phụ phí bản quyền hằng năm theo USD (Elementor Pro).
*/
public function annualLicenseFeeUsd(): int
{
return $this === self::WORDPRESS_ELEMENTOR ? 60 : 0;
}
public static function frontendLabel(?string $framework): ?string
{
if (!$framework) {
return null;
}
return [
'nextjs' => 'Next.js',
'react' => 'React',
'vue' => 'Vue',
'nuxt' => 'Nuxt',
'astro' => 'Astro',
'angular' => 'Angular',
][$framework] ?? $framework;
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}