them log, chinh upload
This commit is contained in:
233
docs/ENUM_GUIDE.md
Normal file
233
docs/ENUM_GUIDE.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# Hướng dẫn thêm Enum mới
|
||||
|
||||
## Tổng quan
|
||||
|
||||
Hệ thống sử dụng PHP Enums với i18n support. Khi cần thêm giá trị enum mới (Service Type, Contract Type, v.v.), chỉ cần cập nhật 3 chỗ.
|
||||
|
||||
## Cấu trúc Enum hiện có
|
||||
|
||||
```
|
||||
app/Enums/
|
||||
├── ContractStatus.php # Trạng thái hợp đồng
|
||||
├── ContractType.php # Loại hợp đồng
|
||||
├── HandoverStatus.php # Trạng thái bàn giao
|
||||
└── ServiceType.php # Loại dịch vụ
|
||||
```
|
||||
|
||||
## Ví dụ: Thêm Service Type mới
|
||||
|
||||
Giả sử cần thêm loại dịch vụ "Bảo trì định kỳ" (maintenance).
|
||||
|
||||
### Bước 1: Thêm case vào Enum
|
||||
|
||||
**File:** `app/Enums/ServiceType.php`
|
||||
|
||||
```php
|
||||
enum ServiceType: string
|
||||
{
|
||||
case MANAGEMENT_FEE = 'management_fee';
|
||||
case GRATITUDE = 'gratitude';
|
||||
case SELF_BUSINESS = 'self_business';
|
||||
case MAINTENANCE = 'maintenance'; // ← THÊM MỚI
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::MANAGEMENT_FEE => __('enums.service.management_fee'),
|
||||
self::GRATITUDE => __('enums.service.gratitude'),
|
||||
self::SELF_BUSINESS => __('enums.service.self_business'),
|
||||
self::MAINTENANCE => __('enums.service.maintenance'), // ← THÊM MỚI
|
||||
};
|
||||
}
|
||||
|
||||
public function color(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::MANAGEMENT_FEE => 'info',
|
||||
self::GRATITUDE => 'success',
|
||||
self::SELF_BUSINESS => 'warning',
|
||||
self::MAINTENANCE => 'primary', // ← THÊM MỚI
|
||||
};
|
||||
}
|
||||
|
||||
// options() tự động lấy tất cả cases - KHÔNG cần sửa
|
||||
}
|
||||
```
|
||||
|
||||
### Bước 2: Thêm translation tiếng Việt
|
||||
|
||||
**File:** `lang/vi/enums.php`
|
||||
|
||||
```php
|
||||
return [
|
||||
// ... existing keys ...
|
||||
|
||||
'service' => [
|
||||
'management_fee' => 'Phí quản lý',
|
||||
'gratitude' => 'Tri ân',
|
||||
'self_business' => 'Tự doanh',
|
||||
'maintenance' => 'Bảo trì định kỳ', // ← THÊM MỚI
|
||||
],
|
||||
|
||||
// ... existing keys ...
|
||||
];
|
||||
```
|
||||
|
||||
### Bước 3: Thêm translation tiếng Anh
|
||||
|
||||
**File:** `lang/en/enums.php`
|
||||
|
||||
```php
|
||||
return [
|
||||
// ... existing keys ...
|
||||
|
||||
'service' => [
|
||||
'management_fee' => 'Management fee',
|
||||
'gratitude' => 'Gratitude',
|
||||
'self_business' => 'Self business',
|
||||
'maintenance' => 'Periodic maintenance', // ← THÊM MỚI
|
||||
],
|
||||
|
||||
// ... existing keys ...
|
||||
];
|
||||
```
|
||||
|
||||
### Bước 4: Reload browser
|
||||
|
||||
Không cần chạy lệnh nào, chỉ cần reload trang.
|
||||
|
||||
## Ví dụ: Thêm Contract Type mới
|
||||
|
||||
### Bước 1: Thêm case
|
||||
|
||||
**File:** `app/Enums/ContractType.php`
|
||||
|
||||
```php
|
||||
enum ContractType: string
|
||||
{
|
||||
case SALE = 'sale';
|
||||
case LEASE = 'lease';
|
||||
case BCC = 'bcc';
|
||||
case CONSORTIUM = 'consortium'; // ← THÊM MỚI
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::SALE => __('enums.contract_type.sale'),
|
||||
self::LEASE => __('enums.contract_type.lease'),
|
||||
self::BCC => __('enums.contract_type.bcc'),
|
||||
self::CONSORTIUM => __('enums.contract_type.consortium'), // ← THÊM MỚI
|
||||
};
|
||||
}
|
||||
|
||||
public function color(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::SALE => 'success',
|
||||
self::LEASE => 'info',
|
||||
self::BCC => 'warning',
|
||||
self::CONSORTIUM => 'primary', // ← THÊM MỚI
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Bước 2-3: Thêm translations
|
||||
|
||||
```php
|
||||
// lang/vi/enums.php
|
||||
'contract_type' => [
|
||||
'sale' => 'HĐ Mua bán',
|
||||
'lease' => 'HĐ Thuê',
|
||||
'bcc' => 'HĐ Hợp tác kinh doanh (BCC)',
|
||||
'consortium' => 'HĐ Liên doanh', // ← THÊM MỚI
|
||||
],
|
||||
|
||||
// lang/en/enums.php
|
||||
'contract_type' => [
|
||||
'sale' => 'Sale contract',
|
||||
'lease' => 'Lease contract',
|
||||
'bcc' => 'Business cooperation contract (BCC)',
|
||||
'consortium' => 'Consortium contract', // ← THÊM MỚI
|
||||
],
|
||||
```
|
||||
|
||||
## Template Enum (Copy-paste)
|
||||
|
||||
Khi tạo enum mới, copy template sau:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
/**
|
||||
* [Tên] Enum
|
||||
*
|
||||
* Để thêm giá trị mới:
|
||||
* 1. Thêm case mới vào enum
|
||||
* 2. Thêm label trong lang/vi/enums.php và lang/en/enums.php
|
||||
* 3. Reload browser
|
||||
*/
|
||||
enum [EnumName]: string
|
||||
{
|
||||
case [CASE_1] = '[value_1]';
|
||||
case [CASE_2] = '[value_2]';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::[CASE_1] => __('enums.[enum_name].[value_1]'),
|
||||
self::[CASE_2] => __('enums.[enum_name].[value_2]'),
|
||||
};
|
||||
}
|
||||
|
||||
public function color(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::[CASE_1] => 'success',
|
||||
self::[CASE_2] => 'info',
|
||||
};
|
||||
}
|
||||
|
||||
public static function options(): array
|
||||
{
|
||||
return collect(self::cases())->mapWithKeys(fn ($case) => [
|
||||
$case->value => $case->label(),
|
||||
])->toArray();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cách sử dụng Enum trong code
|
||||
|
||||
### Trong Filament Form
|
||||
```php
|
||||
Select::make('service_type')
|
||||
->label(__('enums.service_type'))
|
||||
->options(ServiceType::options())
|
||||
->required(),
|
||||
```
|
||||
|
||||
### Trong Filament Table
|
||||
```php
|
||||
TextColumn::make('service_type')
|
||||
->badge()
|
||||
->color(fn (string $state): string => ServiceType::from($state)->color())
|
||||
->formatStateUsing(fn (string $state): string => ServiceType::from($state)->label()),
|
||||
```
|
||||
|
||||
### Trong PHP code
|
||||
```php
|
||||
$type = ServiceType::MANAGEMENT_FEE;
|
||||
echo $type->label(); // "Phí quản lý"
|
||||
echo $type->value; // "management_fee"
|
||||
echo $type->color(); // "info"
|
||||
```
|
||||
|
||||
## Lưu ý
|
||||
|
||||
1. **`options()` method** - PHẢI là `static` (không phải instance method)
|
||||
2. **Translation keys** - Phải có trong CẢ `lang/vi/enums.php` VÀ `lang/en/enums.php`
|
||||
3. **Color values** - Sử dụng Filament colors: `primary`, `success`, `warning`, `danger`, `info`, `gray`
|
||||
4. **Case values** - Nên dùng snake_case (ví dụ: `management_fee`, không phải `ManagementFee`)
|
||||
162
docs/I18N_GUIDE.md
Normal file
162
docs/I18N_GUIDE.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# Hướng dẫn Đa ngôn ngữ (i18n)
|
||||
|
||||
## Tổng quan
|
||||
|
||||
Hệ thống hỗ trợ 2 ngôn ngữ:
|
||||
- **Tiếng Việt (vi)** - Ngôn ngữ chính
|
||||
- **Tiếng Anh (en)** - Ngôn ngữ dự phòng
|
||||
|
||||
Cấu hình trong `.env`:
|
||||
```
|
||||
APP_LOCALE=vi
|
||||
APP_FALLBACK_LOCALE=en
|
||||
```
|
||||
|
||||
## Cấu trúc thư mục
|
||||
|
||||
```
|
||||
lang/
|
||||
├── en/
|
||||
│ ├── app.php # UI chung: navigation, labels, status, widgets
|
||||
│ ├── feedback.php # Domain feedback: services, notifications, interactions
|
||||
│ └── enums.php # Labels cho enums
|
||||
└── vi/
|
||||
├── app.php
|
||||
├── feedback.php
|
||||
└── enums.php
|
||||
```
|
||||
|
||||
## Quy tắc đặt tên Translation Keys
|
||||
|
||||
### 1. Resource Labels
|
||||
```php
|
||||
// Pattern: app.resource_{name}
|
||||
'resource_feedbacks' => 'Phản ánh',
|
||||
'resource_products' => 'Sản phẩm',
|
||||
'resource_customers' => 'Khách hàng',
|
||||
```
|
||||
|
||||
### 2. Status Labels
|
||||
```php
|
||||
// Pattern: app.status_{name}
|
||||
'status_pending' => 'Chờ xử lý',
|
||||
'status_processing' => 'Đang xử lý',
|
||||
'status_resolved' => 'Đã xử lý',
|
||||
'status_closed' => 'Đã đóng',
|
||||
```
|
||||
|
||||
### 3. Form Labels
|
||||
```php
|
||||
// Pattern: app.{field_name}
|
||||
'name' => 'Tên',
|
||||
'email' => 'Email',
|
||||
'phone' => 'Điện thoại',
|
||||
```
|
||||
|
||||
### 4. Enum Labels
|
||||
```php
|
||||
// Pattern: enums.{enum_name}.{case_value}
|
||||
'service.management_fee' => 'Phí quản lý',
|
||||
'contract_status.active' => 'Đang hiệu lực',
|
||||
```
|
||||
|
||||
### 5. Feedback Domain
|
||||
```php
|
||||
// Pattern: feedback.{action}_{context}
|
||||
'already_closed' => 'Phiếu này đã được đóng.',
|
||||
'close_permission_denied' => 'Chỉ lãnh đạo cấp phòng mới có quyền đóng phiếu.',
|
||||
```
|
||||
|
||||
## Cách sử dụng trong code
|
||||
|
||||
### Resource Labels
|
||||
```php
|
||||
// Phải override getPluralLabel() - KHÔNG tự dịch
|
||||
public static function getPluralLabel(): ?string
|
||||
{
|
||||
return __('app.resource_feedbacks');
|
||||
}
|
||||
|
||||
// Phải override getNavigationGroup() - KHÔNG tự dịch
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return __('app.nav_customer_care');
|
||||
}
|
||||
```
|
||||
|
||||
### Form Fields
|
||||
```php
|
||||
// PHẢI thêm ->label() explicit
|
||||
TextInput::make('name')->label(__('app.name'))
|
||||
Select::make('status')->label(__('app.status'))
|
||||
```
|
||||
|
||||
### RelationManager Title
|
||||
```php
|
||||
// Phải override getTitle() - KHÔNG dùng $title property
|
||||
public static function getTitle(Model $ownerRecord, string $pageClass): string
|
||||
{
|
||||
return __('app.services');
|
||||
}
|
||||
```
|
||||
|
||||
### Widget Heading/Description
|
||||
```php
|
||||
// Phải override getHeading()/getDescription() - KHÔNG dùng property
|
||||
public function getHeading(): string | Htmlable | null
|
||||
{
|
||||
return __('app.widget_avg_time_by_handler');
|
||||
}
|
||||
```
|
||||
|
||||
### Enum Labels
|
||||
```php
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::ACTIVE => __('enums.contract_status.active'),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Cách thêm Translation Key mới
|
||||
|
||||
### Bước 1: Thêm vào `lang/vi/app.php`
|
||||
```php
|
||||
return [
|
||||
// ... existing keys ...
|
||||
'new_key' => 'Giá trị tiếng Việt',
|
||||
];
|
||||
```
|
||||
|
||||
### Bước 2: Thêm vào `lang/en/app.php`
|
||||
```php
|
||||
return [
|
||||
// ... existing keys ...
|
||||
'new_key' => 'English value',
|
||||
];
|
||||
```
|
||||
|
||||
### Bước 3: Sử dụng trong code
|
||||
```php
|
||||
echo __('app.new_key');
|
||||
```
|
||||
|
||||
## Lưu ý quan trọng
|
||||
|
||||
1. **`$pluralLabel` property** - KHÔNG tự dịch, phải override `getPluralLabel()`
|
||||
2. **`getNavigationGroup()`** - KHÔNG tự dịch, phải gọi `__()`
|
||||
3. **`$heading`/`$description`** trong ChartWidget - KHÔNG tự dịch, phải override methods
|
||||
4. **Form fields** - PHẢI thêm `->label()` explicit
|
||||
5. **`$title`** trong RelationManager - KHÔNG tự dịch, phải override `getTitle()`
|
||||
6. **Filament vendor** - Có sẵn 57 file dịch tiếng Việt, tự kích hoạt khi `APP_LOCALE=vi`
|
||||
|
||||
## Kiểm tra Translation
|
||||
|
||||
```bash
|
||||
# Kiểm tra key có tồn tại không
|
||||
php artisan tinker --execute="echo __('app.new_key');"
|
||||
|
||||
# Kiểm tra locale hiện tại
|
||||
php artisan tinker --execute="echo app()->getLocale();"
|
||||
```
|
||||
Reference in New Issue
Block a user