Codebase guidelines for agentic coding operations.
# Development
npm run dev # Start Vite dev server (port 5173)
# Build
npm run build # Production build
# Linting & Formatting
npm run lint # Run ESLint
npm run preview # Preview production buildNote: No test framework is configured. Tests directory exists but contains only utility scripts.
- TypeScript: 5.8.3, strict mode enabled
- React: 19.1.0
- Bundler: Vite 6.3.5
- State Management: Zustand (client), TanStack Query (server)
- Styling: Tailwind CSS 3.4.3 with Shadcn UI components
- Path Alias:
@/*→src/*
// External libraries first
import * as React from "react";
import { useQuery } from "@tanstack/react-query";
import { toast } from "sonner";
// Type imports (use import type)
import type { HomeResponse } from "@/types/diary";
// Local imports (use @ alias for src, relative for same directory)
import { getHomeData } from "@/api/services/home";
import DiaryCards from "../components/home/DiaryCards";- Semicolons: Required (
semi: true) - Quotes: Double quotes (
singleQuote: false) - Width: 100 chars (
printWidth: 100) - Indent: 2 spaces
- Trailing commas: ES5 (
trailingComma: "es5") - Arrow parens: Avoid when possible (
arrowParens: "avoid")
// Components: PascalCase
const Home = () => {};
const DiaryCards = () => {};
// Hooks: camelCase with 'use' prefix
export const useAuth = () => {};
export const useGetHomeData = (token: string) => {};
// API queries: useGet[Resource], use[Action][Resource]
(useGetHomeData, useDeleteDiary, useCreateTodo);
// Services: camelCase, descriptive verbs
(getHomeData, createTodo, updateTodoContent);
// Types/Interfaces: PascalCase
interface HomeResponse {}
interface ApiTodo {}
// Zustand stores: use[StoreName]
const useUserStore = create<AuthStore>(() => {});
const useTodoStore = create<TodoStore>(() => {});
// Constants: PascalCase (in constants/)
const EMOTION_COLORS = { happy: "#FF6B6B" };// Query hooks: useQuery with enabled condition
export const useGetHomeData = (token: string) => {
return useQuery<HomeResponse>({
queryKey: ["homeData", token],
queryFn: () => getHomeData(token),
enabled: !!token, // Don't fetch without token
refetchOnWindowFocus: true,
staleTime: 0,
});
};
// Mutation hooks: useMutation with callbacks
export const useDeleteDiary = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ diaryId }: { diaryId: string }) => deleteDiary(diaryId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["diaries"] });
toast.success("Success message");
},
onError: (error: any) => {
console.error("Operation failed:", error);
toast.error("Error message");
},
});
};// API interceptors (axios.ts) handle:
// - 401: Automatic token refresh via refresh endpoint
// - Failed refresh: Show logout modal, redirect to /login
// - 500+: Log to console
// Service layer: Try-catch with console.error and re-throw
export const createTodo = async ({ content, date }) => {
try {
const response = await api.post("/todos/calendar", { content, date });
return response.data;
} catch (error) {
console.error("❌ createTodo error:", error);
throw error;
}
};
// Mutation layer: toast notifications for UX
onError: (error: any) => {
console.error("Error:", error);
toast.error("Operation failed", { description: "Please try again" });
};// Shadcn UI components with cn() for className merging
import { cn } from "@/lib/utils";
const Button = ({ className, variant, size, ...props }) => {
return <button className={cn(buttonVariants({ variant, size }), className)} {...props} />;
};
// Props interface for TypeScript
interface ProtectedRouteProps {
children: React.ReactNode;
requireAuth?: boolean;
}
// React.forwardRef for ref forwarding
const Component = React.forwardRef<HTMLDivElement, Props>(({ className, ...props }, ref) => {
return <div ref={ref} className={cn("base-styles", className)} {...props} />;
});unused-imports/no-unused-imports: "error"- Auto-remove unused importsnoUnusedLocals: true- TypeScript rulenoUnusedParameters: true- TypeScript rulereact-refresh/only-export-components: "warn"- Vite HMR optimization
- Authentication: JWT in localStorage, refresh via HTTP-only cookie
- Route Protection:
<ProtectedRoute>wrapper component - Toast Notifications: Use
sonnerfor success/error messages - Dark Mode: Class-based dark mode with Tailwind
- Utility Functions: Use
cn()from@/lib/utilsfor className merging - Query Keys: Consistent naming like
["homeData", token],["diaries"]
- Run
npm run lint- Fix all errors - Check TypeScript types - No
anytypes unless absolutely necessary - Remove unused imports - ESLint will auto-fix
- Verify error handling - Ensure try-catch and toast notifications
- Test authentication flows - Check 401 handling and logout behavior