Skip to content

Commit d971980

Browse files
committed
Fixed minute form bug
1 parent 99054f7 commit d971980

5 files changed

Lines changed: 96 additions & 28 deletions

File tree

frontend/src/components/RequestRideModal/requestridemodal.module.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,34 @@
666666
color: #757575;
667667
}
668668

669+
.timeInputWrapper {
670+
display: flex;
671+
flex-direction: column;
672+
gap: 6px;
673+
width: 100%;
674+
align-self: stretch;
675+
}
676+
677+
.timeInputContainerError {
678+
border-color: #c62828;
679+
box-shadow: 0 0 0 2px rgba(198, 40, 40, 0.12);
680+
}
681+
682+
.timeInputContainerError:focus-within {
683+
border-color: #c62828;
684+
box-shadow: 0 0 0 2px rgba(198, 40, 40, 0.2);
685+
}
686+
687+
.timeInputError {
688+
margin: 0;
689+
padding: 0;
690+
color: #c62828;
691+
font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
692+
font-size: 13px;
693+
font-weight: 400;
694+
line-height: 1.35;
695+
}
696+
669697
/* Time slot chips (pickup/dropoff) */
670698
.timeSlotsContainer {
671699
display: flex;

frontend/src/components/RequestRideModal/steps/DropoffLocationStep.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,18 @@ const DropoffLocationStep: React.FC<DropoffLocationStepProps> = ({
6666
}
6767
}, [watchDropoffTime]);
6868

69-
// Update form value when time fields change
69+
// Update form value only when minute is complete and valid (2 digits, 00–59)
70+
const timeMinuteNum = timeMinute.length === 2 ? parseInt(timeMinute, 10) : null;
71+
const timeMinuteInvalid = timeMinuteNum !== null && (Number.isNaN(timeMinuteNum) || timeMinuteNum > 59);
7072
useEffect(() => {
71-
if (timeHour && timeMinute) {
73+
if (timeHour && timeMinute.length === 2 && !timeMinuteInvalid) {
7274
const hour24 = timePeriod === 'AM'
7375
? (timeHour === '12' ? 0 : parseInt(timeHour, 10))
7476
: (timeHour === '12' ? 12 : parseInt(timeHour, 10) + 12);
7577
const timeString = `${hour24.toString().padStart(2, '0')}:${timeMinute.padStart(2, '0')}`;
7678
setValue('dropoffTime', timeString);
7779
}
78-
}, [timeHour, timeMinute, timePeriod, setValue]);
80+
}, [timeHour, timeMinute, timePeriod, setValue, timeMinuteInvalid]);
7981

8082
// Close dropdown when clicking outside
8183
useEffect(() => {
@@ -329,7 +331,8 @@ const DropoffLocationStep: React.FC<DropoffLocationStepProps> = ({
329331
</div>
330332

331333
{/* Time input - Figma-style */}
332-
<div className={styles.timeInputContainer}>
334+
<div className={styles.timeInputWrapper}>
335+
<div className={`${styles.timeInputContainer} ${timeMinuteInvalid ? styles.timeInputContainerError : ''}`}>
333336
<input
334337
type="text"
335338
className={styles.timeInputHour}
@@ -351,15 +354,15 @@ const DropoffLocationStep: React.FC<DropoffLocationStepProps> = ({
351354
placeholder="00"
352355
value={timeMinute}
353356
onChange={(e) => {
354-
const value = e.target.value.replace(/\D/g, '');
355-
if (value === '' || (parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59)) {
356-
setTimeMinute(value.slice(0, 2));
357-
}
357+
const value = e.target.value.replace(/\D/g, '').slice(0, 2);
358+
setTimeMinute(value);
358359
}}
359360
onBlur={() => {
360-
if (timeMinute && timeMinute.length === 1) {
361+
if (timeMinute === '') return;
362+
if (timeMinute.length === 1) {
361363
setTimeMinute(timeMinute.padStart(2, '0'));
362364
}
365+
/* When minutes > 59 we leave the value so the user can correct it; error is shown below */
363366
}}
364367
maxLength={2}
365368
aria-label="Minute"
@@ -386,6 +389,12 @@ const DropoffLocationStep: React.FC<DropoffLocationStepProps> = ({
386389
<polyline points="12 6 12 12 16 14" />
387390
</svg>
388391
</span>
392+
</div>
393+
{timeMinuteInvalid && (
394+
<p className={styles.timeInputError} role="alert">
395+
Please enter minutes between 00 and 59. You can correct it above.
396+
</p>
397+
)}
389398
</div>
390399
</div>
391400
</div>

frontend/src/components/RequestRideModal/steps/PickupLocationStep.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,18 @@ const PickupLocationStep: React.FC<PickupLocationStepProps> = ({
6363
}
6464
}, [watchPickupTime]);
6565

66-
// Update form value when time fields change
66+
// Update form value only when minute is complete and valid (2 digits, 00–59)
67+
const timeMinuteNum = timeMinute.length === 2 ? parseInt(timeMinute, 10) : null;
68+
const timeMinuteInvalid = timeMinuteNum !== null && (Number.isNaN(timeMinuteNum) || timeMinuteNum > 59);
6769
useEffect(() => {
68-
if (timeHour && timeMinute) {
70+
if (timeHour && timeMinute.length === 2 && !timeMinuteInvalid) {
6971
const hour24 = timePeriod === 'AM'
7072
? (timeHour === '12' ? 0 : parseInt(timeHour, 10))
7173
: (timeHour === '12' ? 12 : parseInt(timeHour, 10) + 12);
7274
const timeString = `${hour24.toString().padStart(2, '0')}:${timeMinute.padStart(2, '0')}`;
7375
setValue('pickupTime', timeString);
7476
}
75-
}, [timeHour, timeMinute, timePeriod, setValue]);
77+
}, [timeHour, timeMinute, timePeriod, setValue, timeMinuteInvalid]);
7678

7779
// Close dropdown when clicking outside
7880
useEffect(() => {
@@ -317,7 +319,8 @@ const PickupLocationStep: React.FC<PickupLocationStepProps> = ({
317319
</div>
318320

319321
{/* Time input - Figma-style */}
320-
<div className={styles.timeInputContainer}>
322+
<div className={styles.timeInputWrapper}>
323+
<div className={`${styles.timeInputContainer} ${timeMinuteInvalid ? styles.timeInputContainerError : ''}`}>
321324
<input
322325
type="text"
323326
className={styles.timeInputHour}
@@ -339,15 +342,15 @@ const PickupLocationStep: React.FC<PickupLocationStepProps> = ({
339342
placeholder="00"
340343
value={timeMinute}
341344
onChange={(e) => {
342-
const value = e.target.value.replace(/\D/g, '');
343-
if (value === '' || (parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59)) {
344-
setTimeMinute(value.slice(0, 2));
345-
}
345+
const value = e.target.value.replace(/\D/g, '').slice(0, 2);
346+
setTimeMinute(value);
346347
}}
347348
onBlur={() => {
348-
if (timeMinute && timeMinute.length === 1) {
349+
if (timeMinute === '') return;
350+
if (timeMinute.length === 1) {
349351
setTimeMinute(timeMinute.padStart(2, '0'));
350352
}
353+
/* When minutes > 59 we leave the value so the user can correct it; error is shown below */
351354
}}
352355
maxLength={2}
353356
aria-label="Minute"
@@ -374,6 +377,12 @@ const PickupLocationStep: React.FC<PickupLocationStepProps> = ({
374377
<polyline points="12 6 12 12 16 14" />
375378
</svg>
376379
</span>
380+
</div>
381+
{timeMinuteInvalid && (
382+
<p className={styles.timeInputError} role="alert">
383+
Please enter minutes between 00 and 59. You can correct it above.
384+
</p>
385+
)}
377386
</div>
378387
</div>
379388
</div>

frontend/src/components/RequestRideModal/steps/RequestSummaryStep.tsx

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,13 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
109109
}, [editingSection, dropoffTime]);
110110

111111
// Write pickup time back to form
112+
const pickupMinuteNum = pickupTimeMinute.length === 2 ? parseInt(pickupTimeMinute, 10) : null;
113+
const pickupMinuteInvalid = pickupMinuteNum !== null && (Number.isNaN(pickupMinuteNum) || pickupMinuteNum > 59);
114+
const dropoffMinuteNum = dropoffTimeMinute.length === 2 ? parseInt(dropoffTimeMinute, 10) : null;
115+
const dropoffMinuteInvalid = dropoffMinuteNum !== null && (Number.isNaN(dropoffMinuteNum) || dropoffMinuteNum > 59);
116+
112117
useEffect(() => {
113-
if (editingSection === 'pickup' && pickupTimeHour && pickupTimeMinute) {
118+
if (editingSection === 'pickup' && pickupTimeHour && pickupTimeMinute.length === 2 && !pickupMinuteInvalid) {
114119
const hour24 =
115120
pickupTimePeriod === 'AM'
116121
? pickupTimeHour === '12'
@@ -126,7 +131,7 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
126131

127132
// Write dropoff time back to form
128133
useEffect(() => {
129-
if (editingSection === 'dropoff' && dropoffTimeHour && dropoffTimeMinute) {
134+
if (editingSection === 'dropoff' && dropoffTimeHour && dropoffTimeMinute.length === 2 && !dropoffMinuteInvalid) {
130135
const hour24 =
131136
dropoffTimePeriod === 'AM'
132137
? dropoffTimeHour === '12'
@@ -380,7 +385,8 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
380385
)}
381386
</div>
382387
<h3 className={`${styles.summarySectionLabel} ${styles.summarySectionLabelAfterValue}`}>Pickup Time</h3>
383-
<div className={styles.timeInputContainer}>
388+
<div className={styles.timeInputWrapper}>
389+
<div className={`${styles.timeInputContainer} ${pickupMinuteInvalid ? styles.timeInputContainerError : ''}`}>
384390
<input
385391
type="text"
386392
className={styles.timeInputHour}
@@ -399,11 +405,12 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
399405
placeholder="00"
400406
value={pickupTimeMinute}
401407
onChange={(e) => {
402-
const v = e.target.value.replace(/\D/g, '');
403-
if (v === '' || (parseInt(v, 10) >= 0 && parseInt(v, 10) <= 59)) setPickupTimeMinute(v.slice(0, 2));
408+
const v = e.target.value.replace(/\D/g, '').slice(0, 2);
409+
setPickupTimeMinute(v);
404410
}}
405411
onBlur={() => {
406-
if (pickupTimeMinute && pickupTimeMinute.length === 1) setPickupTimeMinute(pickupTimeMinute.padStart(2, '0'));
412+
if (pickupTimeMinute === '') return;
413+
if (pickupTimeMinute.length === 1) setPickupTimeMinute(pickupTimeMinute.padStart(2, '0'));
407414
}}
408415
maxLength={2}
409416
/>
@@ -424,6 +431,12 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
424431
</button>
425432
</div>
426433
</div>
434+
{pickupMinuteInvalid && (
435+
<p className={styles.timeInputError} role="alert">
436+
Please enter minutes between 00 and 59. You can correct it above.
437+
</p>
438+
)}
439+
</div>
427440
<button
428441
type="button"
429442
className={styles.summaryDoneButton}
@@ -501,7 +514,8 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
501514
)}
502515
</div>
503516
<h3 className={`${styles.summarySectionLabel} ${styles.summarySectionLabelAfterValue}`}>Dropoff Time</h3>
504-
<div className={styles.timeInputContainer}>
517+
<div className={styles.timeInputWrapper}>
518+
<div className={`${styles.timeInputContainer} ${dropoffMinuteInvalid ? styles.timeInputContainerError : ''}`}>
505519
<input
506520
type="text"
507521
className={styles.timeInputHour}
@@ -520,11 +534,12 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
520534
placeholder="00"
521535
value={dropoffTimeMinute}
522536
onChange={(e) => {
523-
const v = e.target.value.replace(/\D/g, '');
524-
if (v === '' || (parseInt(v, 10) >= 0 && parseInt(v, 10) <= 59)) setDropoffTimeMinute(v.slice(0, 2));
537+
const v = e.target.value.replace(/\D/g, '').slice(0, 2);
538+
setDropoffTimeMinute(v);
525539
}}
526540
onBlur={() => {
527-
if (dropoffTimeMinute && dropoffTimeMinute.length === 1) setDropoffTimeMinute(dropoffTimeMinute.padStart(2, '0'));
541+
if (dropoffTimeMinute === '') return;
542+
if (dropoffTimeMinute.length === 1) setDropoffTimeMinute(dropoffTimeMinute.padStart(2, '0'));
528543
}}
529544
maxLength={2}
530545
/>
@@ -545,6 +560,12 @@ const RequestSummaryStep: React.FC<RequestSummaryStepProps> = ({
545560
</button>
546561
</div>
547562
</div>
563+
{dropoffMinuteInvalid && (
564+
<p className={styles.timeInputError} role="alert">
565+
Please enter minutes between 00 and 59. You can correct it above.
566+
</p>
567+
)}
568+
</div>
548569
<button
549570
type="button"
550571
className={styles.summaryDoneButton}

server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"lint": "eslint . --ext .js --ext .jsx --ext .ts --ext .tsx --fix",
4343
"type-check": "tsc --pretty --noEmit",
4444
"test": "cross-env NODE_ENV=test mocha -r ts-node/register \"tests/**/*.ts\" --exit --timeout 30000",
45+
"seed": "ts-node src/scripts/seed-locations.ts",
4546
"test:watch": "cross-env NODE_ENV=test nodemon --watch . --exec 'mocha -r ts-node/register \"tests/**/*.ts\" --timeout 10000' --ext ts"
4647
},
4748
"repository": {

0 commit comments

Comments
 (0)