Quy tắc coding
Naming Conventions
| Đối tượng | Convention | Ví dụ |
|---|---|---|
| File Component | PascalCase | CategoryForm.tsx, NewsTab.tsx |
| File logic/helper | kebab-case | category.schema.ts, use-hook.ts |
| Thư mục (Folders) | kebab-case | category-list/, auth-callback/ |
| React Component | PascalCase | CategoryForm |
| Biến, hàm, props | camelCase | searchValue, handleSubmit |
| Event Handlers | handle / on | handleFilter, onDelete |
| Biến Boolean | is, has, can | isLoading, hasPermission |
| Type/Interface | PascalCase | NotificationItem |
| Hằng số/enum | UPPER_SNAKE_CASE | NOTIFY_TYPE |
| API string key | snake_case | vin_order_noti |
Chi tiết về Naming
- Event Handlers:
- Hàm xử lý logic trong component:
handle+Verb(ví dụ:handleClick,handleFilterChange). - Props callback (nhận từ công ngoài):
on+Verb(ví dụ:onClick,onSuccess).
- Hàm xử lý logic trong component:
- Biến Boolean: Sử dụng prefix
is,has,should,can(ví dụ:isLoading,hasPermission). - Thư mục: Luôn dùng
kebab-casecho tên thư mục.
Import Rules
// ✅ Import trực tiếp từ @vppos/core
import { Button, Input } from "@vppos/core/ui/react";
import { useDebounce } from "@vppos/core/hooks";
import { toastUtils } from "@vppos/core/utils";
// ❌ Không re-export qua file barrel trong app
// ❌ Không tạo lại component/hook đã có trong core
Do & Don't
API Calls
// ✅ Dùng RTK Query
const { data, isLoading } = useGetProductsQuery({ page: 1 });
// ❌ Không gọi API trực tiếp
const data = await fetch("/api/products");
const data = await axios.get("/api/products");
Toast
// ✅ Dùng toastUtils
import { toastUtils } from "@vppos/core/utils";
toastUtils.success("Thành công");
// ❌ Không import toast từ react-toastify
import { toast } from "react-toastify";
Error Handling
// ✅ Dùng helper
import { getApiErrorMessage, getApiError, setFormErrors } from "@vppos/core/utils";
try {
await createProduct(data).unwrap();
toastUtils.success("Tạo thành công");
} catch (err) {
const apiError = getApiError(err);
if (apiError?.fieldErrors) {
setFormErrors(setError, apiError.fieldErrors);
}
toastUtils.error(getApiErrorMessage(err));
}
Date/Time
// ✅ Dùng dayjs
import dayjs from "dayjs";
const now = dayjs();
const formatted = formatDate(date);
// ❌ Không dùng new Date()
const now = new Date();
JSX Handlers
// ✅ Tách handler
const handleDelete = async () => {
await deleteProduct(id).unwrap();
};
<Button onClick={() => handleDelete()}>Delete</Button>
// ❌ Không viết async inline
<Button onClick={async () => { await deleteProduct(id) }}>Delete</Button>
Tailwind
// ✅ Dùng theme colors
<div className="text-primary-500 bg-primary-50" />
// ❌ Không hard-code hex
<div style={{ color: "#f97316" }} />
Git Commits
# Format: <type>: <subject>
# Types: feat | fix | docs | style | refactor | perf | test | build | ci | chore
git commit -m "feat: add store selection page"
git commit -m "fix: auto-select first store"
git commit -m "refactor: simplify login flow"