187 lines
4.4 KiB
Markdown
187 lines
4.4 KiB
Markdown
# Coding Standards — Tour Planner App
|
|
|
|
Aturan ini berlaku untuk semua kode yang ditulis dalam proyek ini.
|
|
AI agent wajib mengikuti seluruh aturan di bawah ini.
|
|
|
|
---
|
|
|
|
## 1. Struktur Komponen
|
|
|
|
```jsx
|
|
// ✅ BENAR — Functional component dengan arrow function
|
|
const MyComponent = ({ prop1, prop2 }) => {
|
|
// hooks di atas
|
|
// logic di tengah
|
|
// return di bawah
|
|
return <div />;
|
|
};
|
|
|
|
export default MyComponent;
|
|
|
|
// ❌ SALAH — Jangan gunakan class component
|
|
class MyComponent extends React.Component { }
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Styling
|
|
|
|
```jsx
|
|
// ✅ BENAR — CSS variables dari design system
|
|
<div style={{ color: 'var(--color-text-primary)', padding: 'var(--space-4)' }}>
|
|
|
|
// ✅ BENAR — className dengan class yang didefinisikan di CSS
|
|
<div className="glass section-title">
|
|
|
|
// ❌ SALAH — Hardcode warna
|
|
<div style={{ color: '#ffffff', backgroundColor: '#1a1a2e' }}>
|
|
|
|
// ❌ SALAH — TailwindCSS
|
|
<div className="text-white bg-gray-900 p-4">
|
|
```
|
|
|
|
---
|
|
|
|
## 3. State Management
|
|
|
|
```javascript
|
|
// ✅ BENAR — Gunakan Zustand store untuk state yang di-share
|
|
const { schedule, addActivity } = useTourStore();
|
|
|
|
// ✅ BENAR — useState untuk state lokal (form, toggle)
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
// ❌ SALAH — Prop drilling lebih dari 2 level
|
|
<A> <B prop={x}> <C prop={x}> <D prop={x} />
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Animasi
|
|
|
|
```jsx
|
|
// ✅ BENAR — Selalu gunakan Framer Motion
|
|
import { motion } from 'framer-motion';
|
|
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
|
|
|
|
// ❌ SALAH — Animasi manual
|
|
setTimeout(() => setVisible(true), 100);
|
|
element.style.transition = 'opacity 0.3s';
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Icons
|
|
|
|
```jsx
|
|
// ✅ BENAR — Lucide React
|
|
import { MapPin, Calendar, Plane } from 'lucide-react';
|
|
<MapPin size={20} />
|
|
|
|
// ❌ SALAH — Emoji sebagai icon UI
|
|
<span>📍</span>
|
|
|
|
// ❌ SALAH — SVG inline tanpa abstraksi
|
|
<svg xmlns="..." viewBox="..."><path d="..." /></svg>
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Data & Constants
|
|
|
|
```javascript
|
|
// ✅ BENAR — Data di src/data/
|
|
import { destinations } from '../data/destinations';
|
|
import { CATEGORY_COLORS } from '../data/constants';
|
|
|
|
// ❌ SALAH — Data hardcode di dalam komponen
|
|
const destinations = [{ name: 'Bali', ... }]; // dalam file .jsx
|
|
```
|
|
|
|
---
|
|
|
|
## 7. File Naming
|
|
|
|
| Tipe | Convention | Contoh |
|
|
|------|------------|--------|
|
|
| Component | PascalCase | `TripCard.jsx` |
|
|
| Hook | camelCase dengan "use" prefix | `useBudget.js` |
|
|
| Utility | camelCase | `formatCurrency.js` |
|
|
| Data file | camelCase | `destinations.js` |
|
|
| CSS | kebab-case | `trip-card.css` |
|
|
|
|
---
|
|
|
|
## 8. Import Order
|
|
|
|
```javascript
|
|
// 1. React & hooks
|
|
import { useState, useEffect } from 'react';
|
|
|
|
// 2. Third-party libraries
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { MapPin } from 'lucide-react';
|
|
|
|
// 3. Internal store & hooks
|
|
import { useTourStore } from '../store/tourStore';
|
|
import { useBudget } from '../hooks/useBudget';
|
|
|
|
// 4. Components
|
|
import Button from '../components/ui/Button';
|
|
import TripCard from '../components/trip/TripCard';
|
|
|
|
// 5. Data & utils
|
|
import { destinations } from '../data/destinations';
|
|
import { formatIDR } from '../utils/formatCurrency';
|
|
|
|
// 6. Styles (jika ada)
|
|
import './ComponentName.css';
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Performance
|
|
|
|
- Gunakan `useMemo` untuk kalkulasi berat (total budget, statistik packing)
|
|
- Gunakan `useCallback` untuk handler yang di-pass sebagai prop
|
|
- Gunakan `React.memo` untuk komponen list item yang berat
|
|
- Jangan lakukan side effect tanpa `useEffect`
|
|
|
|
---
|
|
|
|
## 10. Batasan Fitur (TIDAK BOLEH DILANGGAR)
|
|
|
|
| Larangan | Alasan |
|
|
|----------|--------|
|
|
| ❌ Login / Logout | Aplikasi ini sepenuhnya static & lokal |
|
|
| ❌ API call ke backend | Tidak ada server, semua data lokal |
|
|
| ❌ Menyimpan password atau data sensitif | Tidak ada autentikasi |
|
|
| ❌ TailwindCSS | Gunakan vanilla CSS + CSS variables |
|
|
| ❌ jQuery atau manipulasi DOM manual | Gunakan React state |
|
|
| ❌ `console.log` di production code | Hapus sebelum commit |
|
|
|
|
---
|
|
|
|
## 11. Aksesibilitas (Minimum Requirements)
|
|
|
|
- Semua tombol punya `aria-label` jika tidak ada teks
|
|
- Semua form field punya `<label>` yang terhubung
|
|
- Warna kontras minimum 4.5:1 untuk teks
|
|
- Keyboard navigable untuk modal (focus trap)
|
|
|
|
---
|
|
|
|
## 12. Error Handling
|
|
|
|
```javascript
|
|
// Selalu handle edge case
|
|
const totalDays = trip?.totalDays ?? 0;
|
|
const activities = schedule[dayIndex]?.activities ?? [];
|
|
|
|
// Jangan biarkan crash karena undefined
|
|
// ❌
|
|
const name = trip.name.toUpperCase(); // crash jika trip.name null
|
|
// ✅
|
|
const name = trip?.name?.toUpperCase() ?? 'Unnamed Trip';
|
|
```
|