github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/components/CustomDatePicker.tsx (about) 1 import React, { useState, useEffect } from 'react'; 2 import { isAfter, isSameSecond } from 'date-fns'; 3 import DatePicker from 'react-datepicker'; 4 import Button from '@webapp/ui/Button'; 5 import { formatAsOBject, getUTCdate } from '@webapp/util/formatDate'; 6 import useTimeZone from '@webapp/hooks/timeZone.hook'; 7 import Select from '@webapp/ui/Select'; 8 import TextField from '@webapp/ui/Form/TextField'; 9 import styles from './CustomDatePicker.module.scss'; 10 11 interface CustomDatePickerProps { 12 from: string; 13 until: string; 14 onSubmit: (from: string, until: string) => void; 15 } 16 function CustomDatePicker({ from, until, onSubmit }: CustomDatePickerProps) { 17 const { 18 options: timeZoneOptions, 19 changeTimeZoneOffset, 20 offset, 21 } = useTimeZone(); 22 const [warning, setWarning] = useState(false); 23 const [selectedDate, setSelectedDate] = useState({ 24 from: formatAsOBject(from), 25 until: formatAsOBject(until), 26 }); 27 28 const updateDateRange = () => { 29 if ( 30 isSameSecond(selectedDate.from, selectedDate.until) || 31 isAfter(selectedDate.from, selectedDate.until) 32 ) { 33 return setWarning(true); 34 } 35 36 onSubmit( 37 Math.round(selectedDate.from.getTime() / 1000).toString(), 38 Math.round(selectedDate.until.getTime() / 1000).toString() 39 ); 40 return setWarning(false); 41 }; 42 43 // Since 'from' and 'until' are the source of truth 44 // Since our component state back when they change 45 useEffect(() => { 46 setSelectedDate({ 47 ...selectedDate, 48 from: formatAsOBject(from), 49 until: formatAsOBject(until), 50 }); 51 }, [from, until]); 52 53 const selectFromAsDate = getUTCdate(selectedDate.from, offset); 54 const selectUntilAsDate = getUTCdate(selectedDate.until, offset); 55 56 const onDateChange = (date: Date | null, area: 'from' | 'until') => { 57 if (date) { 58 setSelectedDate({ 59 ...selectedDate, 60 [area]: 61 offset === 0 62 ? new Date( 63 date.getTime() + date.getTimezoneOffset() * 60 * 1000 * -1 64 ) 65 : date, 66 }); 67 } 68 }; 69 70 return ( 71 <div className="drp-custom"> 72 <h4>Custom Date Range</h4> 73 <div className="from"> 74 <DatePicker 75 id="datepicker-from" 76 selected={selectFromAsDate} 77 onChange={(date) => onDateChange(date, 'from')} 78 selectsStart 79 showTimeSelect 80 startDate={selectFromAsDate} 81 dateFormat="yyyy-MM-dd hh:mm aa" 82 customInput={ 83 <TextField 84 className={styles.datePickerInput} 85 label="From:" 86 variant="light" 87 /> 88 } 89 /> 90 </div> 91 <div className="until"> 92 <DatePicker 93 id="datepicker-until" 94 selected={selectUntilAsDate} 95 onChange={(date) => onDateChange(date, 'until')} 96 selectsEnd 97 showTimeSelect 98 startDate={selectFromAsDate} 99 endDate={selectUntilAsDate} 100 minDate={selectFromAsDate} 101 dateFormat="yyyy-MM-dd hh:mm aa" 102 customInput={ 103 <TextField 104 className={styles.datePickerInput} 105 label="Until:" 106 variant="light" 107 /> 108 } 109 /> 110 </div> 111 {warning && <p style={{ color: 'red' }}>Warning: invalid date Range</p>} 112 113 <Button type="submit" kind="secondary" onClick={() => updateDateRange()}> 114 Apply range 115 </Button> 116 117 <div style={{ marginTop: 10 }}> 118 <label htmlFor="select-timezone">Time Zone: </label> 119 <Select 120 ariaLabel="select-timezone" 121 onChange={(e) => changeTimeZoneOffset(Number(e.target.value))} 122 id="select-timezone" 123 value={String(offset)} 124 disabled={timeZoneOptions.every((o) => o.value === 0)} 125 className={styles.timezoneSelect} 126 > 127 {timeZoneOptions.map((o) => ( 128 <option key={o.key} value={o.value}> 129 {o.label} 130 </option> 131 ))} 132 </Select> 133 </div> 134 </div> 135 ); 136 } 137 138 export default CustomDatePicker;