Skip to content

Frontend Architecture

KoAkademy delivers an interactive, single-page-application feel using Inertia 3 paired with React 19 and server-side rendering (SSR enabled in resources/js/ssr.tsx). Server-side Laravel controllers manage state, routing, and authorization, while React renders responsive components on the client without modern SPA routing complexity.

All frontend pages reside in resources/js/pages/ and mirror the application audiences:

resources/js/
├── actions/ # Generated Wayfinder controller helpers
├── routes/ # Generated Wayfinder named route helpers
├── components/
│ ├── ui/ # Reusable shadcn/ui primitives (Radix UI)
│ ├── administrators/ # Admin-specific UI blocks & dialogs
│ ├── faculty/ # Faculty tools, gradebooks, attendance
│ └── student/ # Student schedule, statement of account
├── lib/ # Utilities (cn, formatting, api clients)
├── pages/
│ ├── administrators/ # Staff workspace (students, classes, finance, policies)
│ ├── faculty/ # Faculty action center, grade submission, agendas
│ ├── student/ # Student dashboard, digital ID, statements
│ ├── enrollment/ # Public student registration wizard
│ ├── setup/ # First-run institution initialization wizard
│ ├── auth/ # Login, 2FA challenge, passkey flows
│ └── docs/ # In-app documentation reader
├── app.tsx # Inertia client entry point
└── ssr.tsx # Inertia server-side renderer entry point

Instead of hard-coding URLs or writing brittle route strings, frontend code imports type-safe route helpers generated by Laravel Wayfinder:

import { store, update } from "@/actions/App/Http/Controllers/AdministratorEnrollmentPolicyController";
import { useForm } from "@inertiajs/react";
export function PolicyEditor({ policyId }: { policyId: number }) {
const form = useForm({
name: "College Enrollment Blueprint",
inherit: true,
});
const submitCreate = () => {
// Wayfinder provides typed action URLs
form.post(store.url());
};
const submitUpdate = () => {
form.put(update.url({ enrollmentPolicy: policyId }));
};
return (
<form onSubmit={submitUpdate}>
<button type="submit" disabled={form.processing}>
Save Changes
</button>
</form>
);
}

KoAkademy uses shadcn/ui built on top of Radix UI primitives and styled with Tailwind CSS 4 (@tailwindcss/vite):

  • Primitives: Radix UI components (Dialog, DropdownMenu, Popover, Tooltip, Select, Tabs, Accordion) guarantee full keyboard navigation, screen reader accessibility, and correct ARIA states.
  • Icons: Standardized on lucide-react across all portals.
  • Animations: Subtle interactions using framer-motion and Tailwind CSS transitions. Always respect prefers-reduced-motion.
  • Forms & Validation: Inertia’s useForm hook handles form state, submission lifecycle, and server-side validation error mapping.

Practical Component Sample: Modal Form with Validation

Section titled “Practical Component Sample: Modal Form with Validation”

Here is the standard pattern for creating interactive modal dialogs across portals:

import { store } from "@/actions/App/Http/Controllers/AdministratorEnrollmentPolicyController";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useForm } from "@inertiajs/react";
import { Plus } from "lucide-react";
import { useState } from "react";
export function CreateBlueprintDialog() {
const [open, setOpen] = useState(false);
const form = useForm({
name: "",
school_year: "",
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
form.post(store.url(), {
preserveScroll: true,
onSuccess: () => {
form.reset();
setOpen(false);
},
});
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>
<Plus className="mr-2 size-4" /> New Blueprint
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<form onSubmit={handleSubmit}>
<DialogHeader>
<DialogTitle>Create Enrollment Blueprint</DialogTitle>
<DialogDescription>
Define a new policy scope for student admissions.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="name">Blueprint Name</Label>
<Input
id="name"
value={form.data.name}
onChange={(e) => form.setData("name", e.target.value)}
placeholder="Senior High School 2026"
/>
{form.errors.name && (
<p className="text-destructive text-sm" role="alert">
{form.errors.name}
</p>
)}
</div>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => setOpen(false)}
>
Cancel
</Button>
<Button type="submit" disabled={form.processing}>
{form.processing ? "Creating..." : "Create"}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}
LibraryRole
@tanstack/react-tableData tables with pagination, sorting, and multi-filter capabilities in administrative portals
@tiptap/reactRich text editors for class syllabus, announcements, and notes
recharts / @visx/*Institutional analytics, enrollment pipeline visualization, and finance graphs
signature_padDigital student and registrar signature capture on admission forms
sonnerAccessible toast notifications for background job dispatches and form confirmations
laravel-echo / pusher-jsReal-time WebSocket event listeners for live notifications and status updates

When contributing frontend code:

  1. Strict TypeScript: Provide explicit prop interfaces; avoid any.
  2. Server-Side Validation: Rely on Laravel Form Requests as the source of truth for validation errors. Display error messages directly next to input fields using form.errors.
  3. Accessibility: Preserve accessible markup:
    • Always associate <Label htmlFor="..."> with <Input id="...">.
    • Ensure all icon buttons have an aria-label.
    • Never remove focus visible indicators (focus-visible:ring-*).
  4. Formatting & Linting: Run npm run lint and npm run format before submitting PRs.