Skip to content

Commit 050311f

Browse files
author
Mukul Tayal
committed
add logs download and filter functionality
1 parent c03e0fa commit 050311f

File tree

9 files changed

+748
-32
lines changed

9 files changed

+748
-32
lines changed

src/assets/icons/ic-lines.svg

Lines changed: 3 additions & 0 deletions
Loading

src/components/common/DatePickers/Calender.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const selectedSpanStyles = {
4040
},
4141
}
4242

43-
const customDayStyles = {
43+
export const customDayStyles = {
4444
selectedStartStyles: selectedStyles,
4545
selectedEndStyles: selectedStyles,
4646
hoveredSpanStyles: hoveredSpanStyles,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import React, { useEffect, useState } from 'react'
2+
import {
3+
RadioGroupItem,
4+
VisibleModal,
5+
RadioGroup,
6+
CustomInput,
7+
InfoColourBar,
8+
} from '@devtron-labs/devtron-fe-common-lib'
9+
import Select, { components } from 'react-select'
10+
import { SingleDatePicker } from 'react-dates'
11+
import 'react-dates/initialize'
12+
import 'react-dates/lib/css/_datepicker.css'
13+
import moment, { Moment } from 'moment'
14+
import CustomizableCalendarDay from 'react-dates/lib/components/CustomizableCalendarDay.js'
15+
import { Option } from '../../../../../common/ReactSelect.utils'
16+
import { ReactComponent as Close } from '../../../../../../../assets/icons/ic-close.svg'
17+
import { ReactComponent as Warn } from '../../../../../../../assets/icons/ic-warning.svg'
18+
import { ReactComponent as CalendarIcon } from '../../../../../../../assets/icons/ic-calendar.svg'
19+
import { ReactComponent as ClockIcon } from '../../../../../../../assets/icons/ic-clock.svg'
20+
import { ReactComponent as Info } from '../../../../../../../assets/icons/ic-info-outline-grey.svg'
21+
import './customLogsDropdown.scss'
22+
import { CustomLogsDropdownProps, InputSelectionProps } from '../../nodeDetail.type'
23+
import { ALLOW_UNTIL_TIME_OPTIONS, CUSTOM_LOGS_OPTIONS } from '../../../../../../../config'
24+
import { excludeFutureTimingsOptions, getDurationUnits, getTimeStamp } from '../../nodeDetail.util'
25+
import { multiSelectStyles } from '../../../../../common/ReactSelectCustomization'
26+
import { customDayStyles } from '../../../../../../common'
27+
28+
const DropdownIndicator = (props) => {
29+
return (
30+
<components.DropdownIndicator {...props}>
31+
<ClockIcon className="icon-dim-16" />
32+
</components.DropdownIndicator>
33+
)
34+
}
35+
export const InputForSelectedOption = ({ customLogsOption, setCustomLogsOption }: InputSelectionProps) => {
36+
const [durationUnits, setDurationUnits] = useState(getDurationUnits()[0])
37+
const [date, setDate] = useState<Moment>(moment())
38+
const [input, setInput] = useState(customLogsOption.value)
39+
const [inputError, setInputError] = useState('')
40+
const [showUntilTime, setUnitlTime] = useState<{ label: string; value: string; isdisabled?: boolean }>(
41+
ALLOW_UNTIL_TIME_OPTIONS[0],
42+
)
43+
const [untilTimeOptions, setUntilTimeOptions] =
44+
useState<{ label: string; value: string; isdisabled?: boolean }[]>(ALLOW_UNTIL_TIME_OPTIONS)
45+
const [focused, setFocused] = useState(false)
46+
47+
const getNearestTimeOptionBeforeNow = () => {
48+
let nearestTimeOption
49+
ALLOW_UNTIL_TIME_OPTIONS.forEach((option) => {
50+
const today = moment().format('YYYY-MM-DD')
51+
const dateTimeToCompare = moment(`${today}T${option.value}`)
52+
if (dateTimeToCompare.isBefore(moment())) {
53+
nearestTimeOption = option
54+
}
55+
})
56+
setUnitlTime(nearestTimeOption)
57+
return nearestTimeOption
58+
}
59+
const handleFocusChange = ({ focused }) => {
60+
setFocused(focused)
61+
}
62+
useEffect(() => {
63+
setInputError('')
64+
}, [customLogsOption])
65+
useEffect(() => {
66+
const nearestOption = getNearestTimeOptionBeforeNow()
67+
const index = ALLOW_UNTIL_TIME_OPTIONS.findIndex((option) => option === nearestOption)
68+
const newOptions = excludeFutureTimingsOptions(ALLOW_UNTIL_TIME_OPTIONS, index)
69+
setUntilTimeOptions(newOptions)
70+
}, [])
71+
72+
const handleDatesChange = (selected) => {
73+
setDate(selected)
74+
setCustomLogsOption({ ...customLogsOption, value: getTimeStamp(selected, showUntilTime.value).toString() })
75+
if (selected.isSame(moment(), 'day')) {
76+
const nearestOption = getNearestTimeOptionBeforeNow()
77+
const index = ALLOW_UNTIL_TIME_OPTIONS.findIndex((option) => option === nearestOption)
78+
const newOptions = excludeFutureTimingsOptions(ALLOW_UNTIL_TIME_OPTIONS, index)
79+
setUntilTimeOptions(newOptions)
80+
} else {
81+
setUntilTimeOptions(ALLOW_UNTIL_TIME_OPTIONS)
82+
}
83+
}
84+
const handleTimeUntilChange = (selected) => {
85+
setUnitlTime(selected)
86+
setCustomLogsOption({ ...customLogsOption, value: getTimeStamp(date, selected.value).toString() })
87+
}
88+
89+
const checkRequiredError = (e) => {
90+
if (e.target.value === '') {
91+
setInputError('This field is required')
92+
} else if (inputError) {
93+
setInputError('')
94+
}
95+
}
96+
97+
const offset = moment(new Date()).format('Z')
98+
const timeZone = `${Intl?.DateTimeFormat()?.resolvedOptions()?.timeZone ?? ''} (GMT ${offset})`
99+
100+
switch (customLogsOption.option) {
101+
case 'duration':
102+
case 'lines':
103+
return (
104+
<div className="flexbox-col">
105+
<div className="dc__required-field mb-6 fs-13 fcn-7">
106+
{customLogsOption.option === 'duration' ? 'View logs for last' : 'Set number of lines'}
107+
</div>
108+
<div className="flex dc__align-start">
109+
<div className="w-180">
110+
<CustomInput
111+
error={inputError}
112+
name="duration-lines"
113+
disabled={!!inputError}
114+
handleOnBlur={checkRequiredError}
115+
value={input}
116+
rootClassName="input-focus-none"
117+
onChange={(e) => {
118+
checkRequiredError(e)
119+
setInput(e.target.value)
120+
setCustomLogsOption({ ...customLogsOption, value: e.target.value })
121+
}}
122+
/>
123+
</div>
124+
<div className="flex-grow-1">
125+
{customLogsOption.option === 'duration' ? (
126+
<Select
127+
options={getDurationUnits()}
128+
onChange={(selected) => {
129+
setDurationUnits(selected)
130+
setCustomLogsOption({ ...customLogsOption, unit: selected.value })
131+
}}
132+
value={durationUnits}
133+
styles={{
134+
...multiSelectStyles,
135+
control: (base) => ({
136+
...base,
137+
border: '1px solid var(--N200)',
138+
borderRadius: '0 4px 4px 0',
139+
boxShadow: 'none',
140+
cursor: 'pointer',
141+
}),
142+
}}
143+
components={{
144+
IndicatorSeparator: null,
145+
Option: (props) => <Option {...props} showTippy style={{ direction: 'rtl' }} />,
146+
}}
147+
/>
148+
) : (
149+
<div className="dc__border h-38 dc__right-radius-4 flex fs-13 flex-justify-start pl-8">
150+
Lines
151+
</div>
152+
)}
153+
</div>
154+
</div>
155+
</div>
156+
)
157+
case 'all':
158+
return (
159+
<div className="flexbox-col">
160+
<div className="fs-13 fw-4 fcn-9 mb-16">All available logs will be shown.</div>
161+
<InfoColourBar
162+
classname="warn"
163+
Icon={Warn}
164+
message="Note: It might take longer or result in browser issues for extensive logs."
165+
iconClass="warning-icon"
166+
/>
167+
</div>
168+
)
169+
case 'since':
170+
return (
171+
<div className="flexbox-col">
172+
<div className="dc__required-field mb-6 fs-13 fcn-7">View logs since</div>
173+
<div className="flexbox-col">
174+
<div className="flex">
175+
<SingleDatePicker
176+
placeholder="Select date"
177+
focused={focused}
178+
onFocusChange={handleFocusChange}
179+
date={date}
180+
onDateChange={handleDatesChange}
181+
numberOfMonths={1}
182+
openDirection="down"
183+
renderCalendarDay={(props) => (
184+
<CustomizableCalendarDay {...props} {...customDayStyles} />
185+
)}
186+
hideKeyboardShortcutsPanel
187+
withFullScreenPortal={false}
188+
orientation="horizontal"
189+
customInputIcon={<CalendarIcon className="icon-dim-20" />}
190+
isOutsideRange={(day) => moment().startOf('day').isBefore(day, 'day')}
191+
/>
192+
<div className="flex-grow-1">
193+
<Select
194+
placeholder="Select time"
195+
options={untilTimeOptions}
196+
value={showUntilTime}
197+
onChange={handleTimeUntilChange}
198+
menuPlacement="bottom"
199+
isSearchable={false}
200+
components={{
201+
IndicatorSeparator: null,
202+
ClearIndicator: null,
203+
DropdownIndicator,
204+
}}
205+
styles={{
206+
control: (base) => ({
207+
...base,
208+
border: '1px solid var(--N200)',
209+
borderRadius: '4px',
210+
boxShadow: 'none',
211+
cursor: 'pointer',
212+
}),
213+
}}
214+
isOptionDisabled={(option) => option.isdisabled}
215+
/>
216+
</div>
217+
</div>
218+
<div className="flex mt-4 flex-justify-start">
219+
<Info className="icon-dim-16" />
220+
<div className="ml-4 fs-11 fw-4 fcn-7">Browser time zone: {timeZone} </div>
221+
</div>
222+
</div>
223+
</div>
224+
)
225+
default:
226+
return null
227+
}
228+
}
229+
const CustomLogsDropdown = ({
230+
setCustomLogsOption,
231+
customLogsOption,
232+
setLogsShownOption,
233+
setNewFilteredLogs,
234+
setShowCustomOptions,
235+
onLogsCleared,
236+
}: CustomLogsDropdownProps) => {
237+
const handleClose = () => {
238+
setLogsShownOption((prevValue) => ({ prev: prevValue.prev, current: prevValue.prev }))
239+
setShowCustomOptions(false)
240+
}
241+
const handleSelectedFilter = () => {
242+
setShowCustomOptions(false)
243+
onLogsCleared()
244+
setNewFilteredLogs(true)
245+
}
246+
247+
return (
248+
<VisibleModal className="">
249+
<div className="custom-logs-modal w-500 br-4">
250+
<div className="flex dc__border-bottom-n1 pt-12 pb-12 pl-20 pr-20">
251+
<div className="fs-16 fw-6">View Logs</div>
252+
<Close className="icon-dim-24 ml-auto cursor" onClick={handleClose} />
253+
</div>
254+
<div className="flex dc__border-bottom-n1">
255+
<RadioGroup
256+
value={customLogsOption.option}
257+
name="custom-logs"
258+
onChange={(event) => {
259+
setCustomLogsOption({ option: event.target.value, value: '' })
260+
}}
261+
className="custom-logs-radio-group dc__no-shrink"
262+
>
263+
{CUSTOM_LOGS_OPTIONS.map(({ label, value }) => (
264+
<RadioGroupItem value={value}>
265+
<span>{label}</span>
266+
</RadioGroupItem>
267+
))}
268+
</RadioGroup>
269+
<div className="option-input-container flex-grow-1">
270+
<InputForSelectedOption
271+
customLogsOption={customLogsOption}
272+
setCustomLogsOption={setCustomLogsOption}
273+
/>
274+
</div>
275+
</div>
276+
<div className="flex flex-justify-end pt-16 pb-16 pl-20 pr-20">
277+
<button type="button" className="cta cancel h-36 flex mr-16" onClick={handleClose}>
278+
Cancel
279+
</button>
280+
<button type="button" className="cta h-36 flex" onClick={handleSelectedFilter}>
281+
Done
282+
</button>
283+
</div>
284+
</div>
285+
</VisibleModal>
286+
)
287+
}
288+
export default CustomLogsDropdown
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
.custom-logs-modal {
2+
margin: auto;
3+
display: flex;
4+
flex-direction: column;
5+
margin-top: 84px;
6+
background-color: white;
7+
8+
}
9+
10+
.custom-logs-radio-group {
11+
display: flex;
12+
flex-direction: column;
13+
border: none;
14+
border-radius: 0;
15+
border-right: 1px solid var(--N100);
16+
width: 180px;
17+
padding: 16px 0;
18+
19+
.form__radio-item {
20+
border: none;
21+
}
22+
}
23+
24+
.option-input-container {
25+
align-self: flex-start;
26+
padding: 16px 18px;
27+
flex-grow: 1;
28+
}
29+
30+
.input-focus-none {
31+
border-radius: 4px 0 0 4px;
32+
border-width: 1px 0 1px 1px;
33+
border-style: solid;
34+
border-color: var(--N200);
35+
36+
&:focus,
37+
&:hover {
38+
border-width: 1px 0 1px 1px !important;
39+
border-color: var(--N200) !important;
40+
}
41+
}
42+
43+
.logs-date-picker {
44+
display: flex;
45+
padding: 8px;
46+
justify-content: flex-start;
47+
width: 160px;
48+
margin-right: 8px;
49+
border-radius: 4px;
50+
height: 38px;
51+
border: 1px solid var(--N200);
52+
background-color: var(--N50);
53+
54+
}
55+
56+
.SingleDatePickerInput {
57+
display: flex;
58+
width: 169px;
59+
height: 38px;
60+
padding: 8px 11px 4px;
61+
border: 1px solid var(--N200);
62+
background: var(--N50);
63+
margin-right: 8px;
64+
65+
}
66+
67+
.SingleDatePickerInput_calendarIcon {
68+
padding: 0;
69+
margin: 0;
70+
margin-right: 8px
71+
}
72+
73+
.SingleDatePickerInput__withBorder .DateInput {
74+
height: max-content;
75+
background: var(--N50)
76+
}
77+
78+
.DateInput_input {
79+
font-size: 14px;
80+
padding: 0px;
81+
background: var(--N50);
82+
83+
&.DateInput_input__focused {
84+
border: none
85+
}
86+
}

0 commit comments

Comments
 (0)