Skip to content

Commit f5df158

Browse files
authored
Merge pull request #1943 from tekdi/release-1.11.0
Release 1.11.0 to teacher prod
2 parents 2b32111 + 68cc169 commit f5df158

File tree

3 files changed

+300
-169
lines changed

3 files changed

+300
-169
lines changed

apps/admin-app-repo/src/components/DynamicForm/RJSFWidget/CustomDateWidget.tsx

Lines changed: 94 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @ts-nocheck
22
import React, { useState, useEffect } from 'react';
3-
import { TextField } from '@mui/material';
3+
import { TextField, IconButton } from '@mui/material';
44
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
55
import { LocalizationProvider, DatePicker } from '@mui/x-date-pickers';
66
import { CalendarToday } from '@mui/icons-material';
@@ -14,13 +14,23 @@ const CustomDateWidget = ({
1414
required,
1515
label,
1616
rawErrors = [],
17-
uiSchema = {}, // <-- include uiSchema
17+
uiSchema = {},
1818
}: any) => {
1919
const { minValue, maxValue } = options;
2020
const { t } = useTranslation();
2121

2222
const isDisabled = uiSchema?.['ui:disabled'] === true;
2323

24+
const [isMobile, setIsMobile] = useState(false);
25+
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
26+
27+
useEffect(() => {
28+
const checkMobile = () => setIsMobile(window.innerWidth <= 768);
29+
checkMobile();
30+
window.addEventListener('resize', checkMobile);
31+
return () => window.removeEventListener('resize', checkMobile);
32+
}, []);
33+
2434
const initialValue =
2535
typeof value === 'string' && value.match(/^\d{4}-\d{2}-\d{2}$/)
2636
? value
@@ -49,19 +59,22 @@ const CustomDateWidget = ({
4959
}
5060
};
5161

52-
// Filter out 'is a required property' messages
53-
const displayErrors = rawErrors?.filter(
62+
const handleCalendarIconClick = () => setIsCalendarOpen(true);
63+
const handleCalendarClose = () => setIsCalendarOpen(false);
64+
65+
const displayErrors = rawErrors.filter(
5466
(error) => !error.toLowerCase().includes('required')
55-
) || [];
56-
57-
const errorText = displayErrors.length > 0 ? displayErrors[0] : '';
67+
);
68+
const errorText = displayErrors?.[0] || '';
69+
const hasError =
70+
displayErrors && displayErrors.length > 0 && errorText.trim() !== '';
5871

5972
return (
60-
<>
61-
<input
73+
<LocalizationProvider dateAdapter={AdapterDayjs} required={required}>
74+
<input
6275
value={value ?? ''}
6376
required={required}
64-
onChange={() => { /* no-op */ }}
77+
onChange={() => {}}
6578
tabIndex={-1}
6679
style={{
6780
height: 1,
@@ -73,55 +86,80 @@ const CustomDateWidget = ({
7386
marginTop="50px"
7487

7588
/>
76-
<LocalizationProvider dateAdapter={AdapterDayjs} required={required}>
77-
<DatePicker
78-
disabled={isDisabled}
79-
label={`${t(`FORM.${label}`, {
80-
defaultValue: label,
81-
})}`}
82-
value={selectedDate || null}
83-
onChange={handleDateChange}
84-
minDate={minValue ? dayjs(minValue, 'YYYY-MM-DD') : undefined}
85-
maxDate={maxValue ? dayjs(maxValue, 'YYYY-MM-DD') : undefined}
86-
format="DD-MM-YYYY"
87-
openTo="day"
88-
views={['year', 'month', 'day']}
89-
slotProps={{
90-
textField: {
91-
fullWidth: true,
92-
variant: 'outlined',
93-
error: displayErrors.length > 0,
94-
helperText: errorText,
95-
required,
96-
inputProps: {
89+
{isMobile ? (
90+
<>
91+
{/* Mobile TextField with calendar icon */}
92+
<TextField
93+
disabled={isDisabled}
94+
label={`${t(`FORM.${label}`, { defaultValue: label })}`}
95+
value={selectedDate ? selectedDate.format('DD-MM-YYYY') : ''}
96+
fullWidth
97+
variant="outlined"
98+
required={required}
99+
InputProps={{
97100
readOnly: true,
98-
},
99-
InputProps: {
100-
endAdornment: <CalendarToday />,
101-
},
102-
},
103-
popper: {
104-
placement: 'bottom-start',
105-
modifiers: [
106-
{
107-
name: 'preventOverflow',
108-
options: {
109-
boundary: 'viewport',
110-
},
101+
endAdornment: (
102+
<IconButton
103+
onClick={handleCalendarIconClick}
104+
disabled={isDisabled}
105+
edge="end"
106+
size="small"
107+
>
108+
<CalendarToday />
109+
</IconButton>
110+
),
111+
}}
112+
onClick={!isDisabled ? handleCalendarIconClick : undefined}
113+
error={hasError}
114+
helperText={hasError ? errorText : ''}
115+
/>
116+
117+
{/* Mobile DatePicker popup */}
118+
{isCalendarOpen && (
119+
<DatePicker
120+
open
121+
onClose={handleCalendarClose}
122+
disabled={isDisabled}
123+
value={selectedDate || null}
124+
onChange={(date) => {
125+
handleDateChange(date);
126+
handleCalendarClose();
127+
}}
128+
minDate={minValue ? dayjs(minValue, 'YYYY-MM-DD') : undefined}
129+
maxDate={maxValue ? dayjs(maxValue, 'YYYY-MM-DD') : undefined}
130+
format="DD-MM-YYYY"
131+
slotProps={{ popper: { placement: 'bottom-start' } }}
132+
renderInput={() => null}
133+
/>
134+
)}
135+
</>
136+
) : (
137+
// Desktop DatePicker
138+
<DatePicker
139+
disabled={isDisabled}
140+
label={`${t(`FORM.${label}`, { defaultValue: label })}`}
141+
value={selectedDate || null}
142+
onChange={handleDateChange}
143+
minDate={minValue ? dayjs(minValue, 'YYYY-MM-DD') : undefined}
144+
maxDate={maxValue ? dayjs(maxValue, 'YYYY-MM-DD') : undefined}
145+
format="DD-MM-YYYY"
146+
slotProps={{
147+
textField: {
148+
fullWidth: true,
149+
variant: 'outlined',
150+
error: hasError,
151+
helperText: hasError ? errorText : '',
152+
required,
153+
inputProps: {
154+
readOnly: true, // prevent manual typing
111155
},
112-
],
113-
},
114-
actionBar: {
115-
actions: ['clear', 'today', 'cancel', 'accept'],
116-
},
117-
}}
118-
required={required}
119-
/>
120-
156+
onKeyDown: (e) => e.preventDefault(), // block keyboard typing
157+
},
158+
}}
159+
required={required}
160+
/>
161+
)}
121162
</LocalizationProvider>
122-
{/* Hidden text input to force native validation */}
123-
124-
</>
125163
);
126164
};
127165

0 commit comments

Comments
 (0)