34109-vm/agent.md
2025-09-19 13:31:11 +00:00

2.8 KiB
Raw Permalink Blame History

Agent Coding Guide: Laravel + React SPA with SWR & Zustand

Use this for coding guide and use agent-tasks.md for backlog and tracking progress.

1. Project Structure

Backend (Laravel):

  • Standard folders:
    • app/Http/Controllers (thin, validate, call services)
    • app/Services (business logic)
    • routes/api.php (API endpoints, RESTful JSON)
    • app/Models (Eloquent ORM)
    • app/Http/Resources (format API responses)
  • Use Laravel Sanctum for SPA authentication.

Frontend (React):

  • React app inside /resources/js/react for seamless Laravel integration.
  • Functional components with hooks.
  • React Router for client-side routing.
  • Zustand for global state management.
  • SWR for all server data fetching and caching.
  • Axios as HTTP client used within SWR fetcher function.

2. Code & Style

Backend (Laravel/PHP):

  • PSR-12 standard.
  • Controllers handle request, validation, call service methods, return API Resources.
  • Services contain business logic.
  • Use JSON responses throughout.

Frontend (React/JS):

  • Functional components with hooks and JSX.
  • Manage UI state (modals, forms) locally or via Zustand.
  • Use Axios inside SWR for API calls to leverage SWR caching and revalidation.
  • Leverage SWR hooks (useSWR) for data fetching with automatic caching, re-fetching, and error handling—avoid manual state for server data.
  • Use Zustand only for client state that doesnt come from API (e.g., UI toggles, theme, user session info).
  • Use React Router for SPA navigation, avoiding full page reloads.

3. Example Usage

Zustand Store (Client UI State)

// store/useStore.js
import create from 'zustand';

export const useStore = create(set => ({
  isModalOpen: false,
  openModal: () => set({ isModalOpen: true }),
  closeModal: () => set({ isModalOpen: false }),
  editingPlayer: null,
  setEditingPlayer: (player) => set({ editingPlayer: player }),
}));

SWR Data Fetching with Axios

// utils/api.js
import axios from 'axios';

export const apiClient = axios.create({
  baseURL: '/api',
  withCredentials: true,
});

export const fetcher = url => apiClient.get(url).then(res => res.data);

// components/PlayerList.js
import useSWR from 'swr';
import { fetcher } from '../utils/api';
import { useStore } from '../store/useStore';

function PlayerList() {
  const { data, error } = useSWR('/players', fetcher);
  const { openModal, setEditingPlayer } = useStore();

  if (error) return <div>Failed to load</div>;
  if (!data) return <div>Loading...</div>;

  const handleEdit = (player) => {
    setEditingPlayer(player);
    openModal();
  };

  return (
    <ul>
      {data.players.map(player => (
        <li key={player.id}>
          {player.name}
          <button onClick={() => handleEdit(player)}>Edit</button>
        </li>
      ))}
    </ul>
  );
}