@material-ui/pickers#DatePicker TypeScript Examples
The following examples show how to use
@material-ui/pickers#DatePicker.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: DatePickerComponent.web.tsx From react-native-jigsaw with MIT License | 5 votes |
DatePickerComponent: React.FC<Props & { theme: typeof Theme }> = ({
value,
onChange,
mode,
toggleVisibility,
isVisible,
theme,
}) => {
const internalTheme = createMuiTheme({
palette: {
primary: {
main: theme?.colors?.primary ?? Theme.colors.primary,
},
secondary: {
main: theme?.colors?.secondary ?? Theme.colors.secondary,
},
},
});
const Picker =
mode === "date"
? DatePicker
: mode === "time"
? TimePicker
: DateTimePicker;
return (
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<ThemeProvider theme={internalTheme}>
<Picker
value={value}
open={isVisible}
onChange={(d) => {
toggleVisibility();
onChange(null, d);
}}
onClose={() => toggleVisibility()}
variant="dialog"
TextFieldComponent={() => null}
/>
</ThemeProvider>
</MuiPickersUtilsProvider>
);
}
Example #2
Source File: SQFormDatePicker.tsx From SQForm with MIT License | 4 votes |
function SQFormDatePicker({
name,
label,
size = 'auto',
isDisabled = false,
placeholder = '',
onBlur,
onChange,
setDisabledDate,
muiFieldProps,
muiTextInputProps = {},
isCalendarOnly = false,
InputProps,
InputAdornmentProps,
}: SQFormDatePickerProps): JSX.Element {
const {
formikField: {field, helpers},
fieldState: {isFieldError, isFieldRequired},
fieldHelpers: {handleBlur, HelperTextComponent},
} = useForm<Moment | null, Moment | null>({
name,
onBlur,
});
const handleChange = (date: Moment | null) => {
helpers.setValue(date);
onChange && onChange(date);
};
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
const handleClose = () => setIsCalendarOpen(false);
const toggleCalendar = () => setIsCalendarOpen(!isCalendarOpen);
const handleClickAway = () => {
if (isCalendarOpen) {
setIsCalendarOpen(false);
}
};
const classes = useStyles();
// An empty string will not reset the DatePicker so we have to pass null
const value = field.value || null;
return (
<ClickAwayListener onClickAway={handleClickAway}>
<Grid item sm={size}>
<DatePicker
label={label}
disabled={isDisabled}
shouldDisableDate={setDisabledDate}
value={value}
onChange={handleChange}
onClose={handleClose}
onOpen={toggleCalendar}
open={isCalendarOpen}
renderInput={(inputProps) => {
return (
<TextField
{...inputProps}
name={name}
color="primary"
error={isFieldError}
fullWidth={true}
inputProps={{...inputProps.inputProps, ...muiTextInputProps}}
InputLabelProps={{shrink: true}}
FormHelperTextProps={{error: isFieldError}}
helperText={!isDisabled && HelperTextComponent}
placeholder={placeholder}
onBlur={handleBlur}
required={isFieldRequired}
onClick={
isCalendarOnly && !isDisabled
? toggleCalendar
: handleClickAway
}
classes={classes}
/>
);
}}
InputProps={InputProps}
InputAdornmentProps={InputAdornmentProps}
{...muiFieldProps}
/>
</Grid>
</ClickAwayListener>
);
}
Example #3
Source File: SQFormDatePickerWithDateFNS.tsx From SQForm with MIT License | 4 votes |
function SQFormDatePickerWithDateFNS({
name,
label,
size = 'auto',
isDisabled = false,
placeholder = '',
onBlur,
onChange,
setDisabledDate,
muiFieldProps,
muiTextInputProps = {},
isCalendarOnly = false,
InputProps,
InputAdornmentProps,
}: SQFormDatePickerDateFNSProps): JSX.Element {
const {
formikField: {field, helpers},
fieldState: {isFieldError, isFieldRequired},
fieldHelpers: {handleBlur, HelperTextComponent},
} = useForm<Date | null, Date | null>({
name,
onBlur,
});
const handleChange = (date: Date | null) => {
helpers.setValue(date);
onChange && onChange(date);
};
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
const handleClose = () => setIsCalendarOpen(false);
const toggleCalendar = () => setIsCalendarOpen(!isCalendarOpen);
const handleClickAway = () => {
if (isCalendarOpen) {
setIsCalendarOpen(false);
}
};
const classes = useStyles();
// An empty string will not reset the DatePicker so we have to pass null
const value = field.value || null;
return (
<ClickAwayListener onClickAway={handleClickAway}>
<Grid item sm={size}>
<DatePicker
label={label}
disabled={isDisabled}
shouldDisableDate={setDisabledDate}
value={value}
onChange={handleChange}
onClose={handleClose}
onOpen={toggleCalendar}
open={isCalendarOpen}
renderInput={(inputProps) => {
return (
<TextField
{...inputProps}
name={name}
color="primary"
error={isFieldError}
fullWidth={true}
inputProps={{...inputProps.inputProps, ...muiTextInputProps}}
InputLabelProps={{shrink: true}}
FormHelperTextProps={{error: isFieldError}}
helperText={!isDisabled && HelperTextComponent}
placeholder={placeholder}
onBlur={handleBlur}
required={isFieldRequired}
onClick={
isCalendarOnly && !isDisabled
? toggleCalendar
: handleClickAway
}
classes={classes}
/>
);
}}
InputProps={InputProps}
InputAdornmentProps={InputAdornmentProps}
{...muiFieldProps}
/>
</Grid>
</ClickAwayListener>
);
}
Example #4
Source File: DateInput.tsx From halstack-react with Apache License 2.0 | 4 votes |
DxcDateInput = React.forwardRef<RefType, DateInputPropsType>(
(
{
label,
name,
defaultValue = "",
value,
format = "dd-MM-yyyy",
helperText,
placeholder = false,
clearable,
disabled,
optional,
onChange,
onBlur,
error,
autocomplete,
margin,
size,
tabIndex,
},
ref
) => {
const [innerValue, setInnerValue] = useState(defaultValue);
const [isOpen, setIsOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const colorsTheme = useTheme();
const translatedLabels = useTranslatedLabels();
const refDate = ref || useRef(null);
const handleCalendarOnKeyDown = (event) => {
switch (event.keyCode) {
case 27: // Esc
event.preventDefault();
setIsOpen(false);
break;
}
};
const handleCalendarOnClick = (newDate) => {
const newValue = dayjs(newDate).format(format.toUpperCase());
value ?? setInnerValue(newValue);
newDate?.toJSON()
? onChange?.({
value: newValue,
date: newDate,
})
: onChange?.({
value: newValue,
});
};
const handleIOnChange = ({ value: newValue, error: inputError }) => {
value ?? setInnerValue(newValue);
const dayjsDate = dayjs(newValue, format.toUpperCase(), true);
const invalidDateMessage =
newValue !== "" && !dayjsDate.isValid() && translatedLabels.dateInput.invalidDateErrorMessage;
const callbackParams =
inputError || invalidDateMessage
? { value: newValue, error: inputError || invalidDateMessage }
: { value: newValue };
dayjsDate.isValid()
? onChange?.({
...callbackParams,
date: dayjsDate.toDate(),
})
: onChange?.(callbackParams);
};
const handleIOnBlur = ({ value, error: inputError }) => {
const dayjsDate = dayjs(value, format.toUpperCase(), true);
const invalidDateMessage =
value !== "" && !dayjsDate.isValid() && translatedLabels.dateInput.invalidDateErrorMessage;
const callbackParams =
inputError || invalidDateMessage ? { value, error: inputError || invalidDateMessage } : { value };
dayjsDate.isValid()
? onBlur?.({
...callbackParams,
date: dayjsDate.toDate(),
})
: onBlur?.(callbackParams);
};
const openCalendar = () => {
const dateBtn = refDate.current.getElementsByTagName("button")[0];
setIsOpen(!isOpen);
setAnchorEl(dateBtn);
};
const closeCalendar = () => {
setIsOpen(false);
};
const calendarAction = {
onClick: openCalendar,
icon: (
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" fill="currentColor">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z" />
</svg>
),
};
const dateTheme = createMuiTheme({
overrides: {
MuiTypography: {
root: {
fontFamily: `${colorsTheme.dateInput.pickerFontFamily} !important`,
},
},
MuiPickersYearSelection: {
container: {
color: colorsTheme.dateInput.pickerYearFontColor,
"&::-webkit-scrollbar": {
width: "3px",
},
"&::-webkit-scrollbar-track": {
backgroundColor: "#D9D9D9",
borderRadius: "3px",
},
"&::-webkit-scrollbar-thumb": {
backgroundColor: "#666666",
borderRadius: "3px",
},
},
},
MuiPickersToolbar: {
toolbar: {
backgroundColor: colorsTheme.dateInput.pickerBackgroundColor,
color: colorsTheme.dateInput.pickerDayFontColor,
},
},
MuiIconButton: {
root: {
height: "36px",
width: "36px",
padding: "0px",
},
},
MuiTouchRipple: {
child: {
opacity: "0",
},
},
MuiButtonBase: {
root: {
"&:focus": {
outline: colorsTheme.dateInput.pickerFocusColor + " solid 2px",
},
},
},
MuiPickersBasePicker: {
pickerView: {
minWidth: "unset",
maxWidth: "unset",
minHeight: "unset",
padding: "0px 10px",
height: colorsTheme.dateInput.pickerHeight,
width: colorsTheme.dateInput.pickerWidth,
backgroundColor: colorsTheme.dateInput.pickerBackgroundColor,
fontFamily: colorsTheme.dateInput.pickerFontFamily,
},
},
MuiPickersToolbarText: {
toolbarTxt: {
color: colorsTheme.dateInput.pickerActualDateFontColor,
fontFamily: colorsTheme.dateInput.pickerFontFamily,
fontSize: "2rem",
},
toolbarBtnSelected: {
color: colorsTheme.dateInput.pickerActualDateFontColor,
},
},
MuiPickersCalendarHeader: {
transitionContainer: {
color: colorsTheme.dateInput.pickerMonthFontColor,
},
dayLabel: {
color: colorsTheme.dateInput.pickerWeekFontColor,
fontFamily: colorsTheme.dateInput.pickerFontFamily,
},
switchHeader: {
backgroundColor: "#ffffff",
color: colorsTheme.dateInput.pickerDayFontColor,
},
iconButton: {
backgroundColor: colorsTheme.dateInput.pickerMonthArrowsBackgroundColor,
"&:hover": {
backgroundColor: colorsTheme.dateInput.pickerMonthArrowsBackgroundColor,
},
},
},
MuiPickersCalendar: {
week: {
marginBottom: "2px",
},
},
MuiPickersDay: {
current: {
color: colorsTheme.dateInput.pickerDayFontColor,
},
day: {
fontFamily: colorsTheme.dateInput.pickerFontFamily,
color: colorsTheme.dateInput.pickerDayFontColor,
"&:hover": {
backgroundColor: colorsTheme.dateInput.pickerHoverDateBackgroundColor,
color: colorsTheme.dateInput.pickerHoverDateFontColor,
},
},
daySelected: {
backgroundColor: colorsTheme.dateInput.pickerSelectedDateBackgroundColor,
color: colorsTheme.dateInput.pickerSelectedDateColor,
"&:hover": {
backgroundColor: colorsTheme.dateInput.pickerSelectedDateBackgroundColor,
color: colorsTheme.dateInput.pickerSelectedDateColor,
opacity: "1",
},
},
},
MuiPickersYear: {
yearSelected: {
color: colorsTheme.dateInput.pickerSelectedDateColor,
backgroundColor: colorsTheme.dateInput.pickerSelectedDateBackgroundColor,
margin: "0px 100px",
borderRadius: "20px",
},
root: {
"&:focus": {
color: colorsTheme.dateInput.pickerHoverDateFontColor,
backgroundColor: colorsTheme.dateInput.pickerHoverDateBackgroundColor,
},
},
},
MuiPickersModal: {
dialogAction: {
color: "pink",
},
},
},
});
return (
<ThemeProvider theme={colorsTheme}>
<MuiThemeProvider theme={dateTheme}>
<MuiPickersUtilsProvider utils={DayjsUtils}>
<StyledDPicker>
<DxcTextInput
label={label}
name={name}
defaultValue={defaultValue}
value={value ?? innerValue}
helperText={helperText}
placeholder={placeholder ? format.toUpperCase() : null}
action={calendarAction}
clearable={clearable}
disabled={disabled}
optional={optional}
onChange={handleIOnChange}
onBlur={handleIOnBlur}
error={error}
autocomplete={autocomplete}
margin={margin}
size={size}
tabIndex={tabIndex}
ref={refDate}
/>
<Popover
onKeyDown={handleCalendarOnKeyDown}
open={isOpen}
anchorEl={anchorEl}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
PaperProps={{
style: {
marginTop: "10px",
},
}}
>
<ClickAwayListener onClickAway={closeCalendar}>
<Paper role="dialog" aria-modal="true">
<DatePicker
variant="static"
value={getValueForPicker(value ?? innerValue, format)}
onChange={(date) => handleCalendarOnClick(date)}
format={format}
disabled={disabled}
/>
</Paper>
</ClickAwayListener>
</Popover>
</StyledDPicker>
</MuiPickersUtilsProvider>
</MuiThemeProvider>
</ThemeProvider>
);
}
)
Example #5
Source File: Bookings.tsx From office-booker with MIT License | 4 votes |
Bookings: React.FC<RouteComponentProps> = () => {
// Global state
const { state, dispatch } = useContext(AppContext);
const { user } = state;
// Local state
const [loading, setLoading] = useState(true);
const [offices, setOffices] = useState<Office[] | undefined>();
const [selectedOffice, setSelectedOffice] = useState<Office | undefined>();
const [selectedDate, setSelectedDate] = useState(new Date());
const [dbBookings, setDbBookings] = useState<Booking[] | undefined>();
const [sortedBookings, setSortedBookings] = useState<Booking[] | undefined>();
const [sortBy, setSortBy] = useState<keyof Booking>('user');
const [sortOrder, setSortOrder] = useState<SortOrder>('asc');
const [bookingToCancel, setBookingToCancel] = useState<undefined | Booking>();
// Theme
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
// Helpers
const getAllBookings = useCallback(() => {
if (selectedOffice) {
getBookings({ office: selectedOffice, date: format(selectedDate, 'yyyy-MM-dd') })
.then((data) => setDbBookings(data))
.catch((err) => {
// Handle errors
setLoading(false);
dispatch({
type: 'SET_ALERT',
payload: {
message: formatError(err),
color: 'error',
},
});
});
}
}, [selectedOffice, selectedDate, dispatch]);
const findOffice = useCallback(
(name: OfficeWithSlots['name']) => offices && offices.find((o) => o.name === name),
[offices]
);
// Effects
useEffect(() => {
if (user) {
// Get all offices admin can manage
getOffices()
.then((data) =>
setOffices(
data.filter((office) =>
user.permissions.officesCanManageBookingsFor.find(
(userOffice) => userOffice.name === office.name
)
)
)
)
.catch((err) => {
// Handle errors
setLoading(false);
dispatch({
type: 'SET_ALERT',
payload: {
message: formatError(err),
color: 'error',
},
});
});
}
}, [user, dispatch]);
useEffect(() => {
if (user && offices && offices.length > 0 && !selectedOffice) {
// Retrieve first office user can manage bookings for
setSelectedOffice(user.permissions.officesCanManageBookingsFor[0]);
}
}, [user, offices, selectedOffice, findOffice]);
useEffect(() => {
if (selectedOffice) {
// Retrieve bookings
getAllBookings();
}
}, [dispatch, selectedOffice, selectedDate, getAllBookings]);
useEffect(() => {
if (dbBookings) {
// Sort it!
setSortedBookings(sortData([...dbBookings], sortBy, sortOrder));
}
}, [dbBookings, sortBy, sortOrder]);
useEffect(() => {
if (loading && sortedBookings) {
// Wait for local state to be ready
setLoading(false);
}
}, [loading, sortedBookings]);
// Handlers
const handleSort = (key: keyof Booking) => {
if (key === sortBy) {
setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc');
} else {
setSortBy(key);
}
};
const handleCancelBooking = (booking: Booking) => {
cancelBooking(booking.id, booking.user)
.then(() => {
// Clear selected booking
setBookingToCancel(undefined);
// Retrieve updated bookings
getAllBookings();
// Show confirmation alert
dispatch({
type: 'SET_ALERT',
payload: {
message: 'Booking cancelled',
color: 'success',
},
});
})
.catch((err) =>
dispatch({
type: 'SET_ALERT',
payload: {
message: formatError(err),
color: 'error',
},
})
);
};
// Render
if (!user) {
return null;
}
return (
<AdminLayout currentRoute="bookings">
<BookingStyles>
{loading ? (
<Loading />
) : (
<>
<h3>Bookings</h3>
<OurButton
startIcon={<AddCircleIcon />}
type="submit"
color="secondary"
onClick={() => navigate('/admin/booking')}
variant="contained"
size="small"
>
New Booking
</OurButton>
{selectedOffice && (
<Paper square className="table-container">
<div className="filter">
<FormControl className="filter-office">
<InputLabel id="office-label">Office</InputLabel>
<Select
labelId="office-label"
id="office"
value={selectedOffice.name}
onChange={(e) => setSelectedOffice(findOffice(e.target.value as string))}
>
{user.permissions.officesCanManageBookingsFor.map((office, index) => (
<MenuItem value={office.name} key={index}>
{office.name}
</MenuItem>
))}
</Select>
</FormControl>
<div className="filter-date">
<IconButton
onClick={() => setSelectedDate(addDays(new Date(selectedDate), -1))}
className="date-arrow"
>
<KeyboardArrowLeftIcon />
</IconButton>
<DatePicker
autoOk
disableToolbar
variant="inline"
label="Date"
format="dd/MM/yyyy"
value={selectedDate}
onChange={(date) => setSelectedDate(date as Date)}
className="date-picker"
/>
<IconButton
onClick={() => setSelectedDate(addDays(new Date(selectedDate), 1))}
className="date-arrow"
>
<KeyboardArrowRightIcon />
</IconButton>
</div>
</div>
<div className="total-bookings">
<InputLabel className="bookings-count-label">Bookings:</InputLabel>
<span>{sortedBookings && sortedBookings.length}</span>
</div>
<TableContainer className="table">
<Table>
<TableHead>
<TableRow>
<TableCell className="table-header">
<TableSortLabel
active={sortBy === 'user'}
direction={sortOrder}
onClick={() => handleSort('user')}
>
User
</TableSortLabel>
</TableCell>
{selectedOffice.parkingQuota > 0 && (
<TableCell className="table-header">
<TableSortLabel
active={sortBy === 'parking'}
direction={sortOrder}
onClick={() => handleSort('parking')}
>
Parking
</TableSortLabel>
</TableCell>
)}
<TableCell />
</TableRow>
</TableHead>
<TableBody>
{sortedBookings && sortedBookings.length > 0 ? (
sortedBookings.map((booking, index) => {
const parsedDate = parseISO(booking.date);
return (
<TableRow key={index}>
<TableCell>{booking.user}</TableCell>
{selectedOffice.parkingQuota > 0 && (
<TableCell>{booking.parking ? 'Yes' : 'No'}</TableCell>
)}
{isToday(parsedDate) || !isPast(parsedDate) ? (
<TableCell align="right">
<div className="btn-container">
<OurButton
type="submit"
variant="contained"
color="secondary"
size="small"
onClick={() => setBookingToCancel(booking)}
>
Cancel
</OurButton>
</div>
</TableCell>
) : (
<TableCell />
)}
</TableRow>
);
})
) : (
<TableRow>
<TableCell>No bookings found</TableCell>
{selectedOffice.parkingQuota > 0 && <TableCell />}
<TableCell />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</Paper>
)}
</>
)}
{bookingToCancel && (
<Dialog fullScreen={fullScreen} open={true} onClose={() => setBookingToCancel(undefined)}>
<DialogTitle>{'Are you sure you want to cancel this booking?'}</DialogTitle>
<DialogContent>
<DialogContentText>
Booking for <strong>{bookingToCancel.user}</strong> on{' '}
<strong>{format(parseISO(bookingToCancel.date), 'do LLLL')}</strong> for{' '}
<strong>{bookingToCancel.office.name}</strong>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => setBookingToCancel(undefined)} color="primary" autoFocus>
No
</Button>
<Button
autoFocus
onClick={() => handleCancelBooking(bookingToCancel)}
color="primary"
>
Yes
</Button>
</DialogActions>
</Dialog>
)}
</BookingStyles>
</AdminLayout>
);
}
Example #6
Source File: CreateBooking.tsx From office-booker with MIT License | 4 votes |
AdminCreateBooking: React.FC<RouteComponentProps> = () => {
// Global state
const { state, dispatch } = useContext(AppContext);
const { config, user } = state;
// Local state
const [loading, setLoading] = useState(false);
const [offices, setOffices] = useState<Office[] | undefined>();
const [selectedOffice, setSelectedOffice] = useState<Office | undefined>();
const [selectedOfficeWithSlots, setSelectedWithSlots] = useState<OfficeWithSlots | undefined>();
const [officeSlot, setOfficeSlot] = useState<OfficeSlot | undefined>();
const [bookingDate, setBookingDate] = useState(addDays(new Date(), +1));
const [email, setEmail] = useState('');
const [parking, setParking] = useState(false);
const [showReasonConfirmation, setShowReasonConfirmation] = useState(false);
const [bookingReason, setBookingReason] = useState<string | undefined>();
const [isAutoApprovedUser, setIsAutoApprovedUser] = useState<boolean>(false);
const [searchedUser, setSearchedUser] = useState<User | undefined>();
// Helpers
const findOffice = useCallback(
(name: OfficeWithSlots['name']) => offices && offices.find((o) => o.name === name),
[offices]
);
const handleFetchUser = () => {
setIsAutoApprovedUser(false);
if (user && config?.reasonToBookRequired) {
// Get selected user
getUser(email)
.then((searchedUser) => setSearchedUser(searchedUser))
.catch((err) => {
// Handle errors
setLoading(false);
dispatch({
type: 'SET_ALERT',
payload: {
message: formatError(err),
color: 'error',
},
});
});
}
};
// Effects
useEffect(() => {
if (user) {
// Get all offices user can manage
getOffices()
.then((data) =>
setOffices(
data.filter((office) =>
user.permissions.officesCanManageBookingsFor.find(
(userOffice) => userOffice.name === office.name
)
)
)
)
.catch((err) => {
// Handle errors
setLoading(false);
dispatch({
type: 'SET_ALERT',
payload: {
message: formatError(err),
color: 'error',
},
});
});
}
}, [dispatch, user]);
useEffect(() => {
if (user && offices && offices.length > 0 && !selectedOffice) {
// Retrieve first office user can manage bookings for
setSelectedOffice(findOffice(user.permissions.officesCanManageBookingsFor[0].name));
}
}, [user, offices, selectedOffice, findOffice]);
useEffect(() => {
if (selectedOffice === undefined) {
setSelectedWithSlots(undefined);
} else {
setLoading(true);
getOffice(selectedOffice.id).then((result) => {
setLoading(false);
setSelectedWithSlots(result);
});
}
}, [selectedOffice]);
useEffect(() => {
if (selectedOfficeWithSlots) {
setOfficeSlot(
selectedOfficeWithSlots.slots.find((s) => s.date === format(bookingDate, 'yyyy-MM-dd'))
);
}
}, [selectedOfficeWithSlots, bookingDate]);
useEffect(() => {
if (officeSlot) {
// Wait for everything to be ready
setLoading(false);
}
}, [officeSlot]);
useEffect(() => {
if (searchedUser?.autoApproved === true) {
setIsAutoApprovedUser(true);
}
}, [searchedUser?.autoApproved]);
// Handlers
const handleSubmit = (e?: React.FormEvent<HTMLFormElement>) => {
// Validation
if (email === '') {
return dispatch({
type: 'SET_ALERT',
payload: {
message: 'Email address required',
color: 'error',
},
});
}
if (!config || !validateEmail(config.emailRegex, email)) {
return dispatch({
type: 'SET_ALERT',
payload: {
message: 'Valid email address required',
color: 'error',
},
});
}
if (!selectedOffice || !officeSlot) {
return dispatch({
type: 'SET_ALERT',
payload: {
message: 'Office required',
color: 'error',
},
});
}
if (officeSlot.booked + 1 > selectedOffice.quota) {
return dispatch({
type: 'SET_ALERT',
payload: {
message: 'No office spaces available',
color: 'error',
},
});
}
if (
selectedOffice.parkingQuota > 0 &&
parking &&
officeSlot.bookedParking + 1 > selectedOffice.parkingQuota
) {
return dispatch({
type: 'SET_ALERT',
payload: {
message: 'No office parking spaces available',
color: 'error',
},
});
}
// Submit
const formattedDate = format(bookingDate, 'yyyy-MM-dd');
createBooking(email, formattedDate, selectedOffice, parking, bookingReason)
.then(() => {
// Increase office quota
// This assumes the date and selected office haven't changed
setOfficeSlot(
(slot) =>
slot && {
...slot,
booked: (slot.booked += 1),
bookedParking: parking ? (slot.bookedParking += 1) : slot.bookedParking,
}
);
// Clear form
setEmail('');
setParking(false);
setBookingReason(undefined);
setIsAutoApprovedUser(false);
setSearchedUser(undefined);
// Show success alert
dispatch({
type: 'SET_ALERT',
payload: {
message: `Booking created for ${email}!`,
color: 'success',
},
});
})
.catch((err) =>
// Handle error
dispatch({
type: 'SET_ALERT',
payload: {
message: formatError(err),
color: 'error',
},
})
);
};
// Render
if (!user) {
return null;
}
return (
<AdminLayout currentRoute="bookings">
<CreateBookingStyles>
{loading || !offices ? (
<Loading />
) : (
<>
<h3>Bookings</h3>
<Paper square className="form-container">
<h4>New Booking</h4>
<form
onSubmit={(e) => {
e.preventDefault();
config?.reasonToBookRequired && !isAutoApprovedUser
? setShowReasonConfirmation(true)
: handleSubmit(e);
}}
>
<div className="field">
<TextField
id="outlined-helperText"
label="Email address"
helperText="Who is the booking for"
variant="outlined"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onBlur={() => {
handleFetchUser();
}}
className="input"
/>
</div>
<div className="field">
<FormControl variant="outlined" className="input">
<InputLabel id="office-label" shrink>
Office
</InputLabel>
<Select
labelId="office-label"
id="office"
value={selectedOffice?.name || ''}
onChange={(e) =>
setSelectedOffice(offices.find((o) => o.name === e.target.value))
}
label="Office"
required
>
{offices.map((office, index) => (
<MenuItem value={office.name} key={index}>
{office.name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
{selectedOfficeWithSlots && (
<>
<div className="field">
<DatePicker
autoOk
disableToolbar
minDate={selectedOfficeWithSlots.slots[0].date}
maxDate={
selectedOfficeWithSlots.slots[selectedOfficeWithSlots.slots.length - 1]
.date
}
inputVariant="outlined"
variant="inline"
label="Date"
format="dd/MM/yyyy"
value={bookingDate}
onChange={(date) => setBookingDate(date as Date)}
className="input"
/>
</div>
{officeSlot && (
<>
<div className="field bump short">
<p>
<strong>{selectedOfficeWithSlots.quota - officeSlot.booked}</strong>{' '}
office spaces available
</p>
{selectedOfficeWithSlots.parkingQuota > 0 && (
<p>
<strong>
{selectedOfficeWithSlots.parkingQuota - officeSlot.bookedParking}
</strong>{' '}
parking spaces available
</p>
)}
</div>
{selectedOfficeWithSlots.parkingQuota > 0 && (
<div className="field bump">
<FormControlLabel
control={
<Checkbox
checked={parking}
onChange={(_, checked) => setParking(checked)}
/>
}
label="Include parking"
disabled={
selectedOfficeWithSlots.parkingQuota - officeSlot.bookedParking <= 0
}
/>
</div>
)}
</>
)}
</>
)}
<OurButton
type="submit"
variant="contained"
color="primary"
disabled={
!selectedOffice || !officeSlot || selectedOffice.quota - officeSlot.booked <= 0
}
>
Create booking
</OurButton>
</form>
</Paper>
<Dialog
open={showReasonConfirmation}
onClose={() => setShowReasonConfirmation(false)}
disableBackdropClick
>
<DialogContent>
<DialogContentText color="secondary">
You can only leave home for work purposes where it is unreasonable for you to do
your job from home. Please briefly explain why you cannot work from home.
</DialogContentText>
<TextField
autoFocus
label="Details"
type="text"
margin="normal"
fullWidth
multiline
required
value={bookingReason}
onChange={(e) => setBookingReason(e.target.value)}
/>
</DialogContent>
<DialogActions>
<OurButton
onClick={() => setShowReasonConfirmation(false)}
size="small"
color="primary"
>
Cancel
</OurButton>
<OurButton
onClick={() => {
if (bookingDate && bookingReason && bookingReason.trim().length > 0) {
setShowReasonConfirmation(false);
handleSubmit();
}
}}
variant="contained"
size="small"
color="secondary"
>
Confirm{parking ? ` + Parking` : null}
</OurButton>
</DialogActions>
</Dialog>
</>
)}
</CreateBookingStyles>
</AdminLayout>
);
}
Example #7
Source File: RightSideBar.tsx From projectboard with MIT License | 4 votes |
RightSideBar: React.FC<Props> = ({ showMenu, onCloseMenu }) => {
const ref = useRef<HTMLDivElement>() as RefObject<HTMLDivElement>;
const { task } = useSelector((state: RootState) => state.taskDetail);
const { error, loading, success } = useSelector((state: RootState) => state.updateTask);
const dispatch = useDispatch();
const params = useParams<URLParams>();
const { getAccessTokenSilently } = useAuth0();
const classes = classNames(
`absolute lg:static inset-0 transform duration-300 lg:relative lg:translate-x-0 bg-white flex flex-col flex-shrink-0 w-80 font-sans text-sm text-gray-700 border-l border-gray-100 lg:shadow-none justify-items-start h-screen`,
{
'-translate-x-full ease-out shadow-none': !showMenu,
'translate-x-0 ease-in shadow-xl': showMenu
}
);
const [startDate, setStartDate] = useState<MaterialUiPickersDate>(task.startDate);
const [dueDate, setDueDate] = useState<MaterialUiPickersDate>(task.dueDate);
const [priority, setPriority] = useState(task.priority);
const [label, setLabel] = useState(task.label);
const [status, setStatus] = useState(task.status);
const [assignee, setAssignee] = useState<Member>(task.assignee);
const [edited, setEdited] = useState(false);
// Date Pickers
const [isOpenStartDate, setIsOpenStartDate] = useState(false);
const [isOpenDueDate, setIsOpenDueDate] = useState(false);
const onStartDatePick = () => {
setIsOpenStartDate(true);
};
const onDueDatePick = () => {
setIsOpenDueDate(true);
};
let ready = false;
useClickOutside(ref, () => {
if (ready && showMenu && onCloseMenu)
onCloseMenu();
});
const onCancel = useCallback(() => {
setPriority(task.priority);
setLabel(task.label);
setDueDate(task.dueDate);
setStartDate(task.startDate);
setAssignee(task.assignee);
setStatus(task.status);
setEdited(false);
}, [task]);
const onSave = async () => {
const fieldsToUpdate = {
status: task.status !== status,
priority: task.priority !== priority,
startDate: task.startDate !== startDate,
dueDate: task.dueDate !== dueDate,
label: task.label !== label,
assignee: task?.assignee?.user?._id !== assignee?.user?._id
};
const body: any = {};
if (fieldsToUpdate.dueDate) body.dueDate = dueDate;
if (fieldsToUpdate.startDate) body.startDate = startDate;
if (fieldsToUpdate.assignee) body.assignee = assignee._id;
if (fieldsToUpdate.priority) body.priority = priority;
if (fieldsToUpdate.status) body.status = status;
if (fieldsToUpdate.label) body.label = label;
const token = await getAccessTokenSilently();
dispatch(updateTaskMicroProperties(params.taskId, params.projectId, token, body));
};
useEffect(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
setTimeout(() => ready = true, 300);
});
useEffect(() => {
if (priority !== task.priority) setEdited(true);
if (label !== task.label) setEdited(true);
if (status !== task.status) setEdited(true);
if (startDate !== task.startDate) setEdited(true);
if (dueDate !== task.dueDate) setEdited(true);
if (assignee?.user?._id !== task?.assignee?.user?._id) setEdited(true);
}, [priority, label, status, assignee, edited, task.priority, task.label, task.status, task.startDate, task.dueDate, startDate, dueDate, task?.assignee?.user?._id]);
useEffect(() => {
if (error) {
showError('Please try again later.', 'Unable to Update Task.');
onCancel();
dispatch({ type: UPDATE_TASK_MICRO_PROPS_CLEAR });
}
if (success) {
showInfo('', 'Task Updated Successfully');
setEdited(false);
getAccessTokenSilently().then(token => {
dispatch(getTaskDetail(token, params.projectId, params.taskId));
dispatch({ type: UPDATE_TASK_MICRO_PROPS_CLEAR });
});
}
}, [success, error, onCancel, dispatch, getAccessTokenSilently, params]);
return (
<>
<div className={classes} style={{ zIndex: 3 }} ref={ref}>
<button
className='flex-shrink-0 px-5 ml-2 lg:hidden h-14 focus:outline-none'
onClick={onCloseMenu}
>
<CloseIcon className='w-4' />
</button>
{/* Top menu*/}
{loading ? <div className='flex items-center justify-center flex-1'><CircularProgress color="primary" /></div> : <div className='flex flex-col flex-grow-0 flex-shrink-0 px-5 py-3 pt-10'>
<div className='flex justify-between items-center mb-6'>
<p className='text-gray-400 font-medium w-28'>Status</p>
<div className='flex items-center mr-auto'>
<StatusMenu
id='status-menu'
button={<button className='flex items-center justify-center w-6 h-6 border-none rounded focus:outline-none hover:bg-gray-100'><StatusIcon status={status} /></button>}
onSelect={(st) => {
setStatus(st);
}}
/>
<p className='text-gray-500 ml-2'>{getStatusText(status)}</p>
</div>
</div>
<div className='flex justify-between items-center mb-6'>
<p className='text-gray-400 font-medium w-28'>Priority</p>
<div className='flex items-center mr-auto'>
<PriorityMenu
// id='priority-menu'
button={<button
className='inline-flex items-center h-6 px-2 text-gray-500 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700'
>
{priority && <PriorityIcon priority={priority} />}
</button>}
onSelect={(val) => setPriority(val)}
/>
<p className='text-gray-500 ml-2'>{getPriorityString(priority)}</p>
</div>
</div>
<div className='flex justify-between items-center mb-6'>
<p className='text-gray-400 font-medium w-28'>Assignee</p>
<div className='flex items-center mr-auto'>
<AssigneeMenu
button={<button className='inline-flex items-center h-6 px-2 mr-2 text-gray-500 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700'>
{!assignee ? <><OwnerIcon className='w-3.5 h-3.5 mr-2' />
<span>Unassigned</span></> : <><OwnerIcon className='w-3.5 h-3.5 mr-2' />
<span>{`${assignee?.user?.firstName} ${assignee?.user?.lastName}`}</span></>}
</button>}
onSelect={(assignee: Member) => setAssignee(assignee)}
/>
</div>
</div>
<div className='flex justify-between items-center mb-6'>
<p className='text-gray-400 font-medium w-28'>Label</p>
<div className='flex items-center mr-auto'>
<LabelMenu
id='label-menu'
onSelect={(label: Label) => setLabel(label.name)}
button={<button className='inline-flex items-center h-6 px-2 mr-2 text-gray-500 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700'>
{label === 'No Label' ? <><LabelIcon className='w-3.5 h-3.5 mr-2' /> <span>No Label</span> </> : <><div className="w-2.5 h-2.5 rounded-full mr-2" style={{ background: getLabelObj(label)?.color }}></div> <span>{getLabelObj(label)?.name}</span> </>}
</button>} />
</div>
</div>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<div className='flex justify-between items-center mb-6'>
<p className='text-gray-400 font-medium w-28'>Start Date</p>
<div className='flex items-center mr-auto'>
<DatePicker
disablePast
open={isOpenStartDate}
onOpen={() => setIsOpenStartDate(true)}
onClose={() => setIsOpenStartDate(false)}
TextFieldComponent={() => null}
variant='dialog'
onChange={(date: MaterialUiPickersDate) => setStartDate(date)}
value={startDate}
/>
<button onClick={onStartDatePick} className='inline-flex items-center h-6 px-2 mr-2 text-gray-500 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700'>
{startDate ? `${formatDate(startDate)}` : "Start Date"}
</button>
</div>
</div>
<div className='flex justify-between items-center mb-6'>
<p className='text-gray-400 font-medium w-28'>Due Date</p>
<div className='flex items-center mr-auto'>
<DatePicker
disablePast
open={isOpenDueDate}
onOpen={() => setIsOpenDueDate(true)}
onClose={() => setIsOpenDueDate(false)}
TextFieldComponent={() => null}
variant='dialog'
onChange={(date: MaterialUiPickersDate) => setDueDate(date)}
value={dueDate}
/>
<button onClick={onDueDatePick} className='inline-flex items-center h-6 px-2 mr-2 text-gray-500 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700'>
{dueDate ? `${formatDate(dueDate)}` : "Due Date"}
</button>
</div>
</div>
<div className='flex justify-around mt-4'>
{edited && <><button onClick={onCancel} className='inline-flex items-center justify-center px-4 py-2 transition-all rounded-md border border-gray-200 text-gray-500 hover:bg-gray-100 rouned hover:text-gray-700 w-28'>Cancel</button>
<button onClick={onSave} className='ml-3 inline-flex items-center justify-center px-4 py-2 transition-all duration-400 bg-indigo-700 rounded-md hover:bg-indigo-800 rouned w-5/12 text-white'>Save</button></>}
</div>
</MuiPickersUtilsProvider>
</div>}
</div>
</>
);
}
Example #8
Source File: CreateTask.tsx From projectboard with MIT License | 4 votes |
CreateTask: React.FC<Props> = ({ match, history, location }) => {
const [showMenu, setShowMenu] = useState(false);
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [priority, setPriority] = useState(Priority.NO_PRIORITY);
const [status, setStatus] = useState(location?.state?.status || Status.BACKLOG);
const [label, setLabel] = useState(DEFAULT_LABLES[3]);
const [assignee, setAssignee] = useState<Member>();
const [dueDate, setDueDate] = useState<MaterialUiPickersDate>(() => {
const date = new Date();
const newDate = new Date(Number(date));
newDate.setDate(date.getDate() + 10);
return newDate;
});
const [startDate, setStartDate] = useState<MaterialUiPickersDate>(new Date());
// Date Pickers
const [isOpenStartDate, setIsOpenStartDate] = useState(false);
const [isOpenDueDate, setIsOpenDueDate] = useState(false);
const { projectData } = useSelector((state: RootState) => state.currentProject);
const { memberList } = useSelector((state: RootState) => state);
const memberIds = memberList.members.map((member: Member) => member._id);
const { getAccessTokenSilently } = useAuth0();
const onAssigneeSelect = (member: Member) => {
setAssignee(member);
};
const onStartDatePick = () => {
setIsOpenStartDate(true);
};
const onDueDatePick = () => {
setIsOpenDueDate(true);
};
const handleSubmit = async () => {
if (title === '') {
showWarning('Please enter a title before submiting', 'Title required');
return;
}
const body = {
title,
priority,
status,
label: label.name,
assignee: assignee?._id,
description,
startDate,
dueDate
};
try {
showWarning('Please wait!', 'Creating Task...');
const token = await getAccessTokenSilently();
const { data } = await axios({
url: `${baseURL}${endpoints.projects}/${match.params.projectId}${endpoints.tasks}`,
method: 'POST',
data: body,
headers: {
Authorization: `Bearer ${token}`
}
});
socket.emit('create_task_update', { newTask: data, member: projectData._id, memberIds });
} catch (e) {
showError('', 'Error Creating Task.');
}
setTitle('');
setDescription('');
setPriority(Priority.NO_PRIORITY);
setStatus(Status.BACKLOG);
showInfo('You created new task.', 'Task created');
history.push(`/projects/${projectData.project._id}/tasks`);
};
useEffect(() => {
const buyCoffee = document.getElementById('bmc-wbtn');
if (buyCoffee) {
buyCoffee.style.opacity = '0';
buyCoffee.style.visibility = 'hidden';
}
return () => {
const buyCoffee = document.getElementById('bmc-wbtn');
if (buyCoffee) {
buyCoffee.style.opacity = '1';
buyCoffee.style.visibility = 'visible';
}
};
}, []);
return (
<>
<LeftSideBar showMenu={showMenu} onCloseMenu={() => setShowMenu(false)} />
<div className="flex flex-col flex-grow">
<div className="flex flex-col w-full py-4 flex-1">
{/* header */}
<div className="flex items-center justify-between flex-shrink-0 px-4">
<div className="flex items-center">
<span className="inline-flex items-center p-1 text-gray-400 bg-gray-100 rounded">
<GitIssueIcon className="w-3 mr-1" />
<span>{projectData.project.title}</span>
</span>
<span className="ml-2 font-normal text-gray-700">› New Task</span>
</div>
<div className="flex items-center">
<Link
to={`/projects/${projectData.project._id}/tasks`}
className="inline-flex items-center justify-center ml-2 text-gray-500 h-7 w-7 hover:bg-gray-100 rouned hover:text-gray-700"
>
<CloseIcon className="w-4" />
</Link>
</div>
</div>
<div className="flex flex-col flex-1 pb-3.5 overflow-y-auto">
{/* Task title */}
<div className="flex items-center w-full mt-1.5 px-4">
<StatusMenu
id="status-menu"
button={
<button className="flex items-center justify-center w-6 h-6 border-none rounded focus:outline-none hover:bg-gray-100">
<StatusIcon status={status} />
</button>
}
onSelect={st => {
setStatus(st);
}}
/>
<input
className="w-full ml-1.5 text-lg font-semibold placeholder-gray-400 border-none h-7 focus:outline-none"
placeholder="Task title"
value={title}
onChange={e => setTitle(e.target.value)}
/>
</div>
{/* Task description editor */}
<div className="flex px-4">
<MarkdownStyles>
<Editor
autoFocus
id="editor"
defaultValue={description}
onChange={value => setDescription(value())}
className="mt-4 ml-5 font-normal border-none appearance-none min-h-12 text-md focus:outline-none"
placeholder="Add description..."
/>
</MarkdownStyles>
</div>
</div>
{/* Task labels & priority */}
<div className="flex items-center px-4 pb-3 mt-1 border-b border-gray-200 flex-wrap">
<PriorityMenu
// id='priority-menu'
button={
<button className="mt-2 inline-flex items-center h-6 px-2 text-gray-500 bg-gray-200 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700 mr-2">
<PriorityIcon priority={priority} className="mr-2" />
<span>{getPriorityString(priority)}</span>
</button>
}
onSelect={val => setPriority(val)}
/>
<AssigneeMenu
button={
<button className="mt-2 inline-flex items-center h-6 px-2 mr-2 text-gray-500 bg-gray-200 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700">
{!assignee ? (
<>
<OwnerIcon className="w-3.5 h-3.5 mr-2" />
<span>Assignee</span>
</>
) : (
<>
<OwnerIcon className="w-3.5 h-3.5 mr-2" />
<span>{`${assignee.user.firstName} ${assignee.user.lastName}`}</span>
</>
)}
</button>
}
onSelect={onAssigneeSelect}
/>
<LabelMenu
id="label-menu"
onSelect={(label: Label) => setLabel(label)}
button={
<button className="mt-2 inline-flex items-center h-6 px-2 mr-2 text-gray-500 bg-gray-200 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700">
{label.name === 'No Label' ? (
<>
<LabelIcon className="w-3.5 h-3.5 mr-2" /> <span>No Label</span>{' '}
</>
) : (
<>
<div
className="w-2.5 h-2.5 rounded-full mr-2"
style={{ background: label.color }}
></div>{' '}
<span>{label.name}</span>{' '}
</>
)}
</button>
}
/>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<DatePicker
disablePast
open={isOpenStartDate}
onOpen={() => setIsOpenStartDate(true)}
onClose={() => setIsOpenStartDate(false)}
TextFieldComponent={() => null}
variant="dialog"
onChange={(date: MaterialUiPickersDate) => setStartDate(date)}
value={startDate}
/>
<button
onClick={onStartDatePick}
className="mt-2 inline-flex items-center h-6 px-2 mr-2 text-gray-500 bg-gray-200 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700"
>
{startDate ? `Start Date: ${formatDate(startDate)}` : 'Start Date'}
</button>
<DatePicker
disablePast
open={isOpenDueDate}
onOpen={() => setIsOpenDueDate(true)}
onClose={() => setIsOpenDueDate(false)}
TextFieldComponent={() => null}
variant="dialog"
onChange={(date: MaterialUiPickersDate) => setDueDate(date)}
value={dueDate}
/>
<button
onClick={onDueDatePick}
className="mt-2 inline-flex items-center h-6 px-2 mr-2 text-gray-500 bg-gray-200 border-none rounded focus:outline-none hover:bg-gray-100 hover:text-gray-700"
>
{dueDate ? `Due Date: ${formatDate(dueDate)}` : 'Due Date'}
</button>
</MuiPickersUtilsProvider>
</div>
{/* Footer */}
<div className="flex items-center justify-between flex-shrink-0 px-4 pt-3 w-full">
<div className="flex items-center w-full">
<button
className="px-3 ml-2 text-white bg-indigo-600 rounded hover:bg-indigo-700 h-7 focus:outline-none ml-auto"
onClick={handleSubmit}
>
Save Task
</button>
</div>
</div>
</div>
</div>
</>
);
}