From efcb5dc6d4d54634eaff7d7f740d1cdb37f243da Mon Sep 17 00:00:00 2001 From: "gitstart-app[bot]" <57568882+gitstart-app[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 19:07:51 +0200 Subject: [PATCH] New Datetime field picker (#4907) ### Description New Datetime field picker ### Refs https://github.com/twentyhq/twenty/issues/4376 ### Demo https://github.com/twentyhq/twenty/assets/140154534/32656323-972c-413a-9986-a78efffae1b4 Fixes #4376 --------- Co-authored-by: gitstart-twenty Co-authored-by: v1b3m Co-authored-by: Matheus Co-authored-by: Lucas Bordeau Co-authored-by: Charles Bochet --- package.json | 1 + .../src/theme/DocSidebarItem/index.js | 2 - .../menu-item/menuItemToggleCode.js | 5 +- .../constants/SettingsFieldTypeConfigs.ts | 5 +- .../ui/field/input/components/DateInput.tsx | 5 +- .../date/components/DateTimeInput.tsx | 109 ++++++++++ .../date/components/InternalDatePicker.tsx | 196 ++++++++++-------- .../date/components/MonthAndYearDropdown.tsx | 88 ++++++++ .../internal/date/components/TimeInput.tsx | 80 +++++++ .../internal/date/constants/DateTimeBlocks.ts | 22 ++ .../internal/date/constants/DateTimeMask.ts | 3 + .../internal/date/constants/TimeBlocks.ts | 14 ++ .../internal/date/constants/TimeMask.ts | 1 + .../display/icon/components/TablerIcons.ts | 3 + yarn.lock | 32 +++ 15 files changed, 468 insertions(+), 98 deletions(-) create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimeInput.tsx create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/components/MonthAndYearDropdown.tsx create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/components/TimeInput.tsx create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeBlocks.ts create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeMask.ts create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeBlocks.ts create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeMask.ts diff --git a/package.json b/package.json index 0664fc64dd..fbb8e741a7 100644 --- a/package.json +++ b/package.json @@ -152,6 +152,7 @@ "react-hook-form": "^7.45.1", "react-hotkeys-hook": "^4.4.4", "react-icons": "^4.12.0", + "react-imask": "^7.6.0", "react-intersection-observer": "^9.5.2", "react-loading-skeleton": "^3.3.1", "react-phone-number-input": "^3.3.4", diff --git a/packages/twenty-docs/src/theme/DocSidebarItem/index.js b/packages/twenty-docs/src/theme/DocSidebarItem/index.js index 6ec77d73ef..034cee9642 100644 --- a/packages/twenty-docs/src/theme/DocSidebarItem/index.js +++ b/packages/twenty-docs/src/theme/DocSidebarItem/index.js @@ -6,8 +6,6 @@ import SearchBar from '@theme-original/SearchBar'; const CustomComponents = { 'search-bar': () => { const openSearchModal = () => { - console.log('yo'); - const searchInput = document.querySelector('#search-bar'); if (searchInput) { searchInput.focus(); diff --git a/packages/twenty-docs/src/ui/navigation/menu-item/menuItemToggleCode.js b/packages/twenty-docs/src/ui/navigation/menu-item/menuItemToggleCode.js index f128292224..ed0a363d7e 100644 --- a/packages/twenty-docs/src/ui/navigation/menu-item/menuItemToggleCode.js +++ b/packages/twenty-docs/src/ui/navigation/menu-item/menuItemToggleCode.js @@ -1,5 +1,6 @@ -import { IconBell } from "@tabler/icons-react"; -import { MenuItemToggle } from "@/ui/navigation/menu-item/components/MenuItemToggle"; +import { IconBell } from '@tabler/icons-react'; + +import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle'; export const MyComponent = () => { const handleToggleChange = (toggled) => { diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts index 168b8ef3ab..a867f7cb18 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts @@ -1,5 +1,6 @@ import { IconCalendarEvent, + IconCalendarTime, IconCheck, IconCoins, IconComponent, @@ -67,8 +68,8 @@ export const SETTINGS_FIELD_TYPE_CONFIGS: Record< defaultValue: true, }, [FieldMetadataType.DateTime]: { - label: 'Date & Time', - Icon: IconCalendarEvent, + label: 'Date and Time', + Icon: IconCalendarTime, defaultValue: DEFAULT_DATE_VALUE.toISOString(), }, [FieldMetadataType.Date]: { diff --git a/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx index ac3ffaafb6..41c0667aa2 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx @@ -41,9 +41,9 @@ export type DateInputProps = { export const DateInput = ({ value, - hotkeyScope, onEnter, onEscape, + hotkeyScope, onClickOutside, clearable, onChange, @@ -65,7 +65,7 @@ export const DateInput = ({ ], }); - const handleChange = (newDate: Date) => { + const handleChange = (newDate: Date | null) => { setInternalValue(newDate); onChange?.(newDate); }; @@ -96,6 +96,7 @@ export const DateInput = ({ }} clearable={clearable ? clearable : false} isDateTimeInput={isDateTimeInput} + onClickOutside={onClickOutside} /> diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimeInput.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimeInput.tsx new file mode 100644 index 0000000000..f4b33aca36 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimeInput.tsx @@ -0,0 +1,109 @@ +import { useEffect, useState } from 'react'; +import { useIMask } from 'react-imask'; +import styled from '@emotion/styled'; +import { DateTime } from 'luxon'; + +import { DATE_TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/DateTimeBlocks'; +import { DATE_TIME_MASK } from '@/ui/input/components/internal/date/constants/DateTimeMask'; + +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<{ hasError?: boolean }>` + 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%; + color: ${({ hasError, theme }) => (hasError ? theme.color.red : 'inherit')}; +`; + +type DateTimeInputProps = { + onChange?: (date: Date | null) => void; + date: Date | null; + isDateTimeInput?: boolean; + onError?: (error: Error) => void; +}; + +export const DateTimeInput = ({ + date, + onChange, + isDateTimeInput, +}: DateTimeInputProps) => { + const [hasError, setHasError] = useState(false); + + const parseDateToString = (date: any) => { + const dateParsed = DateTime.fromJSDate(date); + + const formattedDate = dateParsed.toFormat('MM/dd/yyyy HH:mm'); + + return formattedDate; + }; + + const parseStringToDate = (str: string) => { + setHasError(false); + + const parsedDate = DateTime.fromFormat(str, 'MM/dd/yyyy HH:mm'); + + const isValid = parsedDate.isValid; + + if (!isValid) { + setHasError(true); + + return null; + } + + const jsDate = parsedDate.toJSDate(); + + return jsDate; + }; + + const { ref, setValue, value } = useIMask( + { + mask: Date, + pattern: DATE_TIME_MASK, + blocks: DATE_TIME_BLOCKS, + min: new Date(1970, 0, 1), + max: new Date(2100, 0, 1), + format: parseDateToString, + parse: parseStringToDate, + lazy: false, + autofix: true, + }, + { + onComplete: (value) => { + const parsedDate = parseStringToDate(value); + + onChange?.(parsedDate); + }, + onAccept: () => { + setHasError(false); + }, + }, + ); + + useEffect(() => { + setValue(parseDateToString(date)); + }, [date, setValue]); + + return ( + + + + ); +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx index a36bad5c86..28b4ecb4fc 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/InternalDatePicker.tsx @@ -1,9 +1,18 @@ -import { useState } from 'react'; import ReactDatePicker from 'react-datepicker'; import styled from '@emotion/styled'; import { DateTime } from 'luxon'; -import { IconCalendarX } from 'twenty-ui'; +import { IconCalendarX, IconChevronLeft, IconChevronRight } from 'twenty-ui'; +import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; +import { DateTimeInput } from '@/ui/input/components/internal/date/components/DateTimeInput'; +import { + MONTH_AND_YEAR_DROPDOWN_ID, + MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, + MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, + MonthAndYearDropdown, +} from '@/ui/input/components/internal/date/components/MonthAndYearDropdown'; +import { TimeInput } from '@/ui/input/components/internal/date/components/TimeInput'; +import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent'; import { StyledHoverableMenuItemBase } from '@/ui/navigation/menu-item/internals/components/StyledMenuItemBase'; import { OVERLAY_BACKGROUND } from '@/ui/theme/constants/OverlayBackground'; @@ -45,13 +54,21 @@ const StyledContainer = styled.div` & .react-datepicker__header { background: transparent; border: none; + padding: 0; + } + + & + .react-datepicker__input-time-container + .react-datepicker-time__input-container + .react-datepicker-time__input { + outline: none; } & .react-datepicker__header__dropdown { display: flex; color: ${({ theme }) => theme.font.color.primary}; margin-left: ${({ theme }) => theme.spacing(1)}; - margin-bottom: ${({ theme }) => theme.spacing(1)}; + margin-bottom: ${({ theme }) => theme.spacing(10)}; } & .react-datepicker__month-dropdown-container, @@ -177,7 +194,7 @@ const StyledContainer = styled.div` } & .react-datepicker__navigation--previous { right: 38px; - top: 8px; + top: 6px; left: auto; & > span { @@ -187,7 +204,7 @@ const StyledContainer = styled.div` & .react-datepicker__navigation--next { right: 6px; - top: 8px; + top: 6px; & > span { margin-left: 6px; @@ -239,32 +256,26 @@ const StyledButton = styled(MenuItemLeftContent)` justify-content: start; `; +const StyledCustomDatePickerHeader = styled.div` + align-items: center; + display: flex; + justify-content: space-between; + padding-left: ${({ theme }) => theme.spacing(2)}; + padding-right: ${({ theme }) => theme.spacing(2)}; + padding-top: ${({ theme }) => theme.spacing(2)}; + + gap: ${({ theme }) => theme.spacing(1)}; +`; + export type InternalDatePickerProps = { - date: Date | null; + date: Date; onMouseSelect?: (date: Date | null) => void; - onChange?: (date: Date) => void; + onChange?: (date: Date | null) => void; clearable?: boolean; isDateTimeInput?: boolean; + onClickOutside?: (event: MouseEvent | TouchEvent, date: Date | null) => void; }; -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 = ({ @@ -273,92 +284,97 @@ export const InternalDatePicker = ({ onMouseSelect, clearable = true, isDateTimeInput, + onClickOutside, }: InternalDatePickerProps) => { + const internalDate = date ?? new Date(); + + const dateFormatted = + DateTime.fromJSDate(internalDate).toFormat(PICKER_DATE_FORMAT); + + const { closeDropdown } = useDropdown(MONTH_AND_YEAR_DROPDOWN_ID); + const { closeDropdown: closeDropdownMonthSelect } = useDropdown( + MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, + ); + const { closeDropdown: closeDropdownYearSelect } = useDropdown( + MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, + ); + const handleClear = () => { + closeDropdowns(); onMouseSelect?.(null); }; - const initialDate = date - ? DateTime.fromJSDate(date).toFormat(PICKER_DATE_FORMAT) - : DateTime.now().toFormat(PICKER_DATE_FORMAT); + const closeDropdowns = () => { + closeDropdownYearSelect(); + closeDropdownMonthSelect(); + closeDropdown(); + }; - const [dateValue, setDateValue] = useState(initialDate); + const handleClickOutside = (event: any) => { + closeDropdowns(); + onClickOutside?.(event, internalDate); + }; - const dateValueAsJSDate = DateTime.fromFormat(dateValue, PICKER_DATE_FORMAT) - .isValid - ? DateTime.fromFormat(dateValue, PICKER_DATE_FORMAT).toJSDate() - : null; + const handleMouseSelect = (newDate: Date) => { + closeDropdowns(); + onMouseSelect?.(newDate); + }; + + // TODO: implement keyboard events here return (
- - { - 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()); - } - } - }} - /> - - { - // We need to use onSelect here but onChange is almost redundant with onSelect but is require + selected={internalDate} + value={dateFormatted} + onChange={(newDate) => { + onChange?.(newDate); }} + renderCustomHeader={({ + decreaseMonth, + increaseMonth, + prevMonthButtonDisabled, + nextMonthButtonDisabled, + }) => ( + <> + + + + + decreaseMonth()} + size="medium" + disabled={prevMonthButtonDisabled} + /> + increaseMonth()} + size="medium" + disabled={nextMonthButtonDisabled} + /> + + + )} 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), - ); + const dateUTC = DateTime.fromJSDate(date, { + zone: 'utc', + }).toJSDate(); if (event?.type === 'click') { - onMouseSelect?.(isDateTimeInput ? date : dateForDateField); + handleMouseSelect?.(dateUTC); } else { - onChange?.(isDateTimeInput ? date : dateForDateField); + onChange?.(dateUTC); } }} + onClickOutside={handleClickOutside} >
{clearable && ( diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/MonthAndYearDropdown.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/MonthAndYearDropdown.tsx new file mode 100644 index 0000000000..d92abebd78 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/MonthAndYearDropdown.tsx @@ -0,0 +1,88 @@ +import { IconCalendarDue } from 'twenty-ui'; + +import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; +import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; +import { Select } from '@/ui/input/components/Select'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; + +type MonthAndYearDropdownProps = { + date: Date; + onChange?: (newDate: Date) => void; +}; + +const months = [ + { label: 'January', value: 0 }, + { label: 'February', value: 1 }, + { label: 'March', value: 2 }, + { label: 'April', value: 3 }, + { label: 'May', value: 4 }, + { label: 'June', value: 5 }, + { label: 'July', value: 6 }, + { label: 'August', value: 7 }, + { label: 'September', value: 8 }, + { label: 'October', value: 9 }, + { label: 'November', value: 10 }, + { label: 'December', value: 11 }, +]; + +const years = Array.from( + { length: 200 }, + (_, i) => new Date().getFullYear() + 5 - i, +).map((year) => ({ label: year.toString(), value: year })); + +export const MONTH_AND_YEAR_DROPDOWN_ID = 'date-picker-month-and-year-dropdown'; +export const MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID = + 'date-picker-month-and-year-dropdown-month-select'; +export const MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID = + 'date-picker-month-and-year-dropdown-year-select'; + +export const MonthAndYearDropdown = ({ + date, + onChange, +}: MonthAndYearDropdownProps) => { + const handleChangeMonth = (month: number) => { + const newDate = new Date(date); + newDate.setMonth(month); + onChange?.(newDate); + }; + + const handleChangeYear = (year: number) => { + const newDate = new Date(date); + newDate.setFullYear(year); + onChange?.(newDate); + }; + + return ( + + } + dropdownComponents={ + + + + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/TimeInput.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/TimeInput.tsx new file mode 100644 index 0000000000..b8c9ba54f6 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/TimeInput.tsx @@ -0,0 +1,80 @@ +import { useEffect } from 'react'; +import { useIMask } from 'react-imask'; +import styled from '@emotion/styled'; +import { DateTime } from 'luxon'; +import { IconClockHour8 } from 'twenty-ui'; + +import { TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/TimeBlocks'; +import { TIME_MASK } from '@/ui/input/components/internal/date/constants/TimeMask'; + +const StyledIconClock = styled(IconClockHour8)` + position: absolute; +`; + +const StyledTimeInputContainer = styled.div` + align-items: center; + background-color: ${({ theme }) => theme.background.tertiary}; + border-radius: ${({ theme }) => theme.border.radius.sm}; + display: flex; + margin-right: 0; + padding: 0 ${({ theme }) => theme.spacing(2)}; + + text-align: left; + width: 136px; + height: 32px; + gap: ${({ theme }) => theme.spacing(1)}; + + z-index: 10; +`; + +const StyledTimeInput = styled.input` + background: transparent; + border: none; + color: ${({ theme }) => theme.font.color.primary}; + outline: none; + font-weight: 500; + font-size: ${({ theme }) => theme.font.size.md}; + margin-left: ${({ theme }) => theme.spacing(5)}; +`; + +type TimeInputProps = { + onChange?: (date: Date) => void; + date: Date; +}; + +export const TimeInput = ({ date, onChange }: TimeInputProps) => { + const handleComplete = (value: string) => { + const [hours, minutes] = value.split(':'); + + const newDate = new Date(date); + + newDate.setHours(parseInt(hours, 10)); + newDate.setMinutes(parseInt(minutes, 10)); + + onChange?.(newDate); + }; + + const { ref, setValue } = useIMask( + { + mask: TIME_MASK, + blocks: TIME_BLOCKS, + lazy: false, + }, + { + onComplete: handleComplete, + }, + ); + + useEffect(() => { + const formattedDate = DateTime.fromJSDate(date).toFormat('HH:mm'); + + setValue(formattedDate); + }, [date, setValue]); + + return ( + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeBlocks.ts b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeBlocks.ts new file mode 100644 index 0000000000..53f2ef6f82 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeBlocks.ts @@ -0,0 +1,22 @@ +import { IMask } from 'react-imask'; + +import { TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/TimeBlocks'; + +export const DATE_TIME_BLOCKS = { + YYYY: { + mask: IMask.MaskedRange, + from: 1970, + to: 2100, + }, + MM: { + mask: IMask.MaskedRange, + from: 1, + to: 12, + }, + DD: { + mask: IMask.MaskedRange, + from: 1, + to: 31, + }, + ...TIME_BLOCKS, +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeMask.ts b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeMask.ts new file mode 100644 index 0000000000..80a62d7884 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/DateTimeMask.ts @@ -0,0 +1,3 @@ +import { TIME_MASK } from '@/ui/input/components/internal/date/constants/TimeMask'; + +export const DATE_TIME_MASK = `MM/DD/YYYY ${TIME_MASK}`; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeBlocks.ts b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeBlocks.ts new file mode 100644 index 0000000000..681c3ec042 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeBlocks.ts @@ -0,0 +1,14 @@ +import { IMask } from 'react-imask'; + +export const TIME_BLOCKS = { + HH: { + mask: IMask.MaskedRange, // Use MaskedRange for valid hour range (0-23) + from: 0, + to: 23, + }, + mm: { + mask: IMask.MaskedRange, // Use MaskedRange for valid minute range (0-59) + from: 0, + to: 61, + }, +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeMask.ts b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeMask.ts new file mode 100644 index 0000000000..f5a864c092 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/constants/TimeMask.ts @@ -0,0 +1 @@ +export const TIME_MASK = 'HH:mm'; // Define blocks for hours and minutes diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index 1a5f4247bf..481ee1d52f 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -26,7 +26,9 @@ export { IconBriefcase, IconBuildingSkyscraper, IconCalendar, + IconCalendarDue, IconCalendarEvent, + IconCalendarTime, IconCalendarX, IconCheck, IconCheckbox, @@ -40,6 +42,7 @@ export { IconCirclePlus, IconCircleX, IconClick, + IconClockHour8, IconCode, IconCoins, IconColorSwatch, diff --git a/yarn.lock b/yarn.lock index cc7ad961a7..c27369545f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3387,6 +3387,16 @@ __metadata: languageName: node linkType: hard +"@babel/runtime-corejs3@npm:^7.24.4": + version: 7.24.4 + resolution: "@babel/runtime-corejs3@npm:7.24.4" + dependencies: + core-js-pure: "npm:^3.30.2" + regenerator-runtime: "npm:^0.14.0" + checksum: 121bec9a0b505e2995c4b71cf480167e006e8ee423f77bccc38975bfbfbfdb191192ff03557c18fad6de8f2b85c12c49aaa4b92d1d5fe0c0e136da664129be1e + languageName: node + linkType: hard + "@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.23.5 resolution: "@babel/runtime@npm:7.23.5" @@ -30042,6 +30052,15 @@ __metadata: languageName: node linkType: hard +"imask@npm:^7.6.0": + version: 7.6.0 + resolution: "imask@npm:7.6.0" + dependencies: + "@babel/runtime-corejs3": "npm:^7.24.4" + checksum: c754210124efbb5dcaa37e9e21497dc9f166e7fb5759853840e49c4cde1bac61bc12f23ca5c6150a2fa57246d762d3849ad3203f5c803fc22d928e8b546b95d1 + languageName: node + linkType: hard + "immer@npm:^10.0.2": version: 10.0.3 resolution: "immer@npm:10.0.3" @@ -40738,6 +40757,18 @@ __metadata: languageName: node linkType: hard +"react-imask@npm:^7.6.0": + version: 7.6.0 + resolution: "react-imask@npm:7.6.0" + dependencies: + imask: "npm:^7.6.0" + prop-types: "npm:^15.8.1" + peerDependencies: + react: ">=0.14.0" + checksum: f5e7d9a865943ebf05d1c28819d8489a9f36cdeb2a005de340121636dbcb5e3b265017f2b64d7c40268bb5b16b9cf969721f371f18528bfe68a3b86cd8be2373 + languageName: node + linkType: hard + "react-intersection-observer@npm:^9.5.2": version: 9.5.3 resolution: "react-intersection-observer@npm:9.5.3" @@ -45980,6 +46011,7 @@ __metadata: react-hook-form: "npm:^7.45.1" react-hotkeys-hook: "npm:^4.4.4" react-icons: "npm:^4.12.0" + react-imask: "npm:^7.6.0" react-intersection-observer: "npm:^9.5.2" react-loading-skeleton: "npm:^3.3.1" react-phone-number-input: "npm:^3.3.4"