A full-stack web app with two separate views — a customer-facing storefront that fetches and renders donuts from a PHP REST API, and an admin panel for managing the menu with full CRUD operations.
The storefront is built as a React app that fetches the donut list from the PHP API on mount via useEffect. Each donut is rendered by a Donut
component that receives name, description, and price as props — the component defines the card structure
once and the data drives how many times it renders.
Adding a new donut to the database automatically adds a new card to the page without any code changes — the component just maps over whatever the API returns.
App owns the data fetching and state. Donut is a
pure presentational component — it receives props and renders, with no knowledge of where the data comes
from.
export function Donut({ name, description, price }) { return ( <div className="DonutPanel"> <h1>{name}</h1> <p className="donutdescription"> {description} </p> <h2>{price}</h2> </div> ); } // In App — data drives rendering, no manual card creation {donuts.map(donut => <Donut name={donut.Name} description={donut.Description} price={donut.Price} /> )}
The admin panel uses controlled inputs — each form field is bound to a useState hook and updates on every keystroke via onChange. React owns the field values, so reading them for an API call is just
reading state rather than querying the DOM.
Selecting a donut from the dropdown populates all three fields immediately from the donuts array already in state — no extra fetch needed. Update and Delete buttons
are disabled until a donut is selected, derived directly from whether selectedId is empty.
donuts array is updated
directly — the page reflects the change immediately without re-fetching from the API.
A single index.php file handles all four HTTP methods, routing on $_SERVER['REQUEST_METHOD'] to the appropriate handler. All database queries use
prepared statements throughout.
| Method | Operation | Details |
|---|---|---|
| GET | Fetch all donuts | Returns full Donuts array — used by both storefront on mount and admin
dropdown |
| POST | Add donut | Inserts new row, returns generated ID so client can update state without re-fetching |
| PUT | Update donut | Checks existence before updating — returns 400 if ID not found |
| DELETE | Delete donut | Checks existence before deleting — returns 400 if ID not found |