MuteTimingTimeRange.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import { css } from '@emotion/css';
  2. import React, { FC } from 'react';
  3. import { useFieldArray, useFormContext } from 'react-hook-form';
  4. import { GrafanaTheme2 } from '@grafana/data';
  5. import { Field, InlineFieldRow, InlineField, Input, Button, IconButton, useStyles2 } from '@grafana/ui';
  6. import { MuteTimingFields } from '../../types/mute-timing-form';
  7. interface Props {
  8. intervalIndex: number;
  9. }
  10. export const MuteTimingTimeRange: FC<Props> = ({ intervalIndex }) => {
  11. const styles = useStyles2(getStyles);
  12. const { register, formState } = useFormContext<MuteTimingFields>();
  13. const {
  14. fields: timeRanges,
  15. append: addTimeRange,
  16. remove: removeTimeRange,
  17. } = useFieldArray<MuteTimingFields>({
  18. name: `time_intervals.${intervalIndex}.times`,
  19. });
  20. const validateTime = (timeString: string) => {
  21. if (!timeString) {
  22. return true;
  23. }
  24. const [hour, minutes] = timeString.split(':').map((x) => parseInt(x, 10));
  25. const isHourValid = hour >= 0 && hour < 25;
  26. const isMinuteValid = minutes > -1 && minutes < 60;
  27. const isTimeValid = hour === 24 ? minutes === 0 : isHourValid && isMinuteValid;
  28. return isTimeValid || 'Time is invalid';
  29. };
  30. const formErrors = formState.errors.time_intervals?.[intervalIndex];
  31. const timeRangeInvalid = formErrors?.times?.some((value) => value?.start_time || value?.end_time) ?? false;
  32. return (
  33. <div>
  34. <Field
  35. className={styles.field}
  36. label="Time range"
  37. description="The time inclusive of the starting time and exclusive of the end time in UTC"
  38. invalid={timeRangeInvalid}
  39. error={timeRangeInvalid ? 'Times must be between 00:00 and 24:00 UTC' : ''}
  40. >
  41. <>
  42. {timeRanges.map((timeRange, index) => {
  43. return (
  44. <div className={styles.timeRange} key={timeRange.id}>
  45. <InlineFieldRow>
  46. <InlineField label="Start time" invalid={!!formErrors?.times?.[index]?.start_time}>
  47. <Input
  48. {...register(`time_intervals.${intervalIndex}.times.${index}.start_time`, {
  49. validate: validateTime,
  50. })}
  51. className={styles.timeRangeInput}
  52. // @ts-ignore react-hook-form doesn't handle nested field arrays well
  53. defaultValue={timeRange.start_time}
  54. placeholder="HH:MM"
  55. data-testid="mute-timing-starts-at"
  56. />
  57. </InlineField>
  58. <InlineField label="End time" invalid={!!formErrors?.times?.[index]?.end_time}>
  59. <Input
  60. {...register(`time_intervals.${intervalIndex}.times.${index}.end_time`, {
  61. validate: validateTime,
  62. })}
  63. className={styles.timeRangeInput}
  64. // @ts-ignore react-hook-form doesn't handle nested field arrays well
  65. defaultValue={timeRange.end_time}
  66. placeholder="HH:MM"
  67. data-testid="mute-timing-ends-at"
  68. />
  69. </InlineField>
  70. <IconButton
  71. className={styles.deleteTimeRange}
  72. title={'Remove'}
  73. name={'trash-alt'}
  74. type="button"
  75. onClick={(e) => {
  76. e.preventDefault();
  77. removeTimeRange(index);
  78. }}
  79. />
  80. </InlineFieldRow>
  81. </div>
  82. );
  83. })}
  84. </>
  85. </Field>
  86. <Button
  87. className={styles.addTimeRange}
  88. variant="secondary"
  89. type="button"
  90. icon={'plus'}
  91. onClick={() => addTimeRange({ start_time: '', end_time: '' })}
  92. >
  93. Add another time range
  94. </Button>
  95. </div>
  96. );
  97. };
  98. const getStyles = (theme: GrafanaTheme2) => ({
  99. field: css`
  100. margin-bottom: 0;
  101. `,
  102. timeRange: css`
  103. margin-bottom: ${theme.spacing(1)};
  104. `,
  105. timeRangeInput: css`
  106. width: 120px;
  107. `,
  108. deleteTimeRange: css`
  109. margin: ${theme.spacing(1)} 0 0 ${theme.spacing(0.5)};
  110. `,
  111. addTimeRange: css`
  112. margin-bottom: ${theme.spacing(2)};
  113. `,
  114. });