Sử dụng khi tạo mới hoặc sửa đổi React Components trong thư mục frontend/src/components/ hoặc frontend/src/pages/.
Skill này áp dụng cho mọi thay đổi trong:
frontend/src/components/ — Shared components (Navbar, Sidebar, DbCard, ConnectionPanel, ConnectedList, SemanticLayer...)frontend/src/pages/ — Page components (ChatPage, DataSourcesPage, LoginPage, RegisterPage, SemanticLayerPage, SettingsPage, LandingPage)| Package | Phiên bản | Mục đích |
|---|---|---|
| React | 19 | UI framework |
| TypeScript | 5.x | Type safety |
| Vite | 7 | Dev server / bundler |
| lucide-react | latest | Icon library duy nhất |
| CSS Modules | built-in | Scoped styling |
Tuyệt đối không dùng: Tailwind CSS, styled-components, emotion, inline styles.
Mỗi component phải có cấu trúc thư mục riêng:
ComponentName/
├── ComponentName.tsx ← Logic + JSX
├── ComponentName.module.css ← Styles (CSS Modules)
└── index.ts ← Re-export (optional nhưng nên có)
index.ts mẫu:
export { default } from './ComponentName';
// hoặc nếu có named export:
export { ComponentName } from './ComponentName';
Tất cả giá trị màu sắc được lấy từ src/index.css — luôn dùng CSS variables, không hardcode giá trị:
| Token | Giá trị | Dùng khi |
|---|---|---|
var(--bg-base) | #0F172A | Nền trang chính |
var(--bg-card) | #1E293B | Nền card, panel, container |
var(--bg-panel) | #161E2E | Nền slide-in panel (ConnectionPanel) |
var(--bg-input) | #0F172A | Nền input field |
var(--border) | #334155 | Viền mặc định |
var(--purple) | #7C3AED | Màu accent chính — nút primary, highlight |
var(--purple-lt) | #A855F7 | Hover state, gradient accent |
var(--green) | #10B981 | Trạng thái connected/success |
var(--red) | #EF4444 | Trạng thái error/danger |
var(--text) | #F1F5F9 | Text chính |
var(--text-muted) | #94A3B8 | Text phụ, placeholder, label |
var(--radius) | 12px | Border radius mặc định |
import styles from './ComponentName.module.css';
import { SomeIcon } from 'lucide-react';
interface ComponentNameProps {
// Khai báo props rõ ràng với TypeScript
title: string;
onAction: () => void;
isActive?: boolean;
}
export default function ComponentName({ title, onAction, isActive = false }: ComponentNameProps) {
return (
<div className={`${styles.container} ${isActive ? styles.active : ''}`}>
<SomeIcon size={18} />
<span>{title}</span>
<button className={styles.actionBtn} onClick={onAction}>
Click
</button>
</div>
);
}
.container {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 1rem;
}
.container.active {
border-color: var(--purple);
}
.actionBtn {
background: var(--purple);
color: var(--text);
border: none;
border-radius: 8px;
padding: 0.5rem 1rem;
cursor: pointer;
transition: background 0.2s;
}
.actionBtn:hover {
background: var(--purple-lt);
}
useAuth() — Auth context:import { useAuth } from '@/hooks/useAuth';
const { user, isAuthenticated, isLoading, login, logout, getValidToken } = useAuth();
user: { id, username, email, role } hoặc nullgetValidToken(): Trả về access token hợp lệ (auto-refresh nếu gần hết hạn). Luôn dùng cái này khi gọi API cần auth.useSettings() — Settings context:import { useSettings } from '@/contexts/SettingsContext';
const { language, theme, setLanguage, setTheme } = useSettings();
useTranslation() — i18n:import { useTranslation } from 'react-i18next';
const { t } = useTranslation();
// Dùng: t('key.subkey')
Luôn dùng các service file có sẵn, không fetch trực tiếp trong component:
// Auth
import { authApi } from '@/services/authApi';
// Connections
import { connectionApi } from '@/services/connectionApi';
// Chat
import { chatApi } from '@/services/chatApi';
Pattern gọi API với auth:
const { getValidToken } = useAuth();
async function fetchData() {
const token = await getValidToken();
const result = await connectionApi.getAll(token);
setConnections(result);
}
useEffect(() => {
fetchData();
}, []);
src/components/| Component | Mô tả |
|---|---|
Navbar/ | Top navigation bar, hiện user info + logout |
Sidebar/ | Left sidebar menu (Menu + Hệ thống groups) |
DbCard/ | Card chọn DB engine (PostgreSQL, MySQL...) |
ConnectionPanel/ | Slide-in drawer cấu hình kết nối |
ConnectedList/ | Danh sách kết nối đang active |
SemanticLayer/ | React Flow diagram cho semantic layer |
ProtectedRoute/ | Route guard (auth check) |
src/pages/| Page | Route | Mô tả |
|---|---|---|
LandingPage/ | /landing | Landing page marketing |
LoginPage/ | auth gate | Đăng nhập |
RegisterPage/ | auth gate | Đăng ký tài khoản |
DataSourcesPage/ | main | Quản lý data source |
SemanticLayerPage/ | main | Chỉnh sửa semantic layer |
ChatPage/ | main | Chat với AI agent |
SettingsPage/ | main | Cài đặt theme/ngôn ngữ |
Luôn import từ lucide-react. Size mặc định cho icon trong UI:
size={16} hoặc size={18}size={20} hoặc size={24}size={32} hoặc size={48}import { Database, Settings, LogOut, ChevronRight } from 'lucide-react';
Sidebar hiện có menu structure (dùng activePage prop để highlight):
datasources, semantic, chatsettings, accountKhi thêm page mới → cập nhật cả:
Sidebar/Sidebar.tsx — thêm menu itemApp.tsx — thêm vào PAGE_MAPtransition cho hover effects (0.2s ease)translateX animation@keyframes pulse!important trong CSS — nếu cần override, tăng specificity bằng cách nest class37:["$","$L3e",null,{"content":"$3f","frontMatter":{"name":"ui-component","description":"Sử dụng khi tạo mới hoặc sửa đổi React Components trong thư mục frontend/src/components/ hoặc frontend/src/pages/."}}]