Skip to content

feat: migrate Angular 16 frontend to React 18 + TypeScript#3

Open
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1775540208-migrate-angular-to-react
Open

feat: migrate Angular 16 frontend to React 18 + TypeScript#3
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1775540208-migrate-angular-to-react

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Complete rewrite of the Spring PetClinic frontend from Angular 16 to React 18 + TypeScript. The backend REST API (http://localhost:9966/petclinic/api/) is unchanged.

New tech stack: Vite, React Router v6, React Query (@tanstack/react-query), react-hook-form, Axios, Bootstrap 3.

What changed:

  • All Angular source (src/app/), config (angular.json, karma.conf.js, etc.), and e2e tests deleted
  • New React app structure: src/models/, src/api/, src/hooks/, src/pages/, src/components/
  • Dockerfile upgraded from Node 16 to Node 20, added Nginx SPA fallback (try_files)
  • README rewritten for the new stack
  • Static assets moved from src/assets/ to public/

Updates since last revision

  • Fixed route/param consistency: OwnerDetail.tsx now navigates to /owners/${id}/pets/${pet.id}/edit and /owners/${id}/pets/${pet.id}/visits/add, correctly supplying ownerId to downstream components.
  • Fixed VisitEdit.tsx type hack: Removed navigate(-1 as unknown as string) in favor of navigate(-1).
  • Cleaned up Dockerfile dead code: Removed the now-redundant sed command for the listen port, since printf overwrites default.conf entirely.

Review & Testing Checklist for Human

  • Fallback route params: App.tsx still defines fallback routes /pets/:petId/edit and /pets/:petId/visits/add (without :ownerId in the path). If a user navigates to these URLs directly, ownerId will be undefined, causing broken API calls in PetEdit and VisitAdd. Consider removing these fallback routes or adding guards.
  • API endpoint mapping: Verify each API file (ownerApi.ts, petApi.ts, visitApi.ts, etc.) matches the actual Spring PetClinic REST endpoints. Especially addPetPOST owners/${ownerId}/pets and addVisitPOST owners/${ownerId}/pets/${petId}/visits.
  • No unit tests: The migration plan called for Vitest + React Testing Library tests, but none were written. The npm test script exists but will find no test files.
  • End-to-end manual test: Start the backend spring-petclinic-rest, run npm run dev, and verify: owner search/add/edit/delete, pet add/edit from owner detail, visit add/edit, vet list/add/edit, pet types CRUD, specialties CRUD. Delete operations have no confirmation dialog — verify this is acceptable.

Notes

  • .env is committed with VITE_REST_API_URL=http://localhost:9966/petclinic/api/ (not a secret, but worth noting)
  • Bootstrap 3.4.1 is EOL but preserved to match the original Angular app's styling
  • Build and lint pass locally (npm run build, npm run lint)

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/f01a71fe45ab4c7aa55bc86885cd0382


Open with Devin

- Replace Angular CLI with Vite build tool
- Implement React 18 with TypeScript, React Router v6, React Query, react-hook-form
- Create data models, API layer with Axios, React Query hooks
- Build all page components: Owners, Pets, Visits, Vets, PetTypes, Specialties
- Add shared Navbar and Layout components with Bootstrap 3 styling
- Configure client-side routing matching original Angular routes
- Update Dockerfile to Node 20 with Nginx SPA fallback
- Update README with new tech stack documentation
- Remove all Angular source files, configs, and e2e tests
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment thread src/App.tsx
<Route path="/owners/:id/pets/add" element={<PetAdd />} />
<Route path="/owners/:ownerId/pets/:petId/edit" element={<PetEdit />} />
<Route path="/owners/:ownerId/pets/:petId/visits/add" element={<VisitAdd />} />
<Route path="/pets/:petId/edit" element={<PetEdit />} />

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 PetEdit breaks on /pets/:petId/edit route due to missing ownerId param

The PetEdit component destructures ownerId from useParams (src/pages/pets/PetEdit.tsx:14), but the route at src/App.tsx:37 (/pets/:petId/edit) does not include an ownerId segment. When this route is matched, ownerId is undefined, causing: (1) Number(ownerId)NaN sent in the API request body (PetEdit.tsx:43), making the update request malformed; (2) the Back button navigates to /owners/undefined (PetEdit.tsx:95); (3) success redirect goes to /owners/undefined (PetEdit.tsx:46).

Prompt for agents
The route /pets/:petId/edit renders PetEdit which requires ownerId from URL params, but this route does not include ownerId. The PetEdit component at src/pages/pets/PetEdit.tsx:14 destructures ownerId from useParams, but it will be undefined on this route.

Two possible fixes:
1. Remove the /pets/:petId/edit route from App.tsx if it's not needed (since /owners/:ownerId/pets/:petId/edit already exists).
2. Make PetEdit resilient to a missing ownerId by falling back to pet.ownerId from the fetched pet data. In the onSubmit handler, use pet?.ownerId instead of Number(ownerId) when ownerId is undefined. Similarly, update the Back button and success navigation to use pet?.ownerId as a fallback.

The same pattern applies to the /pets/:petId/visits/add route and the VisitAdd component.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/App.tsx
<Route path="/owners/:ownerId/pets/:petId/edit" element={<PetEdit />} />
<Route path="/owners/:ownerId/pets/:petId/visits/add" element={<VisitAdd />} />
<Route path="/pets/:petId/edit" element={<PetEdit />} />
<Route path="/pets/:petId/visits/add" element={<VisitAdd />} />

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 VisitAdd breaks on /pets/:petId/visits/add route due to missing ownerId param

The VisitAdd component destructures ownerId from useParams (src/pages/visits/VisitAdd.tsx:12), but the route at src/App.tsx:38 (/pets/:petId/visits/add) does not include ownerId. When this route is matched, ownerId is undefined, causing: (1) Number(ownerId)NaN passed to addVisit(NaN, petId, visit), producing a request to owners/NaN/pets/${petId}/visits (src/api/visitApi.ts:15) which will fail; (2) Back button and success redirect navigate to /owners/undefined (VisitAdd.tsx:80,25).

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<div>No owners with LastName starting with "{searchTerm}"</div>
)}

{owners && owners.length > 0 && (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 'Add Owner' button hidden when owner list is empty, preventing creation of first owner

The "Add Owner" button is rendered inside the {owners && owners.length > 0 && ...} conditional block at src/pages/owners/OwnerList.tsx:51. When there are zero owners in the system (e.g., fresh database), the button is not rendered and there is no other navigation to /owners/add in the UI (the navbar only has a link to /owners). This is a regression from the old Angular code where the button was shown whenever data was received, regardless of count. Users with an empty database cannot add their first owner through the UI.

Suggested change
{owners && owners.length > 0 && (
{owners && owners.length > 0 && (
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants