Files
Ready-to-Planner-Tour/.agents/rules/coding-standards.md
T
2026-08-27 19:45:15 +07:00

4.4 KiB

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

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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

// 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

// 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';