ReactJS: Firebase timestamp converting with MobileDatePicker - reactjs

Am using MobileDatePicker to set a date (chosen by user) and saving that as a timestamp within Firebase.
For the adding of the record it is working fine:
<MobileDatePicker
label={"Date From"}
inputFormat="dd/MM/yyyy"
closeOnSelect={true}
value={datefrom}
minDate={new Date()}
views={['day']}
onChange={(newValue) => {
setDateFrom(format(newValue, 'MM/dd/yyyy')); }}
renderInput={(params) => <TextField variant="outlined" {...params} fullWidth />}
/>
firebase
.firestore()
.collection("collection")
.doc(tripid)
.set({
DateFrom: firebase.firestore.Timestamp.fromDate(new Date(datefrom))
...
When they update the collection, this is where I am having problems - I convert the timestamp into a date that MobileDatePicker understands and displays properly but am having issues then allowing the user to update the date and re-save it back as a timestamp field.
<MobileDatePicker
style={{ width: '100%' }}
label={"Date From"}
inputFormat="dd/MM/yyyy"
disablePast
views={['day']}
value={item.DateFrom == '' ? item.DateFrom : new Date(item.DateFrom.toDate()).toISOString()}
onChange={handleChangeFrom}
renderInput={(params) => <TextFields {...params} fullWidth />}
/>
const handleChangeFrom = (event) => {
setItem({ ...item, DateFrom: firebase.firestore.Timestamp.fromDate(new Date(item.DateFrom)) });
settogles(true)
};
I get the error: RangeError: Invalid time value
I think it has something to do with the handleChangeFrom function (so it is not assigning a time) ... but have no idea how to then convert it back to a timestamp that firebase understands.

Related

Convert DatePicker value in material ui

I have a form with a datePicker input from material-ui
When I pick a date and submit my form, if I console.log my data I will receive 'Tue Jan 10 2023 10:31:48 GMT+0100 (Central European Standard Time)' i want to received '01/11/2023'
I would like to format the data received.
Here my DatePicker :
const [deadLine, setDeadLine] = useState(new Date());
<Controller
name="deadLine"
defaultValue={deadLine}
control={control}
render={({ field: { onChange, ...restField } }) => (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Request Date"
onChange={(event) => {
console.log(event);
onChange(event);
setDeadLine(event);
}}
renderInput={(params) => <TextField {...params} />}
{...restField}
/>
</LocalizationProvider>
)}
/>
I have already tried :
To Use "format="dd-MM-yyyy"" from material-ui, but it only change the
visual not the data received.
To use moment with
setDeadLine(moment(event.target.value).format('MM/DD/YYYY'));,
still received the initial value.
To know : I am using react-hook-form
First of all to use moment you should wrap you DatePicker with moment Adapter:
<LocalizationProvider dateAdapter={AdapterMoment}> more also, onChange return the value you shouldn't access it with event.target.value, but you have directly the value with moment(value).format('DD/MM/YYYY') and of course don't forget to import Adapter Moment - import { AdapterMoment } from '#mui/x-date-pickers/AdapterMoment';
It should look like that:
<LocalizationProvider dateAdapter={AdapterMoment}>
<DatePicker
label="Request Date"
onChange={(value) => {
setDeadLine(moment(value).format('DD/MM/YYYY'));
}}
renderInput={(params) => <TextField {...params} />}
{...restField}
/>
</LocalizationProvider>

Disable manual input for MUI TimePicker

I have a bit of a custom TimePicker provided from Material UI. I add an ability for the user to select only whole hours, such as 15:00, 16:00 etc. by clicking on a clock icon
What I want to achieve is to add same for manual input of the text field. For now user can manually add any valid time, for example 14:34, which is incorrect for my case
Can anyone help me?
Here is my TimePicker:
<LocalizationProvider dateAdapter={AdapterDateFns} locale={locale}>
<TimePicker
ampm={false}
openTo="hours"
views={['hours']}
inputFormat="HH:mm"
mask="__:__"
value={dayStartValue}
InputAdornmentProps={{
position: 'start',
}}
components={{
OpenPickerIcon: ClockFilledIcon,
}}
onChange={(newValue) => {
setDayStartValue(newValue)
}}
renderInput={(params) =>
<TextField
{...params}
helperText="Input start of Day time"
/>
}
/>
</LocalizationProvider>
You can control and validate the user's input when he clicks away from the time picker by using onBlur inside InputProps.
import * as React from "react";
import TextField from "#mui/material/TextField";
import { AdapterDateFns } from "#mui/x-date-pickers/AdapterDateFns";
import { LocalizationProvider } from "#mui/x-date-pickers/LocalizationProvider";
import { TimePicker } from "#mui/x-date-pickers/TimePicker";
export default function BasicTimePicker() {
const [dayStartValue, setDayStartValue] = React.useState<Date | null>(null);
return (
<LocalizationProvider
dateAdapter={AdapterDateFns}
locale={locale}
>
<TimePicker
ampm={false}
openTo="hours"
views={["hours"]}
inputFormat="HH:mm"
mask="__:__"
value={dayStartValue}
InputAdornmentProps={{
position: "start"
}}
components={{
OpenPickerIcon: ClockFilledIcon,
}}
onChange={(newValue: Date, keyboardInputValue?: string) => {
setDayStartValue(newValue);
}}
renderInput={(params) => (
<TextField {...params} helperText="Input start of Day time" />
)}
InputProps={{
onBlur: (
e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement, Element>
) => {
const newDate = new Date(dayStartValue);
newDate.setMinutes(0);
if (e.target.value && e.target.value.length === 5) {
setDayStartValue(newDate);
}
}
}}
/>
</LocalizationProvider>
);
}
For the validation we check the user input and if it's a valid Date (5 characters length), we create a new Date with that and set the minutes to 0.
Code Sandbox example
You can change your input format and the mask to only accept whole hours like so:
<LocalizationProvider dateAdapter={AdapterDayjs}>
<TimePicker
ampm={false}
openTo="hours"
views={['hours']}
inputFormat="HH:00"
mask="__:00"
value={value}
InputAdornmentProps={{
position: 'start',
}}
onChange={(newValue) => {
setValue(newValue)
}}
renderInput={(params) =>
<TextField
{...params}
helperText="Input start of Day time"
/>
}
/>
</LocalizationProvider>
sandbox

ReactJS/Firebase: How to set a timestamp field to null/undefined?

I want the ability for users not to select a date.
<LocalizationProvider dateAdapter={AdapterDateFns}>
<MobileDatePicker
label={"Date From"}
inputFormat="dd/MM/yyyy"
disablePast={true}
value={datefrom}
minDate={new Date()}
onChange={(newValue) => {
setDateFrom(format(newValue, 'MM/dd/yyyy'));
if (dateto == '') {
setDateTo(format(newValue, 'MM/dd/yyyy'));
}
}}
renderInput={(params) => <TextField variant="outlined" {...params} helperText={null} fullWidth />}
/>
I thought this would work:
<Button onClick={(newValue) => {setDateFrom(null);}}>CLEAR DATES</Button>
And while it clears the date, it does not allow me to save a blank field
If you only want to ignore undefined value for this case, you can conditionally spread object.
Something like this should work.
await setDoc(docRef, {
mandatoryField: "value",
...(dateTo === "" ? {} : {timeStamp: dateTo} )
})
If you want to ignore all undefined value for future usage you can initialize firestore setting as below.
import { getFirestore, initializeFirestore } from 'firebase/firestore'; // Firebase v9+
// Must be called before getFirestore()
initializeFirestore(app, {
ignoreUndefinedProperties: true
});
const firestore = getFirestore(app);

Material UI Date Picker: Allow null or empty value when saving as timestamp to Firebase

I am using the following in ReactJS:
<LocalizationProvider dateAdapter={AdapterDateFns}>
<MobileDatePicker
label={"Date From"}
inputFormat="dd/MM/yyyy"
closeOnSelect={true}
value={datefrom}
minDate={new Date()}
views={['day']}
onChange={(newValue) => {
setDateFrom(format(newValue, 'MM/dd/yyyy'));
if (dateto == '') {
setDateTo(format(newValue, 'MM/dd/yyyy'));
}
}}
renderInput={(params) => <TextField variant="outlined" {...params} fullWidth />}
/>
</LocalizationProvider>
I want to allow a null or empty date - but the record does not save and the console returns the error :
Uncaught RangeError: Invalid time value at Date.toISOString ()
How do I allow a null or empty date - this field is being saved as a timestamp to Firebase.

Setting "DD/MM/YYYY" format in MUI

In my MUI form I'm using DatePicker to select dates for departure and return of users. But when I used .toLocaleDateString() it set my dates in mm-dd-yyyy format. But I wanted to set them as dd-mm-yyyy format. For that I've passed 'en-GB' as parameter in .toLocaleDateString(). But in the DatePicker TextField it shows a red border Like this and the pre-defined date (from the state) are also gone. Without the parameter it shows This which is the mm-dd-yyyy format. From the similar previous questions I even tried it through moment package and set the dt variable as moment(new Date()).format("DD/MM/YYYY") but it is still showing red border around the text field. I know this question was asked a lot of times before and I went through all of them and got no solution.
Date picker
const dt = new Date().toLocaleDateString();
console.log(dt);
const [formData, setFormData] = useState({
from: "",
to: "",
depart: dt,
return: dt,
noOfPassenger: 1,
tripType: "Return",
});
{/* App component */}
<div className="dates">
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Departure Date"
disablePast
onChange={(e) =>
setFormData({
...formData,
depart: e.toLocaleDateString(),
})
}
value={formData.depart}
renderInput={(params) => <TextField {...params} required />}
/>
</LocalizationProvider>
</div>
In my case I used a DateRangePicker, but it's the same across all pickers.
Just set the inputFormat like here:
<DateRangePicker
startText={t`From`}
endText={t`To`}
value={dateValue}
inputFormat="dd.MM.yyyy"
onChange={(newValue) => {
setDateValue(newValue);
console.log(newValue);
}}
renderInput={(startProps, endProps) => (
<React.Fragment>
<TextField {...startProps} />
<TextField {...endProps} />
</React.Fragment>
)}
/>
Emanuel Avram has already given a good answer. I will just add.
The value (date) of the component must have the same format defined internally, so it's no use using "toLocaleString()" without changing the internal format. Use the "inputFormat" property.
// "dd" = "Su", "Mo", ..., "Sa"
// "DD" = "01", "02", ..., "31"
inputFormat="dd/MM/YYYY" // Tu/09/2022
inputFormat="DD/MM/YYYY" // 09/13/2022
<DatePicker
label="Departure Date"
inputFormat="DD-MM-YYYY" // 13-09-2022
onChange={(e) =>
setFormData({
...formData,
depart: e.toLocaleDateString(),
})
}
value={formData.depart}
renderInput={(params) => <TextField {...params} required />}
/>

Resources