Scaffold Inertia Page
user-invocable: true allowed-tools: Read, Write, Edit, Bash, Glob, Grep argument-hint: <folder/page_name>
Description
Scaffolds a full Inertia.js page with Rails controller, route, and tests. The argument is the page path (e.g., settings/index, chat/show).
Steps
Given an argument like settings/index:
- •React page component at
app/frontend/pages/settings/index.tsx - •Rails controller at
app/controllers/settings_controller.rbwith the action rendering the Inertia page - •Route added to
config/routes.rb - •Integration test at
test/integration/settings_test.rb - •Frontend test at
app/frontend/test/pages/settings/index.test.tsx
Templates
React Page Component
Follow this exact pattern from the existing home/index page:
tsx
// app/frontend/pages/<folder>/<page>.tsx
interface Props {
// Add props here
}
export default function <PascalName>({ }: Props) {
return (
<div className="min-h-screen bg-dashbot-bg text-dashbot-text flex items-center justify-center">
<h1><Human-readable name></h1>
</div>
)
}
- •Default export, PascalCase function name derived from folder + page (e.g.,
settings/index→SettingsIndex) - •
interface Propsalways defined at top - •Use
@/alias for imports (e.g.,@/components/ui/button)
Rails Controller
Follow this exact pattern from HomeController:
ruby
# frozen_string_literal: true
class <Name>Controller < ApplicationController
def <action>
render inertia: "<folder>/<page>"
end
end
- •
frozen_string_literal: trueheader - •Inherits
ApplicationController(which includes theAuthenticationconcern) - •Action name matches the page name (e.g.,
index,show)
Route
Add to config/routes.rb following the existing pattern. Read the file first and add the route in the appropriate section:
ruby
get "<path>", to: "<controller>#<action>", as: :<route_name>
Integration Test
Follow this exact pattern from test/integration/home_test.rb:
ruby
# frozen_string_literal: true
require "test_helper"
class <Name>Test < ActionDispatch::IntegrationTest
test "GET /<path> renders Inertia page when authenticated" do
sign_in_as(users(:admin))
get <route_helper>_path
assert_response :success
match = response.body.match(%r{<script[^>]*data-page[^>]*>(.+?)</script>}m)
assert match, "Expected Inertia page data in response body"
page = JSON.parse(match[1])
assert_equal "<folder>/<page>", page["component"]
end
test "GET /<path> redirects to login when not authenticated" do
get <route_helper>_path
assert_redirected_to login_path
end
end
Frontend Test
Follow this exact pattern from app/frontend/test/pages/home/index.test.tsx:
tsx
import { render, screen } from "@testing-library/react"
import <ComponentName> from "@/pages/<folder>/<page>"
describe("<ComponentName>", () => {
it("renders without crashing", () => {
render(<<ComponentName> />)
expect(screen.getByText("<expected text>")).toBeInTheDocument()
})
})
- •Use
@/path alias for imports - •
vi,describe,it,expectare globals (no imports needed) - •Use
@testing-library/reactfor rendering - •Mock
@inertiajs/reactif the component usesrouter:tsxvi.mock("@inertiajs/react", () => ({ router: { get: vi.fn(), post: vi.fn(), delete: vi.fn() }, }))
Notes
- •Parse the argument to extract folder and page name (e.g.,
settings/index→ folder:settings, page:index) - •Use PascalCase for component names:
settings/index→SettingsIndex - •Use snake_case for Ruby files:
settings_controller.rb - •Always read
config/routes.rbbefore editing to place the route correctly - •All Ruby files must have
frozen_string_literal: true