import {Meta} from '@storybook/addon-docs/blocks'

<Meta title='@dxtmisha/vi/functional-basic/Classes/Formatters - Tiện ích Định dạng Dữ liệu'/>

# Lớp Formatters

Một lớp chuyên dụng để định dạng danh sách dữ liệu dựa trên cấu hình được cung cấp. Nó đóng vai trò là cầu nối giữa dữ liệu thô và các chuỗi đã được bản địa hóa, dễ đọc, sử dụng singleton `GeoIntl` để định dạng nhất quán trên các ngôn ngữ khác nhau.

## Các tính năng chính

- **Cấu hình khai báo** — Xác định các quy tắc định dạng một lần và áp dụng chúng cho toàn bộ tập dữ liệu.
- **Hỗ trợ nhiều loại** — Hỗ trợ tích hợp cho tiền tệ, ngày tháng, tên, số, số nhiều và đơn vị đo lường.
- **Chuyển đổi tùy chỉnh** — Khả năng cung cấp các hàm riêng của bạn cho các nhu cầu định dạng duy nhất.
- **Typing** — Hỗ trợ TypeScript đầy đủ cho các mục, tham số và danh sách đã định dạng kết quả.
- **Tự động tạo khóa** — Tự động thêm các giá trị đã định dạng vào các mục với hậu tố `Format` (ví dụ: `price` trở thành `priceFormat`).

## Khởi tạo

Để khởi tạo đối tượng, hãy gọi constructor `Formatters(options, list)`.

### Các ràng buộc Generic (Generic Constraints)

- `Options extends FormattersOptionsList` — kiểu tham số định dạng.
- `List extends FormattersListProp` — kiểu dữ liệu đầu vào (mảng hoặc một mục).
- `Item extends FormattersItemProp<List>` — kiểu của một mục duy nhất trong danh sách.

**Tham số:**
- `options: Options` — tham số định dạng cho mỗi cột/thuộc tính.
- `list?: List` — danh sách dữ liệu ban đầu để định dạng.

```typescript
import { Formatters } from '@dxtmisha/functional-basic'

// 1. Xác định các tham số định dạng
const options = {
  price: { 
    type: 'currency', 
    options: { currencyPropName: 'currency' } 
  },
  created: { 
    type: 'date', 
    options: { type: 'datetime' } 
  },
  user: {
    type: 'name',
    options: { lastPropName: 'surname', firstPropName: 'name' }
  }
}

// 2. Dữ liệu thô
const data = [
  {
    price: 100,
    currency: 'VND',
    created: '2024-03-07T12:00:00',
    name: 'An',
    surname: 'Nguyễn'
  }
]

// 3. Khởi tạo
const formatter = new Formatters(options, data)
```

## Các phương thức

### Xử lý
- `to(): FormattersReturn<List, Options>` — Xử lý dữ liệu và trả về kết quả. Nếu đầu vào là một mảng, trả về một mảng mới gồm các mục đã được làm giàu. Nếu là một mục duy nhất — một đối tượng duy nhất. Thêm các thuộc tính với hậu tố `Format`.

### Cấu hình
- `setList(list?: List): this` — Cập nhật dữ liệu để định dạng. Hỗ trợ nối chuỗi.

### Thông tin
- `is(): boolean` — Kiểm tra xem dữ liệu đã được thiết lập để xử lý hay chưa.
- `isArray(): boolean` — Liệu dữ liệu hiện tại có phải là một mảng hay không.
- `length(): number` — Trả về số lượng bản ghi trong danh sách.
- `getList(): FormattersList<Item>` — Trả về danh sách hiện tại dưới dạng mảng.
- `getOptions(): Options` — Trả về cấu hình định dạng hiện tại.

## Các loại định dạng và Tham số

Cấu hình `FormattersOptionsList` là một từ điển trong đó các khóa tương ứng với tên thuộc tính trong dữ liệu (hỗ trợ ký hiệu chấm `path.to.prop`) và các giá trị là đối tượng `FormattersOptionsItem`.

### Cấu trúc chung của `FormattersOptionsItem`
- `type?: FormattersType` — Loại định dạng.
- `transformation?: (value, item, options) => string` — Hàm chuyển đổi tùy chỉnh.
- `options?: Options` — Đối tượng tham số, cấu trúc phụ thuộc vào `type` đã chọn.

### Mô tả chi tiết các loại

#### 1. Tiền tệ (`currency`)
Được sử dụng để hiển thị các giá trị tiền tệ đã được bản địa hóa.
- `currencyPropName?: string` — Tên thuộc tính trong mục chứa mã tiền tệ (mặc định là `currency`).
- `options?: string | Intl.NumberFormatOptions` — Bạn có thể truyền một mã cố định (ví dụ: `'VND'`) hoặc một đối tượng cài đặt `Intl.NumberFormat`.
- `numberOnly?: boolean` — Nếu `true`, chỉ trả về số đã định dạng mà không có biểu tượng tiền tệ.

#### 2. Ngày tháng (`date`)
Định dạng ngày và giờ thông qua `GeoIntl`.
- `type?: GeoDate` — Định dạng đặt trước:
    - `full` — Ngày và giờ đầy đủ.
    - `datetime` — Ngày và giờ (không có giây).
    - `date` — Chỉ ngày.
    - `time` — Chỉ giờ.
    - `year-month`, `year`, `month`, `day`, `day-month`, `hour-minute`.
- `options?: Intl.DateTimeFormatOptions` — Các tham số `Intl` tiêu chuẩn (ví dụ: `month: 'long'`).
- `hour24?: boolean` — Nếu `true`, buộc sử dụng chế độ 24 giờ.

#### 3. Tên (`name`)
Kết hợp các phần của tên đầy đủ theo đúng thứ tự.
- `lastPropName?: string` — Trường cho họ (mặc định là `lastName`).
- `firstPropName?: string` — Trường cho tên (mặc định là `firstName`).
- `surname?: string` — Trường cho tên đệm (mặc định là `surname`).
- `short?: boolean` — Nếu `true`, xuất họ và chữ cái đầu (ví dụ: "Nguyễn A.").

#### 4. Số (`number`)
Định dạng số tiêu chuẩn với phân tách chữ số.
- `options?: Intl.NumberFormatOptions` — Các tham số `Intl` (số chữ số thập phân, kiểu dáng, v.v.).

#### 5. Số nhiều (`plural`)
Chọn dạng từ đúng tùy thuộc vào số lượng.
- `words: string` — Các dạng từ thông qua dấu phân cách `|`.
    - Cho tiếng Việt: `"quả táo|những quả táo"`.
    - Cho tiếng Anh: `"apple|apples"`.
- `options?: Intl.PluralRulesOptions` — Cài đặt quy tắc số nhiều.
- `optionsNumber?: Intl.NumberFormatOptions` — Cài đặt định dạng cho chính con số được xuất trước từ.

#### 6. Đơn vị đo lường (`unit`)
Thêm các đơn vị đo lường (km, kg, l).
- `unit: string` — Mã đơn vị đo lường (ví dụ: `'kilometer'`, `'kilogram'`, `'liter'`).
- `options?: Intl.NumberFormatOptions` — Tham số định dạng cho phần số.

## Ví dụ

### Sử dụng Dot Notation
```typescript
const options = {
  'user.profile.age': { type: 'number' }
}
const data = { user: { profile: { age: 25 } } }
const formatter = new Formatters(options, data)

const result = formatter.to()
// result.userProfileAgeFormat === "25"
```

### Chuyển đổi tùy chỉnh
```typescript
const options = {
  status: {
    // Hàm tùy chỉnh có ưu tiên cao hơn type
    transformation: (value, item) => {
       return value === 'active' ? `✅ ${item.name} đang trực tuyến` : '❌ Ngoại tuyến'
    }
  }
}
```
