New field type: DATE (#4876)

### Description
New field type: DATE

### Refs
https://github.com/twentyhq/twenty/issues/4377

### Demo

https://jam.dev/c/d0b59883-593c-4ca3-966b-c12d5d2e1c32

Fixes #4377

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
gitstart-app[bot]
2024-04-11 17:29:29 +02:00
committed by GitHub
parent ca9cc86742
commit 7211730570
49 changed files with 354 additions and 62 deletions

View File

@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { flip, offset, useFloating } from '@floating-ui/react';
@ -14,8 +14,6 @@ const StyledCalendarContainer = styled.div`
border-radius: ${({ theme }) => theme.border.radius.md};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
margin-top: 1px;
position: absolute;
z-index: 1;
@ -38,6 +36,7 @@ export type DateInputProps = {
hotkeyScope: string;
clearable?: boolean;
onChange?: (newDate: Nullable<Date>) => void;
isDateTimeInput?: boolean;
};
export const DateInput = ({
@ -48,6 +47,7 @@ export const DateInput = ({
onClickOutside,
clearable,
onChange,
isDateTimeInput,
}: DateInputProps) => {
const theme = useTheme();
@ -60,7 +60,7 @@ export const DateInput = ({
middleware: [
flip(),
offset({
mainAxis: theme.spacingMultiplicator * 2,
mainAxis: theme.spacingMultiplicator * -6,
}),
],
});
@ -70,10 +70,6 @@ export const DateInput = ({
onChange?.(newDate);
};
useEffect(() => {
setInternalValue(value);
}, [value]);
useRegisterInputEvents({
inputRef: wrapperRef,
inputValue: internalValue,
@ -99,6 +95,7 @@ export const DateInput = ({
onEnter(newDate);
}}
clearable={clearable ? clearable : false}
isDateTimeInput={isDateTimeInput}
/>
</StyledCalendarContainer>
</div>

View File

@ -1,6 +1,7 @@
import React from 'react';
import { useState } from 'react';
import ReactDatePicker from 'react-datepicker';
import styled from '@emotion/styled';
import { DateTime } from 'luxon';
import { IconCalendarX } from 'twenty-ui';
import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent';
@ -235,7 +236,7 @@ const StyledButtonContainer = styled(StyledHoverableMenuItemBase)`
`;
const StyledButton = styled(MenuItemLeftContent)`
justify-content: center;
justify-content: start;
`;
export type InternalDatePickerProps = {
@ -243,24 +244,100 @@ export type InternalDatePickerProps = {
onMouseSelect?: (date: Date | null) => void;
onChange?: (date: Date) => void;
clearable?: boolean;
isDateTimeInput?: boolean;
};
const StyledInputContainer = styled.div`
width: 100%;
display: flex;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
height: ${({ theme }) => theme.spacing(8)};
`;
const StyledInput = styled.input`
background: ${({ theme }) => theme.background.secondary};
border: none;
color: ${({ theme }) => theme.font.color.primary};
outline: none;
padding: 8px;
font-weight: 500;
font-size: ${({ theme }) => theme.font.size.md};
width: 100%;
`;
const PICKER_DATE_FORMAT = 'MM/dd/yyyy';
export const InternalDatePicker = ({
date,
onChange,
onMouseSelect,
clearable = true,
isDateTimeInput,
}: InternalDatePickerProps) => {
const handleClear = () => {
onMouseSelect?.(null);
};
const initialDate = date
? DateTime.fromJSDate(date).toFormat(PICKER_DATE_FORMAT)
: DateTime.now().toFormat(PICKER_DATE_FORMAT);
const [dateValue, setDateValue] = useState(initialDate);
const dateValueAsJSDate = DateTime.fromFormat(dateValue, PICKER_DATE_FORMAT)
.isValid
? DateTime.fromFormat(dateValue, PICKER_DATE_FORMAT).toJSDate()
: null;
return (
<StyledContainer>
<div className={clearable ? 'clearable ' : ''}>
<StyledInputContainer>
<StyledInput
type="text"
placeholder={`Type date${
isDateTimeInput ? ' and time' : ' (mm/dd/yyyy)'
}`}
inputMode="numeric"
value={dateValue}
onChange={(e) => {
const inputValue = e.target.value;
setDateValue(inputValue);
if (!isDateTimeInput) {
const parsedInputDate = DateTime.fromFormat(
inputValue,
PICKER_DATE_FORMAT,
{ zone: 'utc' },
);
const isValid = parsedInputDate.isValid;
if (isValid) {
onChange?.(parsedInputDate.toJSDate());
}
} else {
// TODO: implement time also
const parsedInputDate = DateTime.fromFormat(
inputValue,
PICKER_DATE_FORMAT,
{ zone: 'utc' },
);
const isValid = parsedInputDate.isValid;
if (isValid) {
onChange?.(parsedInputDate.toJSDate());
}
}
}}
/>
</StyledInputContainer>
<ReactDatePicker
open={true}
selected={date}
selected={dateValueAsJSDate}
value={dateValue}
showMonthDropdown
showYearDropdown
onChange={() => {
@ -268,10 +345,18 @@ export const InternalDatePicker = ({
}}
customInput={<></>}
onSelect={(date: Date, event) => {
// Setting the time to midnight might sometimes return the previous day
// We set to 21:00 to avoid any timezone issues
const dateForDateField = new Date(date.setHours(21, 0, 0, 0));
setDateValue(
DateTime.fromJSDate(date).toFormat(PICKER_DATE_FORMAT),
);
if (event?.type === 'click') {
onMouseSelect?.(date);
onMouseSelect?.(isDateTimeInput ? date : dateForDateField);
} else {
onChange?.(date);
onChange?.(isDateTimeInput ? date : dateForDateField);
}
}}
></ReactDatePicker>