Repository navigation
Conversation
|
Claude finished @vvshk's task in 4m 32s —— View job PR Review: adjustment as per new signup form in appI reviewed the diff against
Key findings (inline)🟠 Should address
🟡 Worth confirming / nice-to-have 👍 Looks good
Note: I reviewed for correctness/conventions only and did not run a linter or tests (this is a static frontend with no test suite in the repo). |
| const res = await fetch( | ||
| `${CONFIG.baseUrl}/client/checkMobile/${val}` | ||
| ); |
There was a problem hiding this comment.
Missing auth header + no res.ok check on the phone lookup.
Every other fetch in this codebase sends Authorization: Bearer <token>, and there's already an authenticated admin endpoint for mobile lookups — ${CONFIG.basePath}/card/by-mobile/${mobno} (see admin/wifi/permanentCodeRequests.js:408). This new call instead hits the client API (CONFIG.baseUrl, not basePath) with no token.
- If
/client/checkMobilerequires auth, this will 401. Because the code never checksres.okand only readsdata.exists, the admin will always see the misleading "Phone number is not registered!" message instead of an error. - Please confirm the endpoint is intentionally public. If it isn't, switch to the authenticated admin endpoint.
At minimum, guard on the response status:
| const res = await fetch( | |
| `${CONFIG.baseUrl}/client/checkMobile/${val}` | |
| ); | |
| const res = await fetch( | |
| `${CONFIG.baseUrl}/client/checkMobile/${val}` | |
| ); | |
| if (!res.ok) { | |
| nameEl.textContent = 'Error checking phone number'; | |
| nameEl.style.color = '#c62828'; | |
| return; | |
| } |
| const res = await fetch(`${CONFIG.baseUrl}/client/checkMobile/${val}`); | ||
| const data = await res.json(); |
There was a problem hiding this comment.
Same phone-lookup concern as in createCard.js: this hits the client API (CONFIG.baseUrl) with no Authorization header — a deviation from every other request here — and never checks res.ok, so an error response silently renders as "Phone number is not registered!". Please verify the endpoint is public; otherwise use the authenticated admin lookup. A minimal safety guard:
| const res = await fetch(`${CONFIG.baseUrl}/client/checkMobile/${val}`); | |
| const data = await res.json(); | |
| const res = await fetch(`${CONFIG.baseUrl}/client/checkMobile/${val}`); | |
| if (!res.ok) { | |
| nameEl.textContent = 'Error checking phone number'; | |
| nameEl.style.color = '#c62828'; | |
| return; | |
| } | |
| const data = await res.json(); |
| referencePhone: document.getElementById('referencePhone')?.value || null, | ||
| guestType: document.getElementById('guestType')?.value || null, | ||
| department: document.getElementById('department')?.value || null |
There was a problem hiding this comment.
Stale data: referencePhone / guestType / department are always submitted regardless of res_status.
These fields are hidden (not cleared) when the status changes, and populateForm fills them from the fetched record. So editing a card that was a GUEST/SEVA KUTIR and switching it to MUMUKSHU/PR will still send the old reference phone / guest type / department to the backend.
createCard.js handles this correctly by only attaching these keys inside the matching res_status block. Mirror that here so irrelevant fields are sent as null:
| referencePhone: document.getElementById('referencePhone')?.value || null, | |
| guestType: document.getElementById('guestType')?.value || null, | |
| department: document.getElementById('department')?.value || null | |
| referencePhone: | |
| document.getElementById('res_status').value === 'GUEST' | |
| ? document.getElementById('referencePhone')?.value || null | |
| : null, | |
| guestType: | |
| document.getElementById('res_status').value === 'GUEST' | |
| ? document.getElementById('guestType')?.value || null | |
| : null, | |
| department: | |
| document.getElementById('res_status').value === 'SEVA KUTIR' | |
| ? document.getElementById('department')?.value || null | |
| : null |
| refPhoneInput.addEventListener('input', async (e) => { | ||
| const val = e.target.value.trim(); | ||
| const nameEl = document.getElementById('refPhoneName'); | ||
| if (val.length === 10) { | ||
| nameEl.textContent = 'Checking...'; | ||
| nameEl.style.color = '#777'; | ||
| try { | ||
| const res = await fetch( | ||
| `${CONFIG.baseUrl}/client/checkMobile/${val}` | ||
| ); | ||
| const data = await res.json(); | ||
| if (data.exists) { | ||
| if (data.res_status === 'MUMUKSHU') { | ||
| nameEl.textContent = `Name: ${data.name} (Mumukshu)`; | ||
| nameEl.style.color = '#2e7d32'; | ||
| } else { | ||
| nameEl.textContent = `Name: ${data.name} (${data.res_status}) - Warning: Not a Mumukshu`; | ||
| nameEl.style.color = '#c62828'; | ||
| } | ||
| } else { | ||
| nameEl.textContent = 'Phone number is not registered!'; | ||
| nameEl.style.color = '#c62828'; | ||
| } | ||
| } catch (err) { | ||
| nameEl.textContent = 'Error checking phone number'; | ||
| nameEl.style.color = '#c62828'; | ||
| } | ||
| } else { | ||
| nameEl.textContent = ''; | ||
| } | ||
| }); |
There was a problem hiding this comment.
Two smaller points on this listener:
- No debounce / out-of-order responses. A request fires on every keystroke once the value hits 10 chars (and again on each subsequent edit). Since responses are async, a slower earlier request can resolve after a later one and overwrite the label with stale data. Consider debouncing, or tracking the latest query and ignoring outdated responses.
- Duplication. This inline handler is a near-exact copy of
checkReferencePhone()inupdateCard.js. Extracting a shared helper (or reusing the same function name/logic) would keep the two forms in sync as this evolves.
Neither blocks the PR, but worth cleaning up while the code is fresh.
| placeholder="Auto-generated" | ||
| readonly |
There was a problem hiding this comment.
cardno is now readonly / auto-generated and lost its previous required + pattern="[0-9]{10}". Nothing in createCard.js populates this field, so the submit handler sends cardno: form.cardno.value as an empty string (createCard.js:231).
Please confirm the /card/create backend treats an empty cardno as "generate one". If it still validates the format, "" will be rejected; if it auto-generates, consider dropping cardno from the request payload entirely to make the intent explicit.
Conflicts in the card edit screen keep dev's #202 guest fields (host shown, can't be cleared, type kept). On top of that: - The reference-phone lookup (new refPhone.js) uses the staff-only by-mobile route with the staff token, checks res.ok, is debounced, ignores stale answers, and shows the host's name and type as text. It warns when the host is not a Mumukshu and says "not registered" for an unknown phone. - Create: the card number box says it is made on save, and the new number is shown after create. Seva Kutir cards get a department. - Edit: hidden department and phone fields are no longer submitted. - API values put into option lists are escaped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Claude finished @vendz's task in 1m 8s —— View job PR Review (second pass, after the new push)
Earlier review comments, now fixed ✅
New inline comments🟠 Should fix
🟡 Nit Things to check with the backend
👍 Looks good
This was a read-only review. I didn't run a linter or tests because the repo has neither for these static pages. |
| stateSelect.addEventListener('change', () => | ||
| fetchCities(country, stateSelect.value) | ||
| ); |
There was a problem hiding this comment.
Duplicate change listeners on the state dropdown (this bug was already there; you only reformatted it, but it's worth fixing while you're here).
Each call to fetchStates adds another listener to #state, and each one keeps the country it was created with. If staff pick country A and then switch to country B, changing the state calls fetchCities(A, state) and fetchCities(B, state) at the same time. Both requests then append to #city, so the dropdown can show duplicate cities or cities from the wrong country. The country listener at line 160 isn't affected because loadLocationData runs only once.
Fix: read the current country when the event fires. Either register the listener once, outside fetchStates, or set the handler property so it replaces the old one:
| stateSelect.addEventListener('change', () => | |
| fetchCities(country, stateSelect.value) | |
| ); | |
| stateSelect.onchange = () => | |
| fetchCities(document.getElementById('country').value, stateSelect.value); |
| <input | ||
| type="text" | ||
| id="reference_phone" | ||
| name="reference_phone" | ||
| class="form-control" | ||
| /> |
There was a problem hiding this comment.
The reference phone isn't checked for format. refPhone.js only looks up the host when the value is exactly 10 characters, but nothing stops the form from being submitted with 12345 or letters in it. assignCard only checks that the field isn't empty, so a bad number goes straight to /card/create. Use the same limits as #mobno:
| <input | |
| type="text" | |
| id="reference_phone" | |
| name="reference_phone" | |
| class="form-control" | |
| /> | |
| <input | |
| type="text" | |
| id="reference_phone" | |
| name="reference_phone" | |
| class="form-control" | |
| maxlength="10" | |
| pattern="\d{10}" | |
| /> |
(updateCard.html:201 should get the same maxlength/pattern. An empty field still passes pattern, so it stays optional there.)
| show('Checking...', null); | ||
| try { | ||
| const res = await fetch( | ||
| `${CONFIG.baseUrl}/admin/card/by-mobile/${encodeURIComponent(phone)}`, |
There was a problem hiding this comment.
Nit: this builds the same URL as the other by-mobile callers, but in a different way. Those use CONFIG.basePath (utsavRegistration.js:239, permanentCodeRequests.js:408). Using it here too means the admin prefix is defined in one place only.
| `${CONFIG.baseUrl}/admin/card/by-mobile/${encodeURIComponent(phone)}`, | |
| `${CONFIG.basePath}/card/by-mobile/${encodeURIComponent(phone)}`, |
- The state dropdown has one handler that reads the current country, so a country switch no longer runs duplicate city lookups for the old country. - The reference phone must be 10 digits (maxlength and pattern) on create and edit. - The phone lookup builds its URL from CONFIG.basePath like other callers. - Create no longer sends an empty cardno; the backend makes the number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review round 1 handled in 7bfc0c2. Create no longer sends |
|
Claude finished @vendz's task in 1m 2s —— View job PR Review (third pass, after commit
|
| Earlier finding | Status |
|---|---|
Duplicate change listeners on #state used an old country |
✅ Now uses stateSelect.onchange, which reads the current #country when the event fires (createCard.js:193) |
| Reference phone format wasn't checked | ✅ Both screens now have maxlength="10" pattern="\d{10}" |
refPhone.js used CONFIG.baseUrl + /admin/... |
✅ Now uses CONFIG.basePath |
Create sent cardno: "" |
✅ The key is removed from the request |
New inline comments
🟠 Should fix
- A hidden invalid phone blocks Submit with no message. This comes from the new
pattern. If staff type a partial number as a guest and then switch to another type, the hidden field still fails validation. The browser can't focus a hidden field, so it shows no message and nothing happens when Submit is pressed. Updating a card has the same problem. Fix: disable the phone input while it's hidden. Inline →
🟡 Worth fixing
assignCardis attached twice: once byonsubmitin the HTML and once byaddEventListenerin the JS. A successful submit still sends one request because of the disabled-button guard. When validation fails, though, the alert shows twice, because the first call turns the button back on before the second call runs. This was already ondev, but the new Seva Kutir check also hits it. Inline →
Still to check with the backend (from the last pass)
updateCardsendsdepartment: nullwhen a card moves away fromSEVA KUTIR. Make sure the backend reads this as "clear it".- On the update screen, a typed reference phone is ignored when the reference card field also has a value. The placeholder says so, but staff aren't warned if they fill in both.
👍 Looks good
refPhone.jshandles 404 separately from other errors, waits for a pause in typing, and drops answers for an old number.- Hidden fields are only sent for the matching type on both screens.
escapeHtml()is used on every option built withinnerHTML.loadDepartmentsusestextContent/new Option.
Apart from item 1, the PR looks ready once SRATRC/aashray-backend#342 is deployed.
This was a read-only review. I didn't run a linter or tests because the repo has neither for these static pages.
| maxlength="10" | ||
| pattern="\d{10}" | ||
| title="Enter a 10 digit phone number" |
There was a problem hiding this comment.
A hidden phone that fails pattern blocks the save with no message. This comes from the latest commit.
How to reproduce: choose GUEST, type 12345 in this field, then switch to MUMUKSHU/PR/SEVA KUTIR and press Submit. #guestFields is hidden with display: none, but its input still has an invalid value, so the browser blocks the submit. Because the field can't be focused, Chrome shows no tooltip. It only logs "An invalid form control … is not focusable" to the console. Staff see a Submit button that does nothing.
updateCard.html:201 has the same problem, through .guest-only.
Fix: turn the guest inputs off while they are hidden, because disabled controls are skipped by validation. In toggleResStatusFields in createCard.js:
document.getElementById('reference_phone').disabled = res_status !== 'GUEST';In updateCard.js → updateGuestFields():
document.getElementById('referencePhone').disabled = !isGuest;(The submit code already reads these values only for GUEST, so disabling them doesn't change what gets sent.)
There was a problem hiding this comment.
Applied in 677e965. Re-tested in a browser: a guest with 12345, switched to Mumukshu, saves on create and on update, and validation alerts show once.
| @@ -45,14 +47,26 @@ | |||
| <div class="form"> | |||
| <form id="cardForm" onsubmit="assignCard(event)"> | |||
There was a problem hiding this comment.
assignCard runs twice on each submit. It is called by this onsubmit attribute and also by addEventListener('submit', assignCard) in createCard.js:2. This was already true on dev, but this PR adds a new validation branch that makes it visible:
- If the data is valid, the second call returns early because of the
submitBtn.disabledguard, so only one request is sent. That case is fine. - If validation fails (a
GUESTwithout a phone or type, or aSEVA KUTIRwithout a department), the first call shows the alert, turns the button back on, and returns. The second call then gets past the guard and shows the same alert again.
Keep only one of the two. The JS listener is the simpler one to keep:
| <form id="cardForm" onsubmit="assignCard(event)"> | |
| <form id="cardForm"> |
There was a problem hiding this comment.
Applied in 677e965. Re-tested in a browser: a guest with 12345, switched to Mumukshu, saves on create and on update, and validation alerts show once.
- Guest-only inputs (reference phone, guest type, reference card) are disabled while hidden, so a half-typed phone left in a hidden field no longer blocks Submit silently. - The create form no longer has an inline onsubmit as well as the JS listener, so a validation alert shows once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Claude finished @vendz's task in 29s —— View job PR Review (fourth pass, after commit
|
| Earlier finding | Status |
|---|---|
| A hidden invalid reference phone blocked Submit with no message | ✅ On create, toggleResStatusFields turns off #reference_phone and #guest_type unless the type is GUEST (createCard.js:128-131). On update, updateGuestFields does the same for #referenceCardno and #referencePhone (updateCard.js:197-199). The browser skips turned-off fields when it checks the form, so a half-typed number left in a hidden field no longer blocks Submit. |
assignCard ran twice on each submit |
✅ The onsubmit attribute is gone from createCard.html:48. Only the listener at createCard.js:2 is left, so a failed check shows its alert once. |
Checked for side effects
- Turned-off fields and the values sent: both submit functions read the guest fields only when the type is
GUEST, and those fields are on in that case. A turned-off field's value can still be read in JS anyway, so nothing that gets sent changes. - Update screen, Show and fill in the guest fields when staff edit a guest card #202 guest rules:
reference.requiredis still set only whenisGuest, so turning the field off when the card isn't a guest doesn't change the rule that the host can't be cleared.guestTypestill follows!isGuest || !hasReference, the same as before. - Switching back to
GUEST: the fields are turned back on, and any typed value is still there.updateGuestFieldsruns on everyres_statuschange, so the state stays correct.
New issues
None. I didn't post any inline comments this time.
Still to check before merging
- Feat/signup form aashray-backend#342 has to be deployed first. The new lookup, the card number the backend now makes, and
department: nullall depend on it. The last comment from @vendz confirms the backend clears the department when it getsnull. - On the update screen, a typed reference phone is ignored when the reference card field also has a value. The placeholder says so, but staff aren't warned if they fill in both. This is a small UX point and doesn't block the merge.
👍 Looks good
The PR looks ready to merge once the backend is deployed.
I only read the code for this review. I didn't run a linter or tests, because the repo has neither for these static pages.
· feat/signup-form
Update 3 Oct 2026: brought up to date and fixed (not merged)
devmerged in. The card edit screen keeps dev's Show and fill in the guest fields when staff edit a guest card #202 guest rules: the host is shown, can't be cleared, and the type is kept.res.ok, is debounced, ignores stale answers, and shows the host as text. It warns when the host isn't a Mumukshu.