Feature/front end react#215
Conversation
# Conflicts: # src/main/java/org/example/vet1177/controller/PetController.java
…ler.java PetService.java to solve errors/bugs
# Conflicts: # src/main/java/org/example/vet1177/entities/ActivityLog.java
📝 WalkthroughWalkthroughAdds a new React + Vite + Tailwind frontend (pages, components, services, auth flow) and multiple backend endpoints/service updates (authenticated pet listing, activity-log/all, vet-assigned records, JWT claim changes, transactional/entity tweaks). Changes
Sequence Diagram(s)sequenceDiagram
participant User as User (Browser)
participant Frontend as Frontend (React)
participant Local as LocalStorage/Session
participant API as Axios Client
participant Backend as Backend (Spring Boot)
User->>Frontend: Open app / submit login
Frontend->>API: POST /auth/login
API->>Backend: Forward credentials
Backend-->>API: Return JWT
API-->>Frontend: Response with token
Frontend->>Local: Store token (local/session)
Frontend->>Frontend: Decode token (name, userId, role, clinicId?)
Frontend->>API: GET /api/pets (Authorization: Bearer ...)
API->>Backend: Validate token, fetch pets
Backend-->>API: Return pet list
API-->>Frontend: Pet data
Frontend-->>User: Render role-specific dashboard
sequenceDiagram
participant User as User (Browser)
participant Frontend as Frontend (React)
participant API as Axios Client
participant Backend as Backend (Spring Boot)
User->>Frontend: Open "Create case" form
Frontend->>API: GET /api/clinics
API->>Backend: Fetch clinics
Backend-->>API: Clinics list
User->>Frontend: Submit case + attachments
Frontend->>API: POST /api/medical-records
API->>Backend: Create record -> return id
Frontend->>API: POST /api/attachments (multipart) [repeat per file]
API->>Backend: Store attachment(s)
Backend-->>API: Attachment metadata
API-->>Frontend: Attachments saved
Frontend-->>User: Redirect to case detail with timeline
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/resources/data.sql (1)
22-48:⚠️ Potential issue | 🟡 MinorInconsistent
ON CONFLICTupdates across seed users.Anna's UPSERT (Line 22) now updates
password_hashon conflict, but Erik's (Line 36) and Sara's (Line 48) do not. If the bcrypt hashes for those accounts are ever rotated in this file, existing rows will silently keep the old hash and authentication will diverge between a fresh DB and an existing one. Apply the same fix to Erik and Sara for consistency.🔧 Suggested fix
- ON CONFLICT (email) DO UPDATE SET id = EXCLUDED.id, name = EXCLUDED.name; + ON CONFLICT (email) DO UPDATE SET id = EXCLUDED.id, name = EXCLUDED.name, password_hash = EXCLUDED.password_hash;(apply to both the Vet Erik and Admin Sara inserts)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/resources/data.sql` around lines 22 - 48, The Vet Erik and Admin Sara INSERT ... ON CONFLICT blocks currently only update id and name on email conflict; add password_hash to their DO UPDATE SET so the clause matches Anna's behavior (i.e., for the users inserts for 'Erik Veterinär' and 'Sara Admin' update id, name, password_hash = EXCLUDED.password_hash on conflict) to ensure rotated bcrypt hashes in the seed file replace existing rows' password hashes.
🟡 Minor comments (8)
frontend/src/components/Layout.jsx-29-135 (1)
29-135:⚠️ Potential issue | 🟡 MinorNo navigation/logout available on mobile.
The sidebar is
hidden md:flexand there is no alternative mobile header/drawer, so on screens< mdusers see only the main content with no way to navigate between views or log out. Consider adding a mobile top bar with a hamburger toggle that opens this sidebar.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/Layout.jsx` around lines 29 - 135, The mobile view lacks navigation and logout because the sidebar is hidden via "hidden md:flex" with no mobile alternative; add a mobile top bar (e.g., inside Layout.jsx) that contains a hamburger button which toggles a drawer or mobile menu rendering the same nav buttons (reuse getLinkStyle, onNavigate, userRole checks, and onLogout) and ensure the drawer mounts the same button elements (Home, my-pets/my-cases, vet/admin sections) so behavior and active styles match the desktop sidebar; implement local state (e.g., isMobileMenuOpen) and a toggle handler to control visibility and close on navigation.frontend/src/components/PetForm.jsx-111-118 (1)
111-118:⚠️ Potential issue | 🟡 MinorNo upper-bound validation on date of birth.
The
dateinput has nomax, so users can submit a future birth date. Addmax={new Date().toISOString().split('T')[0]}(or validate inhandleSubmit) to prevent invalid records from reaching the backend.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/PetForm.jsx` around lines 111 - 118, The date input for pet DOB in PetForm.jsx allows future dates; constrain it by adding a max attribute computed as today's date (e.g., max={new Date().toISOString().split('T')[0]}) on the input bound to formData.dob, and also add a defensive check in handleSubmit to reject or normalize submissions where formData.dob > today (return validation error or set error state) to ensure no future DOBs reach the backend; update references in the component (the <input> using value={formData.dob} and onChange that calls setFormData, and the handleSubmit function) accordingly.frontend/src/pages/AdminDashboard.jsx-4-69 (1)
4-69:⚠️ Potential issue | 🟡 MinorHardcoded/mock data — wire to backend before release.
Stats (
'12','1,402','89') and the clinic row are fully hardcoded, and the Redigera/Radera/+ Ny Klinik buttons are non-functional. This is fine as a scaffold, but should not ship as the admin dashboard.Want me to sketch the admin data-fetching hook and wire these cards/table to a backend endpoint?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/AdminDashboard.jsx` around lines 4 - 69, The current AdminDashboard.jsx uses hardcoded stats and a hardcoded clinic row (the stats array and the table tbody) and non-functional buttons; replace these with a data-fetching hook (e.g., create useAdminDashboard or useClinics) that calls your backend endpoints (e.g., GET /api/admin/stats and GET /api/admin/clinics) and returns {stats, clinics, loading, error} and actions (createClinic, updateClinic, deleteClinic); wire the component to useAdminDashboard: replace the hardcoded stats array with the hook-provided stats used in the stats.map render, replace the hardcoded table body with clinics.map rendering each clinic row, and replace the + Ny Klinik, Redigera and Radera buttons with handlers that call createClinic/updateClinic/deleteClinic and show optimistic UI or loading/error states using the hook's loading/error values. Ensure you reference and update the symbols stats, clinics, useAdminDashboard (or useClinics), createClinic, updateClinic, and deleteClinic in the component and add basic error/loading UI.src/main/java/org/example/vet1177/controller/PetController.java-34-45 (1)
34-45:⚠️ Potential issue | 🟡 MinorAvoid logging user email at INFO level.
Logging
currentUser.getEmail()leaks PII into logs and is inconsistent with the other endpoints in this controller, which log user IDs (UUIDs) instead. Consider loggingcurrentUser.getId()or dropping to DEBUG.🔒 Proposed fix
- log.info("GET /api/pets - Fetching all pets for user: {}", currentUser.getEmail()); + log.info("GET /api/pets - fetching pets for userId={}", currentUser.getId());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/vet1177/controller/PetController.java` around lines 34 - 45, The log statement in PetController.getAllMyPets currently logs PII via currentUser.getEmail(); change the log to avoid email exposure by using currentUser.getId() (UUID) or lowering to DEBUG level. Update the log.info call (log.info("GET /api/pets - Fetching all pets for user: {}", currentUser.getEmail())) to either log the ID (currentUser.getId()) at INFO or keep email out and use log.debug if you need the email, ensuring consistency with other endpoints that log user IDs.frontend/README.md-30-42 (1)
30-42:⚠️ Potential issue | 🟡 MinorFix stale file path references and minor typos.
- Lines 30 and 42 reference
src/services/api.js, but the actual file added in this PR issrc/services/api.jsx.- Line 18: "Se till at du är" → "Se till att du är" (missing
t).- Line 5 has a double space after
##(markdownlint MD019).📝 Proposed fixes
-## Kom igång +## Kom igång ... -Se till at du är i frontend-mappen när du kör detta: +Se till att du är i frontend-mappen när du kör detta: ... -API-anrop: Vi använder en Axios-interceptor (se src/services/api.js) som automatiskt bifogar din token i Authorization-headern på varje anrop: +API-anrop: Vi använder en Axios-interceptor (se src/services/api.jsx) som automatiskt bifogar din token i Authorization-headern på varje anrop: ... -**src/services/api.js:** All kommunikation med backend (Axios-instans). +**src/services/api.jsx:** All kommunikation med backend (Axios-instans).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/README.md` around lines 30 - 42, Update the README to point to the actual service file name (replace references to src/services/api.js with src/services/api.jsx), fix the Swedish typo "Se till at du är" to "Se till att du är", and remove the extra space after "##" (fix Markdown lint MD019); locate these strings in the provided README content and apply the three textual changes so the docs match the added file and correct typos.frontend/src/pages/Register.jsx-5-10 (1)
5-10:⚠️ Potential issue | 🟡 MinorRemove the hardcoded
rolefield from formData.The backend's
RegisterRequestDTO accepts onlyname,password. Therolefield is ignored by the backend, which hardcodesRole.OWNERserver-side. Removerole: 'OWNER'from the form state and stop sending it in the POST request to avoid unnecessary data transmission.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/Register.jsx` around lines 5 - 10, Remove the hardcoded role from the form state and stop sending it to the backend: remove "role: 'OWNER'" from the useState initial value for formData and update any state updates that reference formData.role, and update the submit logic (e.g., the function handling form submission such as handleSubmit or the fetch/POST body that uses formData) to only serialize name, email, and password when building the request payload; ensure setFormData and any input bindings still only touch name, email, and password fields.frontend/src/pages/CaseDetail.jsx-55-67 (1)
55-67:⚠️ Potential issue | 🟡 MinorPrevent duplicate comment submissions.
newMessageis only cleared after the request resolves, so repeated Enter/clicks can post the same comment multiple times.🐛 Proposed send lock
const [attachments, setAttachments] = useState([]); const [isUploading, setIsUploading] = useState(false); + const [isSending, setIsSending] = useState(false); const [loading, setLoading] = useState(true); ... const handleSendMessage = async () => { - if (!newMessage.trim()) return; + if (!newMessage.trim() || isSending) return; try { + setIsSending(true); const res = await commentService.createComment({ recordId: caseData.id, body: newMessage }); setTimeline(prev => [...prev, { ...res.data, type: 'COMMENT' }]); setNewMessage(''); } catch (error) { alert("Kunde inte skicka meddelande."); + } finally { + setIsSending(false); } }; ... - <button onClick={handleSendMessage} disabled={!newMessage.trim()} className="bg-vet-navy text-white px-6 py-2 rounded-lg hover:bg-vet-accent transition-all font-bold text-xs uppercase tracking-widest shadow-md disabled:opacity-30">Skicka</button> + <button onClick={handleSendMessage} disabled={!newMessage.trim() || isSending} className="bg-vet-navy text-white px-6 py-2 rounded-lg hover:bg-vet-accent transition-all font-bold text-xs uppercase tracking-widest shadow-md disabled:opacity-30">Skicka</button>Also applies to: 178-180
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/CaseDetail.jsx` around lines 55 - 67, handleSendMessage can submit duplicate comments because newMessage is only cleared after the async call; add a send lock (e.g., isSending state) to early-return if a send is in progress, set isSending = true immediately before calling commentService.createComment and reset it in finally, and also clear newMessage only once at a successful send; update UI controls (send button/Enter handler) to respect isSending and disable while true. Reference: handleSendMessage, newMessage, setNewMessage, setTimeline, commentService.createComment.frontend/src/pages/CreateCase.jsx-137-142 (1)
137-142:⚠️ Potential issue | 🟡 MinorUse a semantic file-upload control.
The clickable
<div>is not keyboard-accessible. Wrap the input with a<label>or use a button that handles Enter/Space.♿ Proposed accessible markup
- <div onClick={() => fileInputRef.current.click()} className="border-2 border-dashed border-slate-200 rounded-xl p-6 text-center hover:bg-slate-50 cursor-pointer relative"> + <label className="block border-2 border-dashed border-slate-200 rounded-xl p-6 text-center hover:bg-slate-50 cursor-pointer relative"> <input type="file" ref={fileInputRef} multiple onChange={handleFileChange} className="hidden" /> <p className="text-sm text-slate-500">{files.length > 0 ? `${files.length} filer valda` : 'Klicka för att ladda upp'}</p> - </div> + </label>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/CreateCase.jsx` around lines 137 - 142, The current clickable <div> for attachments isn't keyboard-accessible; update the markup so the file input is triggered by a semantic control: either wrap the hidden <input type="file" ref={fileInputRef} ...> and its contents inside a <label htmlFor="attachments"> (give the input an id like "attachments") or replace the div with a <button> that calls fileInputRef.current.click() and handles Enter/Space, while keeping onChange={handleFileChange} and the files state display intact; ensure you remove the onClick from the non-semantic div and preserve classes, aria-label/role if needed so file selection via keyboard and screen readers works.
🧹 Nitpick comments (5)
src/main/java/org/example/vet1177/entities/ActivityLog.java (1)
28-51: Minor: add a getter forentityIdand guard against a nullmedicalRecord.Two small points on the new
entityId:
- There is no
getEntityId()accessor, so the column cannot be read back from the entity (and won't surface in any future response DTO). Add one for symmetry with the other fields.this.entityId = medicalRecord.getId()in the constructor will NPE if a caller ever passesnull.ActivityLogService.log(...)does guard against this today, but the constructor is public and should be defensive — or at minimum documented. Also, line 51 reassignsentityTypeto the same literal used as a field initializer on line 26, which is redundant.🔧 Suggested tweaks
public ActivityLog(ActivityType action, String description, User performedBy, MedicalRecord medicalRecord) { + if (medicalRecord == null) { + throw new IllegalArgumentException("medicalRecord must not be null"); + } this.action = action; this.description = description; this.performedBy = performedBy; this.medicalRecord = medicalRecord; this.entityId = medicalRecord.getId(); - this.entityType = "MEDICAL_RECORD"; } @@ public MedicalRecord getMedicalRecord() { return medicalRecord; } + + public UUID getEntityId() { return entityId; } + + public String getEntityType() { return entityType; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/vet1177/entities/ActivityLog.java` around lines 28 - 51, Add a public getter for the entityId field (getEntityId) for symmetry and persistence access, and make the ActivityLog constructor defensive: in the ActivityLog(ActivityType action, String description, User performedBy, MedicalRecord medicalRecord) constructor check for a null medicalRecord before calling medicalRecord.getId() (either throw a clear IllegalArgumentException or assign entityId to null based on desired behavior) to avoid an NPE; also remove the redundant reassignment of entityType in that constructor since it’s already initialized on the field.frontend/eslint.config.js (1)
16-23: Nit: conflictingecmaVersionvalues.
languageOptions.ecmaVersionis set to2020whileparserOptions.ecmaVersionis'latest'.parserOptionswill win in practice, but the inconsistency is confusing — unify both to'latest'(or the specific year you target).🔧 Suggested fix
languageOptions: { - ecmaVersion: 2020, + ecmaVersion: 'latest', globals: globals.browser,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/eslint.config.js` around lines 16 - 23, Unify the conflicting ECMAScript version settings by setting both languageOptions.ecmaVersion and parserOptions.ecmaVersion to the same value (e.g., 'latest' or a specific year like 2020); update the languageOptions.ecmaVersion to match parserOptions.ecmaVersion (or change parserOptions.ecmaVersion to match languageOptions) so the configuration is consistent and unambiguous.src/main/java/org/example/vet1177/services/PetService.java (1)
3-26: Prefer Spring's@Transactionaloverjakarta.transaction.Transactional, and mark read pathsreadOnly.Applying
jakarta.transaction.Transactionalat the class level works, but it has two downsides vs.org.springframework.transaction.annotation.Transactionalin a Spring Boot app:
- It does not support
readOnly = true, so read-only methods likegetPetById/getPetsByOwnernow run in a full read-write transaction, losing the JPA/Hibernate read-only optimizations and flush skipping.- Its rollback semantics differ from Spring's (rolls back on any unchecked exception, but with fewer configuration hooks such as
rollbackFor/noRollbackFor).Recommend switching the import and annotating read methods explicitly:
🔧 Suggested change
-import jakarta.transaction.Transactional; +import org.springframework.transaction.annotation.Transactional; @@ `@Service` -@Transactional +@Transactional public class PetService { @@ - public Pet getPetById(UUID petId, User currentUser) { + `@Transactional`(readOnly = true) + public Pet getPetById(UUID petId, User currentUser) { @@ - public List<Pet> getPetsByOwner(UUID currentUserId, UUID ownerId) { + `@Transactional`(readOnly = true) + public List<Pet> getPetsByOwner(UUID currentUserId, UUID ownerId) {The existing
@TransactionalondeletePet(line 135) should also be switched to the Spring annotation for consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/vet1177/services/PetService.java` around lines 3 - 26, Replace the class-level jakarta.transaction.Transactional with org.springframework.transaction.annotation.Transactional and update transaction semantics: change the import to org.springframework.transaction.annotation.Transactional on PetService, annotate read methods (getPetById, getPetsByOwner) with `@Transactional`(readOnly = true) instead of relying on the class-level default, and ensure mutating methods (including deletePet) use the Spring `@Transactional` (keep class-level annotation optional) so they have Spring rollback/rollbackFor capabilities and consistent behavior across the service.frontend/src/components/Layout.jsx (1)
60-60: Role substring matching is brittle.
userRole?.includes('OWNER') || userRole?.includes('VET')works today but silently matches any future role containing those substrings (e.g.,ROLE_VET_ADMIN,ROLE_PET_OWNER_LEGACY). Prefer exact comparisons consistent with lines 85 and 96.♻️ Proposed refactor
- {(userRole?.includes('OWNER') || userRole?.includes('VET')) && ( + {(userRole === 'ROLE_OWNER' || userRole === 'ROLE_VET') && (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/Layout.jsx` at line 60, The conditional using userRole?.includes('OWNER') || userRole?.includes('VET') is brittle; change it to exact equality checks consistent with other conditionals (e.g., compare userRole === 'OWNER' || userRole === 'VET' or if userRole is an array, use explicit array membership checks) so roles like 'ROLE_VET_ADMIN' won't match; update the JSX conditional that references userRole in Layout.jsx to use exact comparisons (matching the approach used at the other checks around the component).frontend/src/components/PetForm.jsx (1)
27-38: Replacealert()with an inline error, and drop debugconsole.logof the payload.
Login.jsxandRegister.jsxalready render errors inline via anerrorstate — this form should do the same for consistency and better UX. Also,console.log("Skickar till backend:", petRequest)is debug noise that should be removed (or gated) before shipping.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/PetForm.jsx` around lines 27 - 38, Remove the debug console.log of the payload and replace the alert-based error handling with an inline error state similar to Login.jsx/Register.jsx: add an error state variable (e.g., error and setError) in the PetForm component, clear it on submit (before calling petService.createPet), remove console.log("Skickar till backend:", petRequest), set error via setError(error.message || a friendly message) inside the catch block instead of calling alert, and render that error state in the component JSX (near the form submit button or top of the form) so users see the validation/server error inline; ensure setIsSubmitting(false) remains in finally and call onSave() only on successful createPet.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/package.json`:
- Around line 12-29: The package.json is missing the Tailwind/PostCSS toolchain
so Vite will fail processing the Tailwind directives; update the
"devDependencies" block in frontend's package.json to add "tailwindcss",
"postcss", and "autoprefixer" (compatible with Tailwind v3), then reinstall dev
deps so the existing frontend/tailwind.config.js and frontend/postcss.config.js
and the `@tailwind` directives in src/index.css can be processed correctly.
In `@frontend/src/App.jsx`:
- Around line 125-159: The declaration const filteredRecords inside the case
'pet-detail' shares lexical scope across switch clauses and violates the
noSwitchDeclarations rule; fix it by wrapping the entire case body in its own
block (add { ... } after case 'pet-detail':) so filteredRecords and any other
locals (e.g., the async onCaseClick handler that calls
medicalRecordService.getRecordById, setSelectedRecord, setCurrentView,
setSelectedPet) are scoped to that block and the return <PetDetail ... />
remains inside the block.
- Around line 97-101: The switch over currentView in App.jsx is missing a
'vet-dashboard' case and falls through to the default which renders
OwnerDashboard with viewMode="vet-dashboard" (resulting in a blank page). Add an
explicit case for 'vet-dashboard' in the switch (before the default) and either
render the proper VetDashboard component (or redirect/set currentView to a
supported view) so vets are routed to a valid view; also consider updating
Layout.jsx to hide the vet nav item until VetDashboard is implemented. Ensure
you reference the currentView switch, the OwnerDashboard render that receives
viewMode="vet-dashboard", and Layout.jsx when making the change.
In `@frontend/src/pages/CreateCase.jsx`:
- Around line 39-50: The form currently allows submitting when selectedPet is
null, sending petId: null; update the handleSubmit function to guard against a
missing pet by checking selectedPet (the same place you check selectedClinic)
and aborting with an alert/return if it's falsy; ensure you don't set
isSubmitting or call the API when selectedPet is missing so the submission state
isn't left true (adjust setIsSubmitting flow in handleSubmit accordingly).
- Around line 45-65: The current flow always calls
medicalRecordService.createRecord even when existingCase exists and creates
duplicate records if attachments fail; change the logic to call
medicalRecordService.updateRecord when existingCase is present (use
existingCase.id) and call createRecord only when creating a new case (no
existingCase). Additionally, ensure attachments are uploaded inside a try/catch
and only call onSave after all uploads succeed; if createRecord succeeded but
any attachment upload fails, roll back by calling a cleanup method (e.g.,
medicalRecordService.deleteRecord(recordId)) or otherwise handle cleanup so
retrying the form does not create duplicates; reference functions/variables:
createRecord, updateRecord, deleteRecord (or cleanup), attachmentService.upload,
existingCase, recordId, files, onSave.
In `@frontend/src/pages/Login.jsx`:
- Around line 16-21: The token extraction in the login flow is fragile and may
store "[object Object]"; update the login handling where you call
authService.login (the response variable in Login.jsx) to validate and normalize
the token before calling localStorage.setItem('token', ...). Specifically,
inspect response.data and prefer known string fields (e.g., response.data.token,
response.data.accessToken, response.data.jwt) and only use a value if typeof
value === 'string'; if response.data is a string use it, otherwise throw or
handle an error path (do not stringify objects). Ensure this logic is applied
right before localStorage.setItem and keep onLoginSuccess() only called after a
valid string token is stored.
In `@frontend/src/pages/OwnerDashboard.jsx`:
- Around line 51-54: The clickable card currently rendered as a <div> with
key={record.id} and onClick={() => onCaseClick(record)} should be a semantic
<button type="button"> to restore keyboard navigation and accessibility; replace
the <div> wrapper with a <button type="button"> keeping the onClick handler
(onCaseClick), key, and all className styles but add the utility classes "w-full
text-left" so the visual layout doesn't change, and retain any hover/transition
classes. Apply the same change to the other clickable card instance (the similar
block that uses onPetClick/onCaseClick around record.id at the 95-98 region).
In `@frontend/src/pages/PetDetail.jsx`:
- Around line 6-7: Remove the debug console.log in PetDetail.jsx that prints
sensitive objects (console.log("PetDetail laddas med:", { pet, petRecords,
STATUS_MAP })); either delete the statement outright or wrap it behind a
development-only guard (e.g., check process.env.NODE_ENV === 'development' or a
DEBUG flag) so that pet, petRecords and STATUS_MAP are never logged in
production; update the PetDetail component accordingly to eliminate the privacy
risk.
- Around line 82-85: The journal card is a clickable <div> (key={record.id})
using onCaseClick(record) which is not keyboard-accessible; replace or update
that element so it behaves as a semantic interactive control: either render a
<button> or an element with role="button", tabIndex={0}, an accessible label
(aria-label or aria-labelledby), and handle Enter/Space in an onKeyDown that
calls onCaseClick(record). Ensure existing className, onClick, and key usage
remain, and keep focus styles for keyboard users.
In `@frontend/src/pages/Register.jsx`:
- Around line 19-33: The current Register.jsx token handling uses
`response.data.token || response.data` which can store an object as a token and
silently do nothing when no token is returned; update the registration handler
(the async submit function that calls `api.post('/auth/register', formData)`) to
explicitly check for a string token (e.g. `response.data.token` exists and
typeof is "string"), only call `localStorage.setItem('token', token)` and
`window.location.reload()` when a valid string token is present, and otherwise
handle the "no token" path by either redirecting the user to the login page or
setting an explicit error via `setError(...)` so the user gets feedback; ensure
`setLoading(false)` still runs in finally and avoid storing non-string
`response.data` values as the token.
In `@frontend/src/services/api.jsx`:
- Around line 30-36: The response interceptor currently removes the token and
reloads the app on any 401 (api.interceptors.response.use), which also fires for
failed logins and prevents Login.jsx from showing errors; change the interceptor
to only treat 401 as session-expiry for authenticated requests (e.g. skip if
error.config?.url matches auth endpoints like '/login' or '/register' or when
there was no token in localStorage). When you detect a real session expiry,
remove the token (localStorage.removeItem('token')) and dispatch a logout event
(new CustomEvent('auth:logout')) instead of calling window.location.reload();
leave other 401s to reject normally so Login.jsx can handle them. Update App.jsx
to listen for 'auth:logout' and call handleLogout() to complete UI teardown and
navigation.
- Around line 4-8: The base URL is hardcoded in the axios instance (const api =
axios.create({...})) which will break non-local and HTTPS deployments; change
the baseURL to read from the Vite env var (import.meta.env.VITE_API_BASE_URL)
with a safe fallback (e.g., 'http://localhost:8080/api') and use that value when
creating the axios instance in frontend/src/services/api.jsx so deployments can
override the URL via VITE_API_BASE_URL.
In `@frontend/tailwind.config.js`:
- Around line 1-18: Add the missing tailwindcss package to devDependencies in
package.json so PostCSS and the `@tailwind` directives work: open package.json and
under "devDependencies" add "tailwindcss" with an appropriate version (or run
the installer), then run the install command to update lockfile; verify
postcss.config.js, tailwind.config.js, and src/index.css (the `@tailwind`
directives) remain unchanged and that the build succeeds.
In `@package.json`:
- Around line 1-7: Move the devDependencies "autoprefixer", "postcss", and
"tailwindcss" out of the root package.json devDependencies and add them to
frontend/package.json devDependencies (preserving the versions), since
frontend/tailwind.config.js and frontend/postcss.config.js expect those
packages; alternatively, if you prefer a monorepo workspace, configure the root
package.json workspaces to include the frontend and adjust root build scripts so
npm install/build from frontend will resolve those packages—ensure the three
package names ("autoprefixer", "postcss", "tailwindcss") are declared in
frontend/package.json or made available via workspace config.
In `@src/main/java/org/example/vet1177/controller/MedicalRecordController.java`:
- Line 23: Remove the redundant controller-level CORS annotation: delete the
`@CrossOrigin`(origins = "http://localhost:5173") annotation from the
MedicalRecordController class so it no longer overrides or narrows the
centralized policy; rely on the existing
SecurityConfig.corsConfigurationSource() which registers allowed origins for
/api/** (including both http://localhost:5173 and http://localhost:3000).
---
Outside diff comments:
In `@src/main/resources/data.sql`:
- Around line 22-48: The Vet Erik and Admin Sara INSERT ... ON CONFLICT blocks
currently only update id and name on email conflict; add password_hash to their
DO UPDATE SET so the clause matches Anna's behavior (i.e., for the users inserts
for 'Erik Veterinär' and 'Sara Admin' update id, name, password_hash =
EXCLUDED.password_hash on conflict) to ensure rotated bcrypt hashes in the seed
file replace existing rows' password hashes.
---
Minor comments:
In `@frontend/README.md`:
- Around line 30-42: Update the README to point to the actual service file name
(replace references to src/services/api.js with src/services/api.jsx), fix the
Swedish typo "Se till at du är" to "Se till att du är", and remove the extra
space after "##" (fix Markdown lint MD019); locate these strings in the provided
README content and apply the three textual changes so the docs match the added
file and correct typos.
In `@frontend/src/components/Layout.jsx`:
- Around line 29-135: The mobile view lacks navigation and logout because the
sidebar is hidden via "hidden md:flex" with no mobile alternative; add a mobile
top bar (e.g., inside Layout.jsx) that contains a hamburger button which toggles
a drawer or mobile menu rendering the same nav buttons (reuse getLinkStyle,
onNavigate, userRole checks, and onLogout) and ensure the drawer mounts the same
button elements (Home, my-pets/my-cases, vet/admin sections) so behavior and
active styles match the desktop sidebar; implement local state (e.g.,
isMobileMenuOpen) and a toggle handler to control visibility and close on
navigation.
In `@frontend/src/components/PetForm.jsx`:
- Around line 111-118: The date input for pet DOB in PetForm.jsx allows future
dates; constrain it by adding a max attribute computed as today's date (e.g.,
max={new Date().toISOString().split('T')[0]}) on the input bound to
formData.dob, and also add a defensive check in handleSubmit to reject or
normalize submissions where formData.dob > today (return validation error or set
error state) to ensure no future DOBs reach the backend; update references in
the component (the <input> using value={formData.dob} and onChange that calls
setFormData, and the handleSubmit function) accordingly.
In `@frontend/src/pages/AdminDashboard.jsx`:
- Around line 4-69: The current AdminDashboard.jsx uses hardcoded stats and a
hardcoded clinic row (the stats array and the table tbody) and non-functional
buttons; replace these with a data-fetching hook (e.g., create useAdminDashboard
or useClinics) that calls your backend endpoints (e.g., GET /api/admin/stats and
GET /api/admin/clinics) and returns {stats, clinics, loading, error} and actions
(createClinic, updateClinic, deleteClinic); wire the component to
useAdminDashboard: replace the hardcoded stats array with the hook-provided
stats used in the stats.map render, replace the hardcoded table body with
clinics.map rendering each clinic row, and replace the + Ny Klinik, Redigera and
Radera buttons with handlers that call createClinic/updateClinic/deleteClinic
and show optimistic UI or loading/error states using the hook's loading/error
values. Ensure you reference and update the symbols stats, clinics,
useAdminDashboard (or useClinics), createClinic, updateClinic, and deleteClinic
in the component and add basic error/loading UI.
In `@frontend/src/pages/CaseDetail.jsx`:
- Around line 55-67: handleSendMessage can submit duplicate comments because
newMessage is only cleared after the async call; add a send lock (e.g.,
isSending state) to early-return if a send is in progress, set isSending = true
immediately before calling commentService.createComment and reset it in finally,
and also clear newMessage only once at a successful send; update UI controls
(send button/Enter handler) to respect isSending and disable while true.
Reference: handleSendMessage, newMessage, setNewMessage, setTimeline,
commentService.createComment.
In `@frontend/src/pages/CreateCase.jsx`:
- Around line 137-142: The current clickable <div> for attachments isn't
keyboard-accessible; update the markup so the file input is triggered by a
semantic control: either wrap the hidden <input type="file" ref={fileInputRef}
...> and its contents inside a <label htmlFor="attachments"> (give the input an
id like "attachments") or replace the div with a <button> that calls
fileInputRef.current.click() and handles Enter/Space, while keeping
onChange={handleFileChange} and the files state display intact; ensure you
remove the onClick from the non-semantic div and preserve classes,
aria-label/role if needed so file selection via keyboard and screen readers
works.
In `@frontend/src/pages/Register.jsx`:
- Around line 5-10: Remove the hardcoded role from the form state and stop
sending it to the backend: remove "role: 'OWNER'" from the useState initial
value for formData and update any state updates that reference formData.role,
and update the submit logic (e.g., the function handling form submission such as
handleSubmit or the fetch/POST body that uses formData) to only serialize name,
email, and password when building the request payload; ensure setFormData and
any input bindings still only touch name, email, and password fields.
In `@src/main/java/org/example/vet1177/controller/PetController.java`:
- Around line 34-45: The log statement in PetController.getAllMyPets currently
logs PII via currentUser.getEmail(); change the log to avoid email exposure by
using currentUser.getId() (UUID) or lowering to DEBUG level. Update the log.info
call (log.info("GET /api/pets - Fetching all pets for user: {}",
currentUser.getEmail())) to either log the ID (currentUser.getId()) at INFO or
keep email out and use log.debug if you need the email, ensuring consistency
with other endpoints that log user IDs.
---
Nitpick comments:
In `@frontend/eslint.config.js`:
- Around line 16-23: Unify the conflicting ECMAScript version settings by
setting both languageOptions.ecmaVersion and parserOptions.ecmaVersion to the
same value (e.g., 'latest' or a specific year like 2020); update the
languageOptions.ecmaVersion to match parserOptions.ecmaVersion (or change
parserOptions.ecmaVersion to match languageOptions) so the configuration is
consistent and unambiguous.
In `@frontend/src/components/Layout.jsx`:
- Line 60: The conditional using userRole?.includes('OWNER') ||
userRole?.includes('VET') is brittle; change it to exact equality checks
consistent with other conditionals (e.g., compare userRole === 'OWNER' ||
userRole === 'VET' or if userRole is an array, use explicit array membership
checks) so roles like 'ROLE_VET_ADMIN' won't match; update the JSX conditional
that references userRole in Layout.jsx to use exact comparisons (matching the
approach used at the other checks around the component).
In `@frontend/src/components/PetForm.jsx`:
- Around line 27-38: Remove the debug console.log of the payload and replace the
alert-based error handling with an inline error state similar to
Login.jsx/Register.jsx: add an error state variable (e.g., error and setError)
in the PetForm component, clear it on submit (before calling
petService.createPet), remove console.log("Skickar till backend:", petRequest),
set error via setError(error.message || a friendly message) inside the catch
block instead of calling alert, and render that error state in the component JSX
(near the form submit button or top of the form) so users see the
validation/server error inline; ensure setIsSubmitting(false) remains in finally
and call onSave() only on successful createPet.
In `@src/main/java/org/example/vet1177/entities/ActivityLog.java`:
- Around line 28-51: Add a public getter for the entityId field (getEntityId)
for symmetry and persistence access, and make the ActivityLog constructor
defensive: in the ActivityLog(ActivityType action, String description, User
performedBy, MedicalRecord medicalRecord) constructor check for a null
medicalRecord before calling medicalRecord.getId() (either throw a clear
IllegalArgumentException or assign entityId to null based on desired behavior)
to avoid an NPE; also remove the redundant reassignment of entityType in that
constructor since it’s already initialized on the field.
In `@src/main/java/org/example/vet1177/services/PetService.java`:
- Around line 3-26: Replace the class-level jakarta.transaction.Transactional
with org.springframework.transaction.annotation.Transactional and update
transaction semantics: change the import to
org.springframework.transaction.annotation.Transactional on PetService, annotate
read methods (getPetById, getPetsByOwner) with `@Transactional`(readOnly = true)
instead of relying on the class-level default, and ensure mutating methods
(including deletePet) use the Spring `@Transactional` (keep class-level annotation
optional) so they have Spring rollback/rollbackFor capabilities and consistent
behavior across the service.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d22c25ec-5dce-431f-84db-5ea021bd45a0
⛔ Files ignored due to path filters (7)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/public/favicon.svgis excluded by!**/*.svgfrontend/public/icons.svgis excluded by!**/*.svgfrontend/src/assets/hero.pngis excluded by!**/*.pngfrontend/src/assets/react.svgis excluded by!**/*.svgfrontend/src/assets/vite.svgis excluded by!**/*.svgpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (33)
.gitignorefrontend/.gitignorefrontend/README.mdfrontend/eslint.config.jsfrontend/index.htmlfrontend/package.jsonfrontend/postcss.config.jsfrontend/src/App.cssfrontend/src/App.jsxfrontend/src/components/Layout.jsxfrontend/src/components/PetForm.jsxfrontend/src/components/PetList.jsxfrontend/src/index.cssfrontend/src/main.jsxfrontend/src/pages/AdminDashboard.jsxfrontend/src/pages/CaseDetail.jsxfrontend/src/pages/CreateCase.jsxfrontend/src/pages/Login.jsxfrontend/src/pages/OwnerDashboard.jsxfrontend/src/pages/PetDetail.jsxfrontend/src/pages/Register.jsxfrontend/src/pages/VetDashboard.jsxfrontend/src/services/api.jsxfrontend/src/utils/statusHelper.jsfrontend/tailwind.config.jsfrontend/vite.config.jspackage.jsonsrc/main/java/org/example/vet1177/controller/MedicalRecordController.javasrc/main/java/org/example/vet1177/controller/PetController.javasrc/main/java/org/example/vet1177/entities/ActivityLog.javasrc/main/java/org/example/vet1177/security/JwtService.javasrc/main/java/org/example/vet1177/services/PetService.javasrc/main/resources/data.sql
…ents in CreateCase
…d expired sessions
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (7)
frontend/src/pages/OwnerDashboard.jsx (1)
116-122: Addtype="button"to the "Registrera nytt djur" action.For consistency with the other two clickable cards on lines 51 and 96 and to avoid accidental form submission if this block is ever nested inside a
<form>, explicitly declare the type.♻️ Proposed tweak
<button + type="button" onClick={onAddPet} className={`flex flex-col items-center justify-center p-6 border-2 border-dashed border-slate-200 rounded-xl hover:border-blue-400 hover:bg-blue-50 transition group ${isPetsView ? 'h-full' : 'w-full mt-4'}`} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/OwnerDashboard.jsx` around lines 116 - 122, The Add Pet button lacks an explicit type, which can cause accidental form submissions; update the <button> rendered in OwnerDashboard.jsx that uses onClick={onAddPet} (the "Registrera nytt djur" button) to include type="button" so it behaves like the other clickable cards and won't submit a surrounding form.frontend/src/services/api.jsx (1)
3-10: Base URL fallback still points athttp://localhost:8080.Past feedback has been applied by reading
VITE_API_BASE_URL. The remaining nit: the fallback is stillhttp://localhost:8080/api, so a production build without an env file will ship a URL that breaks on HTTPS origins (mixed-content blocking) and for any user not running the backend locally. A relative'/api'fallback (proxied/rewritten by the hosting layer) is safer for deployments.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/services/api.jsx` around lines 3 - 10, The API_BASE_URL fallback currently uses an absolute URL causing mixed-content and wrong-host issues; change the fallback in the API_BASE_URL assignment (where import.meta.env.VITE_API_BASE_URL is read) from 'http://localhost:8080/api' to a relative '/api' so the axios instance (api) uses a proxied/rewrite-safe base path in production and on HTTPS origins.frontend/src/pages/PetDetail.jsx (1)
97-99: Minor: empty date placeholder inconsistent with OwnerDashboard.When
record.createdAtis missing, this renders an empty string, whereasOwnerDashboard.jsx(line 70) shows'Datum saknas'. Consider aligning for UX consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/PetDetail.jsx` around lines 97 - 99, The date display in PetDetail.jsx currently renders an empty string when record.createdAt is falsy; update the conditional in the JSX that renders new Date(record.createdAt).toLocaleDateString('sv-SE') (the block referencing record.createdAt) to instead render the same placeholder string used in OwnerDashboard ("Datum saknas") when createdAt is missing so the UX is consistent across views.frontend/src/App.jsx (2)
62-64:fetchInitialDatais a redundant wrapper.It only awaits
fetchData()with no additional logic. Consider inliningfetchData()at the call site on line 37 and removing this wrapper.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.jsx` around lines 62 - 64, Remove the redundant fetchInitialData wrapper and replace its call sites with fetchData() directly: delete the fetchInitialData function (which only awaits fetchData()) and update wherever fetchInitialData is invoked to call fetchData() instead, ensuring any imports/exports or references to fetchInitialData are also removed or updated to fetchData.
204-212: Swallowed error inonCaseClick; also differs from the equivalentPetDetailpath.The
pet-detailversion (line 143) logs the error silently while this branch alerts but doesn't log. Pick one behavior (ideally: log + show a non-blocking banner) and apply it consistently at both call sites to keep failure modes debuggable and uniform.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.jsx` around lines 204 - 212, The onCaseClick handler swallows errors by alerting without logging and differs from the pet-detail path; replace the alert with the same error-handling used by the pet-detail flow: call the shared non-blocking banner/displayError utility (or set an error state like setBanner/setError) and log the error (e.g., console.error or logger.error) when medicalRecordService.getRecordById fails, keeping setSelectedRecord and setCurrentView behavior unchanged; ensure the same utility is used by both the case-detail onCaseClick and the pet-detail path so failures are logged and surfaced consistently.frontend/src/pages/Register.jsx (2)
19-49: Token extraction hardened — LGTM, plus a small consistency nit.The robust
typeofchecks and the fallback toonSwitchToLogin()address the earlier silent-failure concern. One minor nit: this file imports rawapiand callsapi.post('/auth/register', ...)directly, whereasapi.jsxalready exportsauthService.register(userData). Prefer the service wrapper so the endpoint path lives in one place.♻️ Optional refactor
-import api from '../services/api'; +import { authService } from '../services/api'; ... - const response = await api.post('/auth/register', formData); + const response = await authService.register(formData);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/Register.jsx` around lines 19 - 49, Replace the direct call to api.post('/auth/register', formData) with the centralized service wrapper authService.register(formData) to keep the endpoint path single-sourced; update the import in Register.jsx to pull authService (or register) from api.jsx/authService and call authService.register instead of api.post, leaving the surrounding token-extraction, error handling (setError) and loading logic (setLoading) and post-success behavior (localStorage.setItem, onSwitchToLogin) unchanged so the component behavior stays identical while removing duplicated endpoint strings.
36-40: Replacealert()with in-form messaging.Using a blocking
alert()for the "no token returned, please log in" path is jarring compared to the rest of the form that uses the inline error banner. Consider settingerror(or a dedicatedinfostate) and lettingonSwitchToLogin()occur after acknowledgment, or display the banner on the Login screen via a prop/flag.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/Register.jsx` around lines 36 - 40, Replace the blocking alert in the Register.jsx success path with in-form non-blocking messaging: instead of calling alert(...), set a component state (e.g., setError or introduce setInfo) to show the same message as an inline banner inside the Register form, and then call onSwitchToLogin() only after the user acknowledges the banner (or pass an info flag/message via onSwitchToLogin(infoMessage) so the Login component can display the inline banner). Update the rendering logic to display the new info state in the same banner component used for errors and ensure onSwitchToLogin() still performs the navigation once the message is acknowledged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/App.jsx`:
- Around line 218-228: The login/register UI is rendering before auth state is
resolved because the component checks only currentUser; change the conditional
to gate rendering on the auth-loading flag so the auth screen is shown only
after loading completes (e.g., use the existing loading or an authChecked flag):
update the component render guard to return a placeholder (null/spinner) while
loading is true and only render the Login/Register block when loading is false
and currentUser is null; adjust references to isRegistering/setIsRegistering and
Login/Register usage so behavior is unchanged once authenticated.
- Around line 87-98: handleLogout currently clears auth token and currentUser
but leaves pet/record state which causes stale data to show; update handleLogout
to also clear myPets and myRecords and reset selectedPet and selectedRecord by
calling setMyPets([]), setMyRecords([]), setSelectedPet(null) and
setSelectedRecord(null) (same state setters used elsewhere) so no previous-user
data remains before fetchData runs.
- Around line 25-45: The effect inside useEffect decodes the token and always
calls fetchInitialData(), causing admins to trigger endpoints they shouldn't;
update the block after decoding (in the same useEffect where jwtDecode,
setCurrentUser, handleLogout are used) to only call fetchInitialData() when
decoded.role is 'ROLE_OWNER' or 'ROLE_VET', and call setLoading(false) for other
roles instead; keep existing error handling (console.error and handleLogout) and
the existing else branch that calls setLoading(false) when no token is present.
In `@frontend/src/services/api.jsx`:
- Around line 86-94: The upload call currently forces a Content-Type header that
breaks FormData boundary handling and the download call doesn't request binary;
remove the custom headers from the upload definition (the upload function
exported as upload) so axios can set multipart/form-data with its boundary, and
update the download function (download) to call api.get with { responseType:
'blob' } so binary file responses are preserved for URL.createObjectURL usage.
---
Nitpick comments:
In `@frontend/src/App.jsx`:
- Around line 62-64: Remove the redundant fetchInitialData wrapper and replace
its call sites with fetchData() directly: delete the fetchInitialData function
(which only awaits fetchData()) and update wherever fetchInitialData is invoked
to call fetchData() instead, ensuring any imports/exports or references to
fetchInitialData are also removed or updated to fetchData.
- Around line 204-212: The onCaseClick handler swallows errors by alerting
without logging and differs from the pet-detail path; replace the alert with the
same error-handling used by the pet-detail flow: call the shared non-blocking
banner/displayError utility (or set an error state like setBanner/setError) and
log the error (e.g., console.error or logger.error) when
medicalRecordService.getRecordById fails, keeping setSelectedRecord and
setCurrentView behavior unchanged; ensure the same utility is used by both the
case-detail onCaseClick and the pet-detail path so failures are logged and
surfaced consistently.
In `@frontend/src/pages/OwnerDashboard.jsx`:
- Around line 116-122: The Add Pet button lacks an explicit type, which can
cause accidental form submissions; update the <button> rendered in
OwnerDashboard.jsx that uses onClick={onAddPet} (the "Registrera nytt djur"
button) to include type="button" so it behaves like the other clickable cards
and won't submit a surrounding form.
In `@frontend/src/pages/PetDetail.jsx`:
- Around line 97-99: The date display in PetDetail.jsx currently renders an
empty string when record.createdAt is falsy; update the conditional in the JSX
that renders new Date(record.createdAt).toLocaleDateString('sv-SE') (the block
referencing record.createdAt) to instead render the same placeholder string used
in OwnerDashboard ("Datum saknas") when createdAt is missing so the UX is
consistent across views.
In `@frontend/src/pages/Register.jsx`:
- Around line 19-49: Replace the direct call to api.post('/auth/register',
formData) with the centralized service wrapper authService.register(formData) to
keep the endpoint path single-sourced; update the import in Register.jsx to pull
authService (or register) from api.jsx/authService and call authService.register
instead of api.post, leaving the surrounding token-extraction, error handling
(setError) and loading logic (setLoading) and post-success behavior
(localStorage.setItem, onSwitchToLogin) unchanged so the component behavior
stays identical while removing duplicated endpoint strings.
- Around line 36-40: Replace the blocking alert in the Register.jsx success path
with in-form non-blocking messaging: instead of calling alert(...), set a
component state (e.g., setError or introduce setInfo) to show the same message
as an inline banner inside the Register form, and then call onSwitchToLogin()
only after the user acknowledges the banner (or pass an info flag/message via
onSwitchToLogin(infoMessage) so the Login component can display the inline
banner). Update the rendering logic to display the new info state in the same
banner component used for errors and ensure onSwitchToLogin() still performs the
navigation once the message is acknowledged.
In `@frontend/src/services/api.jsx`:
- Around line 3-10: The API_BASE_URL fallback currently uses an absolute URL
causing mixed-content and wrong-host issues; change the fallback in the
API_BASE_URL assignment (where import.meta.env.VITE_API_BASE_URL is read) from
'http://localhost:8080/api' to a relative '/api' so the axios instance (api)
uses a proxied/rewrite-safe base path in production and on HTTPS origins.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6f6904b0-0d13-4b69-9d98-5499f7863881
⛔ Files ignored due to path filters (2)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (10)
frontend/.gitignorefrontend/package.jsonfrontend/src/App.jsxfrontend/src/pages/CreateCase.jsxfrontend/src/pages/Login.jsxfrontend/src/pages/OwnerDashboard.jsxfrontend/src/pages/PetDetail.jsxfrontend/src/pages/Register.jsxfrontend/src/services/api.jsxpackage.json
✅ Files skipped from review due to trivial changes (3)
- frontend/.gitignore
- package.json
- frontend/package.json
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/pages/Login.jsx
- frontend/src/pages/CreateCase.jsx
There was a problem hiding this comment.
Actionable comments posted: 12
♻️ Duplicate comments (3)
frontend/src/services/api.jsx (1)
92-97:⚠️ Potential issue | 🟠 MajorLet Axios set multipart boundaries and request downloads as blobs.
The upload overrides the
FormDatacontent type, which can drop the required multipart boundary. The download path should request binary data explicitly.🐛 Proposed fix
export const attachmentService = { - upload: (recordId, formData) => api.post(`/attachments/record/${recordId}`, formData, { - headers: { 'Content-Type': 'multipart/form-data' }, - }), + upload: (recordId, formData) => api.post(`/attachments/record/${recordId}`, formData), getByRecord: (recordId) => api.get(`/attachments/record/${recordId}`), - download: (id) => api.get(`/attachments/${id}/download`), + download: (id) => api.get(`/attachments/${id}/download`, { responseType: 'blob' }), delete: (id) => api.delete(`/attachments/${id}`), };Axios 1.x FormData upload should Content-Type multipart/form-data boundary be set manually, and how to download binary files with responseType blob?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/services/api.jsx` around lines 92 - 97, For attachmentService: stop overriding the FormData Content-Type in attachmentService.upload so Axios can set the multipart boundary automatically (remove the headers: {'Content-Type': 'multipart/form-data'} override) and for attachmentService.download request binary data by adding responseType: 'blob' to the api.get call; update the upload and download methods (attachmentService.upload and attachmentService.download) accordingly.frontend/src/App.jsx (2)
249-259:⚠️ Potential issue | 🟠 MajorGate the login/register screen until auth initialization finishes.
On refresh with a stored token, the first render still satisfies
!currentUser, so users can see the auth form beforeinitializeAuth()decodes the token.🐛 Proposed guard
+ if (loading) { + return ( + <div className="min-h-screen bg-slate-50 flex items-center justify-center"> + <div className="w-8 h-8 border-4 border-slate-200 border-t-blue-500 rounded-full animate-spin" /> + </div> + ); + } + if (!currentUser) { return (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.jsx` around lines 249 - 259, The auth form is shown before initializeAuth() finishes because the render only checks currentUser; add an auth-initialized guard (e.g., authInitialized or isAuthReady) from your auth context/hook and change the render logic so you return a loader/blank until initializeAuth completes; specifically update the conditional around the JSX that currently checks currentUser (the block containing currentUser, isRegistering, Register, and Login) to require authInitialized (e.g., if (!authInitialized) return null or a spinner) and only show the Register/Login UI when authInitialized && !currentUser, ensuring initializeAuth() is called earlier (or expose its completion flag) so the component waits for token decoding before showing the form.
97-104:⚠️ Potential issue | 🟡 MinorClear selected entities on logout too.
selectedPetandselectedRecordcan survive logout in memory after the token/user/list state is cleared.🛡️ Proposed cleanup
setIsRegistering(false); setMyPets([]); setMyRecords([]); + setSelectedPet(null); + setSelectedRecord(null); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.jsx` around lines 97 - 104, The handleLogout function leaves selectedPet and selectedRecord in memory; update handleLogout to also clear selection state by calling the selectors' setters (e.g., setSelectedPet(null) and setSelectedRecord(null) or equivalent) alongside setCurrentUser, setMyPets, setMyRecords, setCurrentView, and setIsRegistering so no selected entity persists after logout.
🧹 Nitpick comments (2)
frontend/src/pages/CaseDetail.jsx (1)
139-141: Avoid mutating thecaseDataprop.Directly assigning
caseData.description/titlecan desync parent state and makes rendering depend on prop mutation side effects. Keep a local record state or notify the parent with the updated record instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/CaseDetail.jsx` around lines 139 - 141, The code is mutating the caseData prop (caseData.description/title); instead, create and update local component state or call a parent updater: initialize a local state (e.g., localCase via useState) from caseData in the CaseDetail component, replace direct assignments with setLocalCase({...localCase, description: res.data.description, title: res.data.title}), or call a provided prop callback (e.g., onUpdateCase(updatedCase)) to notify the parent with the updated record; update rendering to use localCase rather than mutating caseData and remove any direct writes to caseData.frontend/src/components/admin/UserModal.jsx (1)
61-76: Add dialog semantics and an accessible close label.The modal renders as a generic
div, so assistive tech does not get modal context, and the icon-only close button has no accessible name.♿ Suggested markup
- <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-sm animate-in fade-in duration-200"> - <div className="bg-white w-full max-w-md rounded-[2.5rem] shadow-2xl overflow-hidden border border-slate-200 max-h-[95vh] flex flex-col"> + <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-sm animate-in fade-in duration-200"> + <div + role="dialog" + aria-modal="true" + aria-labelledby="user-modal-title" + className="bg-white w-full max-w-md rounded-[2.5rem] shadow-2xl overflow-hidden border border-slate-200 max-h-[95vh] flex flex-col" + > @@ - <h2 className="text-2xl font-black text-slate-900 italic tracking-tight text-left"> + <h2 id="user-modal-title" className="text-2xl font-black text-slate-900 italic tracking-tight text-left"> @@ - <button onClick={onClose} className="p-2 hover:bg-slate-100 rounded-full transition-colors text-slate-400"> + <button type="button" aria-label="Stäng dialog" onClick={onClose} className="p-2 hover:bg-slate-100 rounded-full transition-colors text-slate-400">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/admin/UserModal.jsx` around lines 61 - 76, Wrap the modal container div in proper dialog semantics by adding role="dialog" and aria-modal="true" and set aria-labelledby to the id of the title element; give the <h2> title a unique id (e.g., modal-title) so screen readers announce the dialog title (refer to the UserModal.jsx top-level modal div and the h2 that renders {initialData ? 'Redigera Profil' : 'Ny Användare'}). Also make the icon-only close button accessible by adding an explicit accessible name (e.g., aria-label="Stäng" or aria-label="Close dialog") on the button that uses onClose so it is announced and focusable. Ensure these attributes are added to the elements in UserModal.jsx (modal container div, h2 title, and the close button) without changing visual layout.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/App.jsx`:
- Around line 47-60: The loading state is cleared in the finally block before
async owner data finishes, causing owners to see empty dashboards; change the
flow in the auth/startup block so that when user.role is not ROLE_VET or
ROLE_ADMIN you await fetchData(user) (or otherwise handle its Promise) before
calling setLoading(false) — locate the conditional using setCurrentView and
fetchData in App.jsx and ensure fetchData(user) is awaited (or chained with
.then/.catch) and errors still call handleLogout, then move or delay
setLoading(false) until after fetchData completes.
In `@frontend/src/components/admin/ClinicModal.jsx`:
- Around line 45-59: The modal in ClinicModal.jsx is missing dialog semantics
and accessible labeling; update the root modal container to include
role="dialog", aria-modal="true" and aria-labelledby that points to the heading
element (give the h2 a unique id), add an accessible name to the close icon
button by adding aria-label="Close" (the button currently wired to onClose and
renders <X />), and ensure every form input inside this component has an
associated <label> element with htmlFor matching a unique input id (and link any
descriptive text via aria-describedby where appropriate); keep these changes
within the ClinicModal component and reuse symbols visible in the diff such as
the h2 heading, onClose button, initialData conditional text, and the form
inputs so screen readers can correctly announce the dialog and controls.
In `@frontend/src/components/admin/UserModal.jsx`:
- Around line 43-50: The vet creation call currently omits the selected clinic
and always calls vetService.create even when editing; update the payload to
include formData.clinicId (or the component's clinic selection) when calling
vetService.create, and add a guard that checks for an existing vet record (e.g.,
response?.data?.id or a local existingVet/editingUser flag) so that on edits you
call vetService.update or vetService.upsert instead of create to avoid
duplicate/409 errors; ensure you reference isVet, response.data.id,
formData.clinicId, and vetService.create/update/upsert in your change.
In `@frontend/src/pages/CaseDetail.jsx`:
- Around line 74-82: handleCloseCase currently creates a comment then calls
medicalRecordService.closeRecord, which can leave a duplicate final note if
close fails; change the flow to call a single atomic endpoint (e.g.,
medicalRecordService.closeRecordWithNote or closeRecord({recordId, finalNote}))
that accepts the final note body (or noteId) so the backend can store the note
and mark the record closed in one transaction; update handleCloseCase to send
{recordId: caseData.id, finalNote: `SLUTGILTIG NOTERING: ${clinicalNote}`} to
that endpoint, remove the separate commentService.createComment call, handle
success/failure and setShowCloseModal(false) only on success, and ensure error
handling shows a retryable message and prevents duplicate notes by verifying
record closed state on retry.
- Around line 286-318: The attachments UI currently allows uploads/deletes even
when the journal is CLOSED; update the render and handlers to block changes: in
CaseDetail.jsx, disable the file input when localStatus === 'CLOSED' (add
localStatus === 'CLOSED' to the existing disabled prop and update the label
styling logic) and hide/disable the delete button in the attachments map when
localStatus === 'CLOSED' so users can't remove files from the UI; additionally,
add early-return guards at the top of handleFileUpload and
handleDeleteAttachment that check if localStatus === 'CLOSED' and abort
(optionally surface a user message), ensuring the client-side logic prevents
changes as well as the UI.
- Around line 16-19: The local editable state (localStatus, editedTitle,
editedDescription) is only initialized once and isn't updated when caseData
changes, causing stale values when opening a different case; add a useEffect
that runs when caseData changes and calls setLocalStatus(caseData?.status),
setEditedTitle(caseData?.title || ''), and
setEditedDescription(caseData?.description || '') (optionally guard so it only
syncs when isEditing is false if you want to preserve in-progress edits),
referencing the existing state setters (setLocalStatus, setEditedTitle,
setEditedDescription, isEditing) to keep the component in sync with new
caseData.
In `@frontend/src/pages/Login.jsx`:
- Line 33: The "Kom ihåg mig" checkbox is not wired and token is always saved to
localStorage (localStorage.setItem('token', token)), and the "Glömt lösenord?"
button is a no-op; wire the controls: add component state (e.g., rememberMe)
used by the Login component to store the token conditionally—if rememberMe is
true save to localStorage, otherwise save to sessionStorage or avoid persistent
storage—and implement a handler for the "Glömt lösenord?" button (e.g.,
onForgotPassword) to navigate to your password-reset route or open the reset
modal; update the controls referenced in Login.jsx (the checkbox and the forgot
password button handlers, also present around the other occurrence at lines
96-103) so the UI reflects real behavior.
- Line 38: Replace the permissive logging of the full Axios error object in
Login.jsx (where console.error("Login error:", err) is called) with a sanitized
log that only prints safe fields such as err.response?.status and err.message
(or a default message) to avoid exposing request body/config (credentials) in
browser logs; locate the error handling in the login handler function and update
the console.error call to only include these specific fields and optionally a
short descriptive string.
In `@frontend/src/pages/VetDashboard.jsx`:
- Around line 20-24: In fetchRecords (the useCallback in VetDashboard.jsx) the
early return when !isPersonalView && !clinicId leaves the component stuck
loading; before returning, call the loading state setter (setLoading(false)) to
clear the spinner (and optionally clear any stale list by calling setRecords([])
or similar) so the clinic overview doesn't stay on "Laddar ärenden..." when
clinicId is missing.
In `@frontend/src/services/api.jsx`:
- Line 121: The current searchByEmail function interpolates the email into the
URL which can break addresses with special characters; change the call to use
Axios' params config instead of string interpolation so Axios will URL-encode
the email (i.e., call api.get with the path '/users/search' and a params object
containing the email) and remove the template literal usage in searchByEmail.
In `@src/main/java/org/example/vet1177/controller/MedicalRecordController.java`:
- Line 66: Replace the PII usage in the logging call inside
MedicalRecordController: avoid currentUser.getEmail() and log either
currentUser.getId() or omit the user value; specifically update the log.info
invocation that currently reads "GET /api/medical-records/my-assigned - User:
{}" with currentUser.getEmail() to use currentUser.getId() (or remove the
placeholder) so logs do not contain email addresses.
In `@src/main/java/org/example/vet1177/repository/ActivityLogRepository.java`:
- Around line 13-14: The current repository method
ActivityLogRepository.findAllByOrderByCreatedAtDesc() loads the entire audit
table; replace or overload it with a pageable variant such as
findAllByOrderByCreatedAtDesc(Pageable pageable) returning Page<ActivityLog> (or
Slice<ActivityLog>) so queries are executed with pagination, and enforce a
maximum page size in the controller/service that calls this repository method to
cap results per request.
---
Duplicate comments:
In `@frontend/src/App.jsx`:
- Around line 249-259: The auth form is shown before initializeAuth() finishes
because the render only checks currentUser; add an auth-initialized guard (e.g.,
authInitialized or isAuthReady) from your auth context/hook and change the
render logic so you return a loader/blank until initializeAuth completes;
specifically update the conditional around the JSX that currently checks
currentUser (the block containing currentUser, isRegistering, Register, and
Login) to require authInitialized (e.g., if (!authInitialized) return null or a
spinner) and only show the Register/Login UI when authInitialized &&
!currentUser, ensuring initializeAuth() is called earlier (or expose its
completion flag) so the component waits for token decoding before showing the
form.
- Around line 97-104: The handleLogout function leaves selectedPet and
selectedRecord in memory; update handleLogout to also clear selection state by
calling the selectors' setters (e.g., setSelectedPet(null) and
setSelectedRecord(null) or equivalent) alongside setCurrentUser, setMyPets,
setMyRecords, setCurrentView, and setIsRegistering so no selected entity
persists after logout.
In `@frontend/src/services/api.jsx`:
- Around line 92-97: For attachmentService: stop overriding the FormData
Content-Type in attachmentService.upload so Axios can set the multipart boundary
automatically (remove the headers: {'Content-Type': 'multipart/form-data'}
override) and for attachmentService.download request binary data by adding
responseType: 'blob' to the api.get call; update the upload and download methods
(attachmentService.upload and attachmentService.download) accordingly.
---
Nitpick comments:
In `@frontend/src/components/admin/UserModal.jsx`:
- Around line 61-76: Wrap the modal container div in proper dialog semantics by
adding role="dialog" and aria-modal="true" and set aria-labelledby to the id of
the title element; give the <h2> title a unique id (e.g., modal-title) so screen
readers announce the dialog title (refer to the UserModal.jsx top-level modal
div and the h2 that renders {initialData ? 'Redigera Profil' : 'Ny Användare'}).
Also make the icon-only close button accessible by adding an explicit accessible
name (e.g., aria-label="Stäng" or aria-label="Close dialog") on the button that
uses onClose so it is announced and focusable. Ensure these attributes are added
to the elements in UserModal.jsx (modal container div, h2 title, and the close
button) without changing visual layout.
In `@frontend/src/pages/CaseDetail.jsx`:
- Around line 139-141: The code is mutating the caseData prop
(caseData.description/title); instead, create and update local component state
or call a parent updater: initialize a local state (e.g., localCase via
useState) from caseData in the CaseDetail component, replace direct assignments
with setLocalCase({...localCase, description: res.data.description, title:
res.data.title}), or call a provided prop callback (e.g.,
onUpdateCase(updatedCase)) to notify the parent with the updated record; update
rendering to use localCase rather than mutating caseData and remove any direct
writes to caseData.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c9e6ec0a-16f6-40d4-be6a-9a7837103703
📒 Files selected for processing (17)
frontend/src/App.jsxfrontend/src/components/Layout.jsxfrontend/src/components/admin/AuditLogView.jsxfrontend/src/components/admin/ClinicModal.jsxfrontend/src/components/admin/UserModal.jsxfrontend/src/pages/AdminDashboard.jsxfrontend/src/pages/CaseDetail.jsxfrontend/src/pages/Login.jsxfrontend/src/pages/VetDashboard.jsxfrontend/src/services/api.jsxsrc/main/java/org/example/vet1177/controller/ActivityLogController.javasrc/main/java/org/example/vet1177/controller/MedicalRecordController.javasrc/main/java/org/example/vet1177/repository/ActivityLogRepository.javasrc/main/java/org/example/vet1177/security/JwtService.javasrc/main/java/org/example/vet1177/services/ActivityLogService.javasrc/main/java/org/example/vet1177/services/MedicalRecordService.javasrc/main/resources/data.sql
✅ Files skipped from review due to trivial changes (1)
- frontend/src/components/Layout.jsx
🚧 Files skipped from review as they are similar to previous changes (3)
- src/main/resources/data.sql
- src/main/java/org/example/vet1177/security/JwtService.java
- frontend/src/pages/AdminDashboard.jsx
| public ResponseEntity<List<MedicalRecordSummaryResponse>> getMyAssignedRecords( | ||
| @AuthenticationPrincipal User currentUser) { | ||
|
|
||
| log.info("GET /api/medical-records/my-assigned - User: {}", currentUser.getEmail()); |
There was a problem hiding this comment.
Avoid logging the user’s email address.
Email is PII; routine access logs should use currentUser.getId() or omit the user value.
🛡️ Proposed fix
- log.info("GET /api/medical-records/my-assigned - User: {}", currentUser.getEmail());
+ log.info("GET /api/medical-records/my-assigned - userId={}", currentUser.getId());📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| log.info("GET /api/medical-records/my-assigned - User: {}", currentUser.getEmail()); | |
| log.info("GET /api/medical-records/my-assigned - userId={}", currentUser.getId()); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/org/example/vet1177/controller/MedicalRecordController.java` at
line 66, Replace the PII usage in the logging call inside
MedicalRecordController: avoid currentUser.getEmail() and log either
currentUser.getId() or omit the user value; specifically update the log.info
invocation that currently reads "GET /api/medical-records/my-assigned - User:
{}" with currentUser.getEmail() to use currentUser.getId() (or remove the
placeholder) so logs do not contain email addresses.
| // Ny metod - Hämta alla | ||
| List<ActivityLog> findAllByOrderByCreatedAtDesc(); |
There was a problem hiding this comment.
Make the audit-log query pageable.
findAllByOrderByCreatedAtDesc() materializes the full audit table. This admin feed will degrade as logs grow; expose a Pageable/Slice query and cap page size at the controller/service boundary.
♻️ Proposed repository shape
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
@@
- // Ny metod - Hämta alla
- List<ActivityLog> findAllByOrderByCreatedAtDesc();
+ Page<ActivityLog> findAllByOrderByCreatedAtDesc(Pageable pageable);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Ny metod - Hämta alla | |
| List<ActivityLog> findAllByOrderByCreatedAtDesc(); | |
| package org.example.vet1177.repository; | |
| import org.springframework.data.domain.Page; | |
| import org.springframework.data.domain.Pageable; | |
| import org.springframework.data.jpa.repository.JpaRepository; | |
| import org.example.vet1177.model.ActivityLog; | |
| public interface ActivityLogRepository extends JpaRepository<ActivityLog, Long> { | |
| Page<ActivityLog> findAllByOrderByCreatedAtDesc(Pageable pageable); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/org/example/vet1177/repository/ActivityLogRepository.java`
around lines 13 - 14, The current repository method
ActivityLogRepository.findAllByOrderByCreatedAtDesc() loads the entire audit
table; replace or overload it with a pageable variant such as
findAllByOrderByCreatedAtDesc(Pageable pageable) returning Page<ActivityLog> (or
Slice<ActivityLog>) so queries are executed with pagination, and enforce a
maximum page size in the controller/service that calls this repository method to
cap results per request.
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (2)
frontend/src/pages/CaseDetail.jsx (1)
82-88:⚠️ Potential issue | 🟠 MajorSend the close-case note through the service contract.
This still calls
closeRecord(caseData.id, payload), butfrontend/src/services/api.jsx:87-89definescloseRecordwith only(id), so the final note is ignored. Update the service/backend close endpoint to accept the payload, otherwise the journal closes without the required “SLUTGILTIG NOTERING”.🛠️ Service contract fix
- closeRecord: (id) => api.put(`/medical-records/${id}/close`), + closeRecord: (id, data) => api.put(`/medical-records/${id}/close`, data),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/CaseDetail.jsx` around lines 82 - 88, The close flow is calling medicalRecordService.closeRecord(caseData.id, { finalNote: ... }) from handleCloseCase but the service API closeRecord function only accepts (id) so the final note is dropped; update the service contract and backend endpoint to accept and forward the payload: change medicalRecordService.closeRecord to accept (id, payload) and ensure the backend handler (the close record endpoint) signature accepts the payload and persists finalNote (e.g., use the finalNote key used in handleCloseCase) so the “SLUTGILTIG NOTERING” is saved when handleCloseCase calls medicalRecordService.closeRecord.frontend/src/App.jsx (1)
254-264:⚠️ Potential issue | 🟠 MajorGate the auth screen behind the startup loading state.
On the first render
currentUseris stillnullwhile the token is being decoded, so this branch can flash Login/Register for already-authenticated users. Return a loading placeholder first, then render auth screens only after startup auth finishes.🐛 Suggested guard
+ if (loading) { + return ( + <div className="min-h-screen bg-slate-50 flex items-center justify-center"> + <div className="w-8 h-8 border-4 border-slate-200 border-t-blue-500 rounded-full animate-spin" /> + </div> + ); + } + if (!currentUser) { return ( <div className="min-h-screen bg-slate-50 flex items-center justify-center p-6">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.jsx` around lines 254 - 264, The auth branch currently returns Login/Register as soon as currentUser is falsy, causing a flash while the app decodes a token; update the conditional to first check the startup/auth-loading flag (e.g., isAuthLoading or isStartupDone) and render a loading placeholder until startup auth finishes, then only if startup is complete and currentUser is still null render the Register or Login components (referencing currentUser, isRegistering, Register, and Login in App.jsx). Ensure the loading state is set where token decoding occurs (or reuse an existing flag) so the UI waits for that flag before showing auth screens.
🧹 Nitpick comments (2)
frontend/src/components/admin/ClinicModal.jsx (2)
154-158: Loading state hides the button's accessible label.While
loadingis true the button renders only<Loader2 />(decorative) with no visible or accessible text, so screen readers announce an unlabeled disabled button. Consider pairing the spinner with text (e.g. "Sparar...") or adding anaria-labelon the button during loading.♿ Suggested fix
- {loading ? ( - <Loader2 className="animate-spin" size={16} /> - ) : ( - initialData ? 'Uppdatera Klinik' : 'Registrera Klinik' - )} + {loading ? ( + <> + <Loader2 className="animate-spin" size={16} aria-hidden="true" /> + <span>Sparar...</span> + </> + ) : ( + initialData ? 'Uppdatera Klinik' : 'Registrera Klinik' + )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/admin/ClinicModal.jsx` around lines 154 - 158, In ClinicModal.jsx, the submit button currently renders only <Loader2 /> when the loading state is true, leaving the button unlabeled for assistive tech; update the button render (in the component ClinicModal where loading and initialData are used) to include accessible text during loading (e.g. "Sparar..." or "Updating...") alongside the <Loader2 /> OR add an appropriate aria-label/aria-live attribute to the button while loading so screen readers get a clear label; ensure the fallback still uses initialData ? 'Uppdatera Klinik' : 'Registrera Klinik' when not loading.
40-44: Prefer inline error UI overalert().
alert()blocks the main thread, can't be styled, and is easy to miss on mobile. Since the component already managesloadingstate, adding anerrorstate and rendering it inline in the modal would be a cleaner UX and more consistent with the rest of the design.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/admin/ClinicModal.jsx` around lines 40 - 44, Replace the blocking alert(...) in the catch block with an inline error state: add const [error, setError] = useState(null) (and clear it when opening the modal or before saving), in the catch do setError(err.response?.data?.message || "Ett fel uppstod vid sparandet av kliniken.") instead of alert, keep setLoading(false) in finally, and render the error string inside the modal JSX (near the form submit button) with the same styling pattern used elsewhere; also clear setError(null) on successful save.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/App.jsx`:
- Around line 80-82: The fetchData function only loads myPets for OWNERs so vets
hit a silent no-op when CaseDetail's "Patientkort" action expects a pet; update
fetchData (or CaseDetail) so non-OWNER routes are handled: either (A) inside
fetchData(user = currentUser) detect when user.role !== 'ROLE_OWNER' and if a
petId prop/route param exists call a new or existing fetchPetById(petId) to load
that single pet into state (so CaseDetail can find it), or (B) change CaseDetail
to disable/hide the Patientkort button when currentUser.role !== 'ROLE_OWNER' or
when the pet lookup (myPets.find(...)) returns undefined; reference fetchData,
currentUser, myPets, petId, and CaseDetail to locate the logic to change. Ensure
you add minimal error handling for missing pet and update state/props
accordingly.
In `@frontend/src/components/admin/ClinicModal.jsx`:
- Around line 145-153: The submit button in ClinicModal.jsx uses a non-existent
Tailwind class `flex-2` (also present in UserModal.jsx), so replace that with a
valid utility like `grow-[2] basis-0` inside the className string for the submit
button (and the analogous submit button in UserModal.jsx), or add a custom
`flex-2` utility to the Tailwind config; update the className on the component's
submit button (the JSX element that currently includes `flex-2 px-10 ...`)
accordingly so the button takes the intended 2× width ratio.
In `@frontend/src/components/admin/UserModal.jsx`:
- Around line 92-98: The modal declared in UserModal.jsx needs focus management:
in the UserModal component add a ref (e.g., modalRef) and on open save
document.activeElement, move focus into the dialog (to the element with id
modalTitleId or first focusable), install a keydown handler to close on Escape
(call the existing close handler) and implement a Tab/Shift+Tab focus trap
within modalRef, and on close restore focus to the previously focused element;
alternatively wrap the markup with a shared accessible modal primitive that
provides these behaviors.
- Around line 58-80: The frontend calls vetService.update(existingVetId,
vetPayload) but vetService lacks an update method and the backend VetController
has no PUT/PATCH endpoint; add a new update method to frontend
src/services/api.jsx (vetService.update(id, payload)) that calls the API PUT
/vets/:id, implement a corresponding PUT handler in the backend VetController
(e.g., update(id) or put id route) that validates input and updates the vet
record in the vet service/repository, and also add backend logic (and a frontend
vetService.delete(id) if missing) to remove or orphan-clean existing vet records
when a user’s role changes away from VET so editing/role changes don’t leave
orphaned vet entries.
In `@frontend/src/pages/CaseDetail.jsx`:
- Around line 25-31: The effect that syncs local edit state (useEffect with
dependencies [caseData, isEditing]) resets editedTitle/editedDescription when
isEditing flips to false, causing saved changes to be overwritten by stale
caseData; update the logic in that effect (or create a separate effect) so it
only initializes local state from caseData when entering edit mode (i.e., when
isEditing becomes true or when caseData is initially loaded) and do not re-sync
on isEditing false; reference the useEffect block, setEditedTitle,
setEditedDescription, caseData, and setIsEditing/handleSaveRecord to ensure
saved edits persist until parent refetch updates caseData.
In `@frontend/src/pages/Login.jsx`:
- Around line 81-90: The label currently reads "E-post eller användarnamn" while
the input element uses type="email" (the input element with attributes
value={email} and onChange={(e) => setEmail(e.target.value)}), causing browser
email-only validation; either change the label text to an email-only string
(e.g., "E-post" or "E-postadress") to match the email-only contract, update
placeholder/aria-label to match, and keep the input as type="email", OR if
usernames should be accepted, change the input's type from "email" to "text" and
adjust any client-side/email validation accordingly (keep value={email} and
setEmail usage consistent).
---
Duplicate comments:
In `@frontend/src/App.jsx`:
- Around line 254-264: The auth branch currently returns Login/Register as soon
as currentUser is falsy, causing a flash while the app decodes a token; update
the conditional to first check the startup/auth-loading flag (e.g.,
isAuthLoading or isStartupDone) and render a loading placeholder until startup
auth finishes, then only if startup is complete and currentUser is still null
render the Register or Login components (referencing currentUser, isRegistering,
Register, and Login in App.jsx). Ensure the loading state is set where token
decoding occurs (or reuse an existing flag) so the UI waits for that flag before
showing auth screens.
In `@frontend/src/pages/CaseDetail.jsx`:
- Around line 82-88: The close flow is calling
medicalRecordService.closeRecord(caseData.id, { finalNote: ... }) from
handleCloseCase but the service API closeRecord function only accepts (id) so
the final note is dropped; update the service contract and backend endpoint to
accept and forward the payload: change medicalRecordService.closeRecord to
accept (id, payload) and ensure the backend handler (the close record endpoint)
signature accepts the payload and persists finalNote (e.g., use the finalNote
key used in handleCloseCase) so the “SLUTGILTIG NOTERING” is saved when
handleCloseCase calls medicalRecordService.closeRecord.
---
Nitpick comments:
In `@frontend/src/components/admin/ClinicModal.jsx`:
- Around line 154-158: In ClinicModal.jsx, the submit button currently renders
only <Loader2 /> when the loading state is true, leaving the button unlabeled
for assistive tech; update the button render (in the component ClinicModal where
loading and initialData are used) to include accessible text during loading
(e.g. "Sparar..." or "Updating...") alongside the <Loader2 /> OR add an
appropriate aria-label/aria-live attribute to the button while loading so screen
readers get a clear label; ensure the fallback still uses initialData ?
'Uppdatera Klinik' : 'Registrera Klinik' when not loading.
- Around line 40-44: Replace the blocking alert(...) in the catch block with an
inline error state: add const [error, setError] = useState(null) (and clear it
when opening the modal or before saving), in the catch do
setError(err.response?.data?.message || "Ett fel uppstod vid sparandet av
kliniken.") instead of alert, keep setLoading(false) in finally, and render the
error string inside the modal JSX (near the form submit button) with the same
styling pattern used elsewhere; also clear setError(null) on successful save.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4258aaef-09c0-48a7-a587-8472d366efd7
📒 Files selected for processing (7)
frontend/src/App.jsxfrontend/src/components/admin/ClinicModal.jsxfrontend/src/components/admin/UserModal.jsxfrontend/src/pages/CaseDetail.jsxfrontend/src/pages/Login.jsxfrontend/src/pages/VetDashboard.jsxfrontend/src/services/api.jsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/services/api.jsx
| // 3. Datahämtning: Endast för OWNER-rollen | ||
| const fetchData = async (user = currentUser) => { | ||
| if (!user || user.role !== 'ROLE_OWNER') return; |
There was a problem hiding this comment.
Avoid a silent no-op patient-card action for vets.
Vet case detail routes are reachable from Lines 168-187, but fetchData only loads myPets for owners, so Line 214 usually finds nothing for vets and the Patientkort button in CaseDetail does nothing. Either hide/disable that action for non-owner routes or fetch the pet by petId when it is missing.
🛠️ Minimal no-op guard
onGoToPet={(petId) => {
const pet = myPets.find(p => p.id === petId);
if (pet) {
setSelectedPet(pet);
setCurrentView('pet-detail');
+ } else {
+ alert("Patientkort är inte tillgängligt från den här vyn ännu.");
}
}}Also applies to: 168-187, 213-219
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/App.jsx` around lines 80 - 82, The fetchData function only loads
myPets for OWNERs so vets hit a silent no-op when CaseDetail's "Patientkort"
action expects a pet; update fetchData (or CaseDetail) so non-OWNER routes are
handled: either (A) inside fetchData(user = currentUser) detect when user.role
!== 'ROLE_OWNER' and if a petId prop/route param exists call a new or existing
fetchPetById(petId) to load that single pet into state (so CaseDetail can find
it), or (B) change CaseDetail to disable/hide the Patientkort button when
currentUser.role !== 'ROLE_OWNER' or when the pet lookup (myPets.find(...))
returns undefined; reference fetchData, currentUser, myPets, petId, and
CaseDetail to locate the logic to change. Ensure you add minimal error handling
for missing pet and update state/props accordingly.
| <button | ||
| disabled={loading} | ||
| type="submit" | ||
| className={`flex-2 px-10 py-4 text-white text-[10px] font-black uppercase tracking-[0.2em] rounded-2xl transition-all italic shadow-lg flex items-center justify-center gap-2 ${ | ||
| initialData | ||
| ? 'bg-blue-600 hover:bg-blue-700 shadow-blue-900/20' | ||
| : 'bg-emerald-600 hover:bg-emerald-700 shadow-emerald-900/20' | ||
| }`} | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether the project extends Tailwind with a custom flex-2 utility.
fd -t f 'tailwind.config' frontend
fd -t f 'tailwind.config' frontend --exec cat {}
rg -nP '\bflex-2\b' frontendRepository: ithsjava25/project-backend-org-random-coders
Length of output: 1051
flex-2 is not a standard Tailwind utility and is not defined in the project's Tailwind config.
Tailwind ships flex-1, flex-auto, flex-initial, and flex-none by default — there is no flex-2. The class will be silently dropped, so the submit button won't take 2× the intended width ratio.
Replace with grow-[2] basis-0 or extend the Tailwind theme with a custom flex-2 utility. Note: The same issue appears in frontend/src/components/admin/UserModal.jsx:253.
♻️ Suggested fix
- <button
- disabled={loading}
- type="submit"
- className={`flex-2 px-10 py-4 text-white text-[10px] font-black uppercase tracking-[0.2em] rounded-2xl transition-all italic shadow-lg flex items-center justify-center gap-2 ${
+ <button
+ disabled={loading}
+ type="submit"
+ className={`grow-[2] basis-0 px-10 py-4 text-white text-[10px] font-black uppercase tracking-[0.2em] rounded-2xl transition-all italic shadow-lg flex items-center justify-center gap-2 ${📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| disabled={loading} | |
| type="submit" | |
| className={`flex-2 px-10 py-4 text-white text-[10px] font-black uppercase tracking-[0.2em] rounded-2xl transition-all italic shadow-lg flex items-center justify-center gap-2 ${ | |
| initialData | |
| ? 'bg-blue-600 hover:bg-blue-700 shadow-blue-900/20' | |
| : 'bg-emerald-600 hover:bg-emerald-700 shadow-emerald-900/20' | |
| }`} | |
| > | |
| <button | |
| disabled={loading} | |
| type="submit" | |
| className={`grow-[2] basis-0 px-10 py-4 text-white text-[10px] font-black uppercase tracking-[0.2em] rounded-2xl transition-all italic shadow-lg flex items-center justify-center gap-2 ${ | |
| initialData | |
| ? 'bg-blue-600 hover:bg-blue-700 shadow-blue-900/20' | |
| : 'bg-emerald-600 hover:bg-emerald-700 shadow-emerald-900/20' | |
| }`} | |
| > |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/admin/ClinicModal.jsx` around lines 145 - 153, The
submit button in ClinicModal.jsx uses a non-existent Tailwind class `flex-2`
(also present in UserModal.jsx), so replace that with a valid utility like
`grow-[2] basis-0` inside the className string for the submit button (and the
analogous submit button in UserModal.jsx), or add a custom `flex-2` utility to
the Tailwind config; update the className on the component's submit button (the
JSX element that currently includes `flex-2 px-10 ...`) accordingly so the
button takes the intended 2× width ratio.
| // 1. Spara/Uppdatera användaren | ||
| const response = await onSave(formData); | ||
|
|
||
| // 2. Hantera Veterinär-detaljer (Om rollen är VET) | ||
| if (isVet) { | ||
| const userId = response?.data?.id || initialData?.id; | ||
|
|
||
| const vetPayload = { | ||
| userId: userId, | ||
| clinicId: formData.clinicId, | ||
| licenseId: formData.licenseId, | ||
| specialization: formData.specialization, | ||
| bookingInfo: formData.bookingInfo | ||
| }; | ||
|
|
||
| // CodeRabbit Fix: Kolla om det redan finns en veterinärpost (Edit vs Create) | ||
| const existingVetId = initialData?.vetRecord?.id; | ||
|
|
||
| if (existingVetId) { | ||
| await vetService.update(existingVetId, vetPayload); | ||
| } else if (userId) { | ||
| await vetService.create(vetPayload); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify vetService methods and backend support for vet update/delete lifecycle.
set -euo pipefail
rg -n -C3 'export const vetService|vetService\.(create|update|delete)|/vets' \
frontend/src/services/api.jsx \
frontend/src/components/admin/UserModal.jsx \
frontend/src/pages/AdminDashboard.jsx
rg -n -C3 '(@(Put|Patch|Delete)Mapping|router\.(put|patch|delete)|app\.(put|patch|delete)).*vets|/vets' .Repository: ithsjava25/project-backend-org-random-coders
Length of output: 20173
Add missing update method to vetService and implement corresponding backend endpoint.
Line 77 calls vetService.update(existingVetId, vetPayload), but this method does not exist in frontend/src/services/api.jsx:127-131. The backend VetController also lacks a PUT/PATCH endpoint for updating veterinarian records—only POST (create), GET (getAll), and GET (getById) are implemented. Editing an existing veterinarian will throw "vetService.update is not a function" at runtime.
Implement the update method in vetService and add a corresponding PUT endpoint in the backend VetController to complete the vet edit workflow. Additionally, implement delete/cleanup handling for when a user role changes from VET to another role (currently the existing vet record is orphaned).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/admin/UserModal.jsx` around lines 58 - 80, The
frontend calls vetService.update(existingVetId, vetPayload) but vetService lacks
an update method and the backend VetController has no PUT/PATCH endpoint; add a
new update method to frontend src/services/api.jsx (vetService.update(id,
payload)) that calls the API PUT /vets/:id, implement a corresponding PUT
handler in the backend VetController (e.g., update(id) or put id route) that
validates input and updates the vet record in the vet service/repository, and
also add backend logic (and a frontend vetService.delete(id) if missing) to
remove or orphan-clean existing vet records when a user’s role changes away from
VET so editing/role changes don’t leave orphaned vet entries.
| <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-sm animate-in fade-in duration-200"> | ||
| <div | ||
| className="bg-white w-full max-w-md rounded-[2.5rem] shadow-2xl overflow-hidden border border-slate-200 max-h-[95vh] flex flex-col" | ||
| role="dialog" | ||
| aria-modal="true" | ||
| aria-labelledby={modalTitleId} | ||
| > |
There was a problem hiding this comment.
Add focus management for the modal.
This declares an ARIA modal dialog, but the component does not move focus into the dialog, trap tab focus, restore focus on close, or handle Escape. Keyboard users can interact with background content while the modal is open.
Add modal focus handling here or wrap this with a shared accessible modal primitive.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/admin/UserModal.jsx` around lines 92 - 98, The modal
declared in UserModal.jsx needs focus management: in the UserModal component add
a ref (e.g., modalRef) and on open save document.activeElement, move focus into
the dialog (to the element with id modalTitleId or first focusable), install a
keydown handler to close on Escape (call the existing close handler) and
implement a Tab/Shift+Tab focus trap within modalRef, and on close restore focus
to the previously focused element; alternatively wrap the markup with a shared
accessible modal primitive that provides these behaviors.
| useEffect(() => { | ||
| if (caseData && !isEditing) { | ||
| setLocalStatus(caseData.status); | ||
| setEditedTitle(caseData.title || ''); | ||
| setEditedDescription(caseData.description || ''); | ||
| } | ||
| }, [caseData, isEditing]); |
There was a problem hiding this comment.
Don’t reset saved journal edits back to stale props.
After handleSaveRecord succeeds, setIsEditing(false) retriggers the sync effect because isEditing is in the dependency list; that reloads editedTitle/editedDescription from stale caseData. The read-only view also renders caseData.*, so saved edits can appear to vanish until the parent refetches.
🐛 Suggested local-state fix
- useEffect(() => {
- if (caseData && !isEditing) {
- setLocalStatus(caseData.status);
- setEditedTitle(caseData.title || '');
- setEditedDescription(caseData.description || '');
- }
- }, [caseData, isEditing]);
+ useEffect(() => {
+ if (caseData) {
+ setLocalStatus(caseData.status);
+ setEditedTitle(caseData.title || '');
+ setEditedDescription(caseData.description || '');
+ setIsEditing(false);
+ }
+ }, [caseData?.id, caseData?.status, caseData?.title, caseData?.description]);
@@
- <h1 className="text-2xl font-bold text-slate-900 tracking-tight text-left">{caseData.title}</h1>
+ <h1 className="text-2xl font-bold text-slate-900 tracking-tight text-left">{editedTitle}</h1>
@@
- <h3 className="text-lg font-bold text-slate-800 mb-2 italic text-left">{caseData.title}</h3>
- <p className="text-slate-600 leading-relaxed font-medium italic whitespace-pre-wrap text-left">{caseData.description}</p>
+ <h3 className="text-lg font-bold text-slate-800 mb-2 italic text-left">{editedTitle}</h3>
+ <p className="text-slate-600 leading-relaxed font-medium italic whitespace-pre-wrap text-left">{editedDescription}</p>Also applies to: 148-160, 220-265
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/pages/CaseDetail.jsx` around lines 25 - 31, The effect that
syncs local edit state (useEffect with dependencies [caseData, isEditing])
resets editedTitle/editedDescription when isEditing flips to false, causing
saved changes to be overwritten by stale caseData; update the logic in that
effect (or create a separate effect) so it only initializes local state from
caseData when entering edit mode (i.e., when isEditing becomes true or when
caseData is initially loaded) and do not re-sync on isEditing false; reference
the useEffect block, setEditedTitle, setEditedDescription, caseData, and
setIsEditing/handleSaveRecord to ensure saved edits persist until parent refetch
updates caseData.
| <label className="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2 ml-1 text-left"> | ||
| E-post eller användarnamn | ||
| </label> | ||
| <input | ||
| type="email" | ||
| required | ||
| value={email} | ||
| onChange={(e) => setEmail(e.target.value)} | ||
| className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-[#003f5a] focus:bg-white outline-none transition-all font-medium text-sm" | ||
| placeholder="namn@exempel.se" |
There was a problem hiding this comment.
Align the label with the email-only input.
Line 82 says “E-post eller användarnamn”, but Line 85 uses type="email", so browser validation blocks username-style logins before submit. Since the backend login contract is email-based, rename the label to email-only; if usernames are intended, switch the input to type="text".
🛠️ Minimal fix
- E-post eller användarnamn
+ E-post📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <label className="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2 ml-1 text-left"> | |
| E-post eller användarnamn | |
| </label> | |
| <input | |
| type="email" | |
| required | |
| value={email} | |
| onChange={(e) => setEmail(e.target.value)} | |
| className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-[#003f5a] focus:bg-white outline-none transition-all font-medium text-sm" | |
| placeholder="namn@exempel.se" | |
| <label className="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2 ml-1 text-left"> | |
| E-post | |
| </label> | |
| <input | |
| type="email" | |
| required | |
| value={email} | |
| onChange={(e) => setEmail(e.target.value)} | |
| className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-[`#003f5a`] focus:bg-white outline-none transition-all font-medium text-sm" | |
| placeholder="namn@exempel.se" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/pages/Login.jsx` around lines 81 - 90, The label currently reads
"E-post eller användarnamn" while the input element uses type="email" (the input
element with attributes value={email} and onChange={(e) =>
setEmail(e.target.value)}), causing browser email-only validation; either change
the label text to an email-only string (e.g., "E-post" or "E-postadress") to
match the email-only contract, update placeholder/aria-label to match, and keep
the input as type="email", OR if usernames should be accepted, change the
input's type from "email" to "text" and adjust any client-side/email validation
accordingly (keep value={email} and setEmail usage consistent).
Summary by CodeRabbit
New Features
Style/Docs/Chores