date-fns#isBefore TypeScript Examples

The following examples show how to use date-fns#isBefore. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: schedule.ts    From tempomat with MIT License 6 votes vote down vote up
export function createScheduleDetails(
    worklogsResults: WorklogEntity[],
    scheduleResults: ScheduleEntity[],
    formattedSelectedDate: string,
    accountId?: string
): ScheduleDetails {
    const now = time.now()
    const formattedNowDate = format(now, DATE_FORMAT)

    const userAccountWorklogs = worklogsResults
        .filter((e: WorklogEntity) => e.author.accountId === accountId)
    const dayWorklogResults = userAccountWorklogs
        .filter((e: WorklogEntity) => e.startDate === formattedSelectedDate && e.author.accountId === accountId)
    const dayScheduleResults = scheduleResults.filter((e: ScheduleEntity) => e.date === formattedSelectedDate)
    const daysToTodayScheduleResults = scheduleResults
        .filter((e: ScheduleEntity) => e.date === formattedNowDate || isBefore(fnsParse(e.date, DATE_FORMAT, new Date()), now))

    const monthRequiredSeconds = _.sumBy(scheduleResults, (r) => r.requiredSeconds)
    const monthLoggedSeconds = _.sumBy(userAccountWorklogs, (r) => r.timeSpentSeconds)
    const monthCurrentPeriodSeconds = monthLoggedSeconds - _.sumBy(daysToTodayScheduleResults, (r) => r.requiredSeconds)
    const dayRequiredSeconds = _.sumBy(dayScheduleResults, (r) => r.requiredSeconds)
    const dayLoggedSeconds = _.sumBy(dayWorklogResults, (r) => r.timeSpentSeconds)
    return {
        monthRequiredDuration: timeParser.toDuration(monthRequiredSeconds),
        monthLoggedDuration: timeParser.toDuration(monthLoggedSeconds),
        monthCurrentPeriodDuration: timeParser.toDuration(monthCurrentPeriodSeconds, true),
        dayRequiredDuration: timeParser.toDuration(dayRequiredSeconds),
        dayLoggedDuration: timeParser.toDuration(dayLoggedSeconds)
    }
}
Example #2
Source File: dategrid.ts    From calendar-hack with MIT License 6 votes vote down vote up
setEvent(date: Date, event: T | undefined) {
        const k = key(date);
        if (event) {
            this._events.set(k, event);
            // min/max/first/last/weekCount maintenance
            if (!this._max || isAfter(date, this._max))
                this._max = date;
            this._last = startOfDay(endOfWeek(this._max, { weekStartsOn: 1 }))
            if (!this._min || isBefore(date, this._min))
                this._min = date;
            this._first = startOfWeek(this._min, { weekStartsOn: 1 })
            this._weekCount = differenceInWeeks(startOfDay(addDays(this._last, 1)), this._first);
        } else {
            this._events.delete(k);
        }
    }
Example #3
Source File: DayPicker.tsx    From symphony-ui-toolkit with Apache License 2.0 6 votes vote down vote up
arrowNavigation(date: Date, nextDate: Date) {
    const delta = differenceInCalendarMonths(nextDate, date);
    const action = isBefore(date, nextDate) ? 'next' : 'previous';
    if (delta !== 0) {
      this.setState({ currentMonth: nextDate }, () =>
        this.focusOnlyEnabledCell(nextDate, action, null, false)
      );
    } else {
      this.focusOnlyEnabledCell(nextDate, action, null);
    }
  }
Example #4
Source File: sleep-data-helper.ts    From nyxo-app with GNU General Public License v3.0 6 votes vote down vote up
// Find the starting time of the night
export function findStartTime(nights: Night[], value: Value): string {
  const nightStartTime = nights
    ?.filter((n: Night) => n.value === value)
    .reduce((previousValue: Night, currentValue: Night) =>
      isBefore(
        new Date(previousValue.startDate),
        new Date(currentValue.startDate)
      )
        ? previousValue
        : currentValue
    )
  return new Date(nightStartTime.startDate).toISOString()
}
Example #5
Source File: dateUtils.ts    From ant-extensions with MIT License 6 votes vote down vote up
makeSuperDate = (start?: DateValue, end?: DateValue) => {
  const startParsed = parseDate(start);
  const endParsed = parseDate(end);
  if (start && end && start && end && startParsed && endParsed) {
    return isBefore(startParsed, endParsed) ? `${start}|${end}` : `${end}|${start}`;
  } else if (start && !end && start) {
    return start.includes("-") ? `${start}|${DateParts.NOW}` : `${DateParts.NOW}|${start}`;
  }
  return undefined;
}
Example #6
Source File: claimable-balances.service.ts    From xBull-Wallet with GNU Affero General Public License v3.0 6 votes vote down vote up
canBeClaimed(predicate: Horizon.Predicate): boolean {
    let canClaim = false;

    // TODO: later when there is proper typing from the SDK, update this
    if ((predicate as any).unconditional === true) {
      canClaim = true;
    }

    if (typeof predicate.abs_before !== 'undefined') {
      canClaim = isBefore(new Date(), new Date(predicate.abs_before));
    }

    if (typeof predicate.rel_before !== 'undefined') {
      canClaim = isAfter(new Date(), new Date(predicate.rel_before));
    }

    if (typeof predicate.not !== 'undefined') {
      canClaim = !this.canBeClaimed(predicate.not);
    }

    if (typeof predicate.and !== 'undefined') {
      canClaim = predicate.and.every(p => this.canBeClaimed(p));
    }

    if (typeof predicate.or !== 'undefined') {
      canClaim = !!predicate.or.find(p => this.canBeClaimed(p));
    }

    return canClaim;
  }
Example #7
Source File: coaching.ts    From nyxo-app with GNU General Public License v3.0 6 votes vote down vote up
canEndCoaching = (
  startDate: string | undefined | null,
  duration: number
): boolean => {
  if (startDate) {
    return isBefore(
      addDays(startOfDay(new Date(startDate)), duration),
      startOfDay(new Date())
    )
  }

  return false
}
Example #8
Source File: DatePicker.stories.tsx    From gio-design with Apache License 2.0 5 votes vote down vote up
StaticDisabledDate.args = {
  disabledDate: (date: Date) => isBefore(startOfToday(), date),
};
Example #9
Source File: db.ts    From command-bot with Apache License 2.0 5 votes vote down vote up
getSortedTasks = async (
  { taskDb: { db }, logger }: Pick<Context, "taskDb" | "logger">,
  {
    onlyNotAlive,
  }: {
    onlyNotAlive?: boolean
  } = {},
) => {
  type Item = {
    id: DbKey
    queuedDate: Date
    task: Task
  }

  const items = await new Promise<Item[]>((resolve, reject) => {
    const databaseItems: Item[] = []

    db.createReadStream()
      .on(
        "data",
        ({
          key: rawKey,
          value: rawValue,
        }: {
          key: ToString
          value: ToString
        }) => {
          try {
            const key = rawKey.toString()
            const value = rawValue.toString()

            // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
            const task: Task = JSON.parse(value)
            if (!onlyNotAlive || !queuedTasks.has(task.id)) {
              const queuedDate = parseTaskQueuedDate(task.queuedDate)
              if (isValid(queuedDate)) {
                databaseItems.push({ id: key, queuedDate, task })
              } else {
                logger.error(
                  { key, value },
                  "Found key with invalid date in the database",
                )
                void db.del(key)
              }
            }
          } catch (error) {
            reject(error)
          }
        },
      )
      .on("error", (error) => {
        reject(error)
      })
      .on("end", () => {
        resolve(databaseItems)
      })
  })

  items.sort(
    (
      { queuedDate: dateA, task: taskA },
      { queuedDate: dateB, task: taskB },
    ) => {
      if (isBefore(dateA, dateB)) {
        return -1
      } else if (isBefore(dateB, dateA)) {
        return 1
      } else if (taskA.id < taskB.id) {
        return -1
      } else if (taskB.id < taskA.id) {
        return 1
      }
      return 0
    },
  )

  return items
}
Example #10
Source File: index.ts    From MDDL with MIT License 5 votes vote down vote up
@Action({ rawError: true })
  async createCollection(payload: CollectionCreate): Promise<Collection> {
    if (!this.ownerId) return Promise.reject(new Error('UserID not set'))
    const { data } = await api.user.addUserCollection(this.ownerId, payload)
    const documents = await this.getDocuments()
    const dates = documents.map((d) => new Date(d.createdDate))
    const oldest = dates.reduce(
      (oldest, current) => (isBefore(current, oldest) ? current : oldest),
      dates[0],
    )
    const newest = dates.reduce(
      (newest, current) => (isBefore(current, newest) ? newest : current),
      dates[0],
    )
    const currentDate = Date.now()
    const daysSinceOldest = differenceInDays(currentDate, oldest)
    const daysSinceNewest = differenceInDays(currentDate, newest)
    this.$ga.event({
      eventCategory: 'collection_shared',
      eventAction: 'number_of_recipients',
      eventLabel: UserRole[this._role!],
      eventValue: payload.individualEmailAddresses.length,
    })
    this.$ga.event({
      eventCategory: 'collection_shared',
      eventAction: 'number_of_documents',
      eventLabel: UserRole[this._role!],
      eventValue: payload.documentIds.length,
    })
    this.$ga.event({
      eventCategory: 'collection_shared',
      eventAction: 'days_since_oldest_document',
      eventLabel: UserRole[this._role!],
      eventValue: daysSinceOldest,
    })
    this.$ga.event({
      eventCategory: 'collection_shared',
      eventAction: 'days_since_newest_document',
      eventLabel: UserRole[this._role!],
      eventValue: daysSinceNewest,
    })
    return data
  }
Example #11
Source File: CreateAppointmentService.ts    From GoBarber with MIT License 5 votes vote down vote up
public async execute({
    provider_id,
    user_id,
    date,
  }: IRequest): Promise<Appointment> {
    const appointmentDate = startOfHour(date);

    if (isBefore(appointmentDate, Date.now())) {
      throw new AppError("You can't book appointments in past dates");
    }

    if (provider_id === user_id) {
      throw new AppError("You can't book appointments with yourself");
    }

    if (getHours(appointmentDate) < 8 || getHours(appointmentDate) > 17) {
      throw new AppError("You can't book appointments outside commercial time");
    }

    const findAppointmentInSameDate = await this.appointmentsRepository.findByDate(
      appointmentDate,
      provider_id,
    );

    if (findAppointmentInSameDate) {
      throw new AppError('This appointment is already booked');
    }

    const appointment = await this.appointmentsRepository.create({
      provider_id,
      user_id,
      date: appointmentDate,
    });

    const dateFormatted = format(appointmentDate, "dd/MM/yyyy 'às' HH:mm'h'");

    await this.notificationsRepository.create({
      recipient_id: provider_id,
      content: `Novo agendamento para ${dateFormatted}`,
    });

    await this.cacheProvider.invalidate(
      `provider-appointments:${provider_id}:${format(
        appointmentDate,
        'd-M-yyyy',
      )}`,
    );

    return appointment;
  }
Example #12
Source File: CreateAppointmentService.ts    From gobarber-project with MIT License 5 votes vote down vote up
public async execute({
    provider_id,
    user_id,
    date,
  }: IRequest): Promise<Appointment> {
    const appointmentDate = startOfHour(date);

    if (isBefore(appointmentDate, Date.now())) {
      throw new AppError("You can't create an appointment on a past date");
    }

    if (user_id === provider_id) {
      throw new AppError("You can't create an appointment with yourself");
    }

    if (getHours(appointmentDate) < 8 || getHours(appointmentDate) > 17) {
      throw new AppError(
        'You can only create appointments between 8am and 5pm',
      );
    }

    const findAppoitmentInSameDate = await this.appointmentsRepository.findByDate(
      appointmentDate,
      provider_id,
    );

    if (findAppoitmentInSameDate) {
      throw new AppError('This appointment is already booked');
    }

    const appointment = await this.appointmentsRepository.create({
      provider_id,
      user_id,
      date: appointmentDate,
    });

    const dateFormatted = format(appointmentDate, "dd/MM/yyyy 'às' HH:mm'h'");

    await this.notificationsRepository.create({
      recipient_id: provider_id,
      content: `Novo agendamento para dia ${dateFormatted}`,
    });

    await this.cacheProvider.invalidate(
      `provider-appointments:${provider_id}:${format(
        appointmentDate,
        'yyyy-M-d',
      )}`,
    );

    return appointment;
  }
Example #13
Source File: validator.ts    From elements with MIT License 5 votes vote down vote up
private validateMaxSingle = (value: string): boolean => {
    const parsedDate: Date = this.parseDate(value);
    return (
      isEqual(parsedDate, this._maxDate) || isBefore(parsedDate, this._maxDate)
    );
  };
Example #14
Source File: matchDayUtils.tsx    From symphony-ui-toolkit with Apache License 2.0 5 votes vote down vote up
function isDayBefore(day1: Date, day2: Date): boolean {
  return isBefore(day1, day2) && !isSameDay(day1, day2);
}
Example #15
Source File: pause.tsx    From protect-scotland with Apache License 2.0 5 votes vote down vote up
Pause: FC = () => {
  const navigation = useNavigation();
  const {t} = useTranslation();
  const {pause} = useExposure();
  const [selectedInterval, setSelectedInterval] = useState<Date>(
    getClosestInterval(15)
  );
  const [selected, setSelected] = useState<boolean>(false);
  const [show, setShow] = useState(false);
  const {setReminder} = useReminder();

  return (
    <>
      <View style={styles.header}>
        <ModalClose onPress={() => navigation.goBack()} />
      </View>
      <ScrollView contentContainerStyle={styles.contentContainerStyle}>
        <Text variant="h3" color="darkGrey">
          {t('pause:title')}
        </Text>
        <Spacing s={24} />
        <Text color="darkGrey">{t('pause:body')}</Text>
        <Spacing s={24} />
        <Markdown accessibleLink={t('links:o')}>
          {t('pause:label', {link: t('links:o')})}
        </Markdown>
        <Spacing s={24} />
        <Button
          type="secondary"
          variant="green"
          rounded
          buttonStyle={styles.smallButton}
          onPress={() => setShow(true)}>
          {!selected ? (
            t('pause:setTimeButton')
          ) : (
            <>
              {format(selectedInterval, 'HH:mm')}{' '}
              {isBefore(selectedInterval, new Date())
                ? t('common:tomorrow')
                : t('common:today')}
            </>
          )}
        </Button>
        <DateTimePickerModal
          isVisible={show}
          mode="time"
          display="spinner"
          minuteInterval={15}
          date={selectedInterval}
          onConfirm={(e) => {
            setShow(false);
            setSelectedInterval(e);
            setSelected(true);
          }}
          onCancel={() => setShow(false)}
          headerTextIOS={t('pause:modalHeader')}
        />
        <Spacing s={18} />
        <Button
          type="primary"
          variant="green"
          rounded
          onPress={() => {
            pause();
            setReminder(selectedInterval);
            navigation.goBack();
          }}>
          {t('pause:button')}
        </Button>
      </ScrollView>
    </>
  );
}
Example #16
Source File: ListProviderAppointmentsService.ts    From hotseat-api with MIT License 5 votes vote down vote up
async execute({
    day,
    year,
    month,
    provider_id,
  }: IRequest): Promise<IResponse[]> {
    const appointmentsListCacheKey = getProviderAppointmentsListCacheKey(
      provider_id,
      new Date(year, parseMonthToJSMonth(month), day),
    );

    let appointments = await this.cacheProvider.get<Appointment[]>(
      appointmentsListCacheKey,
    );

    if (!appointments) {
      appointments = await this.appointmentsRepository.findByDayFromProvider({
        day,
        year,
        month,
        provider_id,
      });

      await this.cacheProvider.save<Appointment[]>(
        appointmentsListCacheKey,
        classToClass(appointments),
      );
    }

    const listAppointments = appointments.map(appointment => {
      const currentDate = new Date(Date.now());

      const parseCachedDate =
        typeof appointment.date === 'string'
          ? parseISO(appointment.date)
          : appointment.date;

      return {
        ...appointment,
        isPast: isBefore(parseCachedDate, currentDate),
      };
    });

    return listAppointments;
  }
Example #17
Source File: CreateAppointmentService.ts    From gobarber-api with MIT License 5 votes vote down vote up
public async execute({
    date,
    provider_id,
    user_id,
  }: IRequest): Promise<Appointment> {
    const appointmentDate = startOfHour(date);

    if (isBefore(appointmentDate, Date.now())) {
      throw new AppError("You cant't create an appointment on past date");
    }

    if (user_id === provider_id) {
      throw new AppError("You can't create an appointment with yourself");
    }

    if (getHours(appointmentDate) < 8 || getHours(appointmentDate) > 17) {
      throw new AppError(
        'You can only create appointments between 8am and 5pm',
      );
    }

    const findAppointmentInSameDate = await this.appointmentsRepository.findByDate(
      {
        date: appointmentDate,
        provider_id,
      },
    );

    if (findAppointmentInSameDate) {
      throw new AppError('This appointment is already booked');
    }

    const appointment = await this.appointmentsRepository.create({
      user_id,
      provider_id,
      date: appointmentDate,
    });

    const dateFormated = format(appointmentDate, "dd/MM/yyyy 'às' HH:mm'h'");

    await this.notificationsRepository.create({
      recipient_id: provider_id,
      content: `Novo agendamento para dia ${dateFormated}.`,
    });

    await this.cacheProvider.invalidate(
      `provider-appointments:${provider_id}:${format(
        appointmentDate,
        'yyyy-M-d',
      )}`,
    );

    return appointment;
  }
Example #18
Source File: BudgetTransaction.ts    From cashcash-desktop with MIT License 4 votes vote down vote up
actions = {
    async initWipTransaction(
        { commit }: Vuex.ActionContext<IBudgetTransactionState, any>,
        obj: { transactionId: string; duplicate: boolean },
    ) {
        let flatTransaction;
        if (obj.transactionId === 'new') {
            flatTransaction = new FlatCashTransaction({ description: 'NO DESCRIPTION' });
        } else {
            const service = Container.get(CashBudgetTransactionService);
            flatTransaction = await service.getFlat(obj.transactionId);
        }
        commit('updateField', { path: 'wipTransaction', value: flatTransaction });
    },
    async removeWipTransaction({ commit }: Vuex.ActionContext<IBudgetTransactionState, any>) {
        commit('updateField', { path: 'wipTransaction', value: null });
    },
    async saveTransaction(
        { dispatch, commit }: Vuex.ActionContext<IBudgetTransactionState, any>,
        wipTransaction: FlatCashTransaction,
    ) {
        const service = Container.get(CashBudgetTransactionService);
        await service.save(wipTransaction);
        Notification.success({
            title: i18n.t('Budget entry saved').toString(),
            message: '',
        });
        commit('updateField', { path: 'wipTransaction', value: null });
        await dispatch('PermanentData/fillBudgetSplit', {}, { root: true });
    },
    async saveTransactionList(
        { dispatch, commit }: Vuex.ActionContext<IBudgetTransactionState, any>,
        wipTransactionList: FlatCashTransaction[],
    ) {
        const service = Container.get(CashBudgetTransactionService);
        await service.saveList(wipTransactionList);
        Notification.success({
            title: i18n
                .t('{total} budget entries saved', {
                    total: wipTransactionList.filter((item) => !item.doNotImport).length,
                })
                .toString(),
            message: '',
        });
        commit('updateField', { path: 'wipTransaction', value: null });
        await dispatch('PermanentData/fillBudgetSplit', {}, { root: true });
    },
    async deleteTransaction(
        { dispatch }: Vuex.ActionContext<IBudgetTransactionState, any>,
        transactionId: string,
    ) {
        const service = Container.get(CashBudgetTransactionService);
        await service.delete(transactionId);
        Notification.success({
            title: i18n.t('Budget entry deleted').toString(),
            message: '',
        });
        await dispatch('PermanentData/fillBudgetSplit', {}, { root: true });
    },
    async deleteTransactionList(
        { dispatch }: Vuex.ActionContext<IBudgetTransactionState, any>,
        transactionIdList: string[],
    ) {
        if (transactionIdList.length === 1) {
            await dispatch('deleteTransaction', transactionIdList[0]);
        } else {
            const service = Container.get(CashBudgetTransactionService);
            await service.deleteList(transactionIdList);
            Notification.success({
                title: i18n
                    .t('{total} transactions deleted', { total: transactionIdList.length })
                    .toString(),
                message: '',
            });
            await dispatch('PermanentData/fillBudgetSplit', {}, { root: true });
        }
    },
    async updateBudgetDate(
        { commit }: Vuex.ActionContext<IBudgetTransactionState, any>,
        budgetDate: Date,
    ) {
        commit('updateField', { path: 'budgetDate', value: budgetDate });
    },
    async adjustBudgetDateIfNeeded({
        commit,
        state,
        rootState,
    }: Vuex.ActionContext<IBudgetTransactionState, any>) {
        const parameters = rootState.TimeFrameData.parameters;
        const transactionDateFrom = parameters.transactionDateFrom;
        const transactionDateTo = parameters.transactionDateTo;

        if (
            isAfter(state.budgetDate, transactionDateTo) ||
            isBefore(state.budgetDate, transactionDateFrom)
        ) {
            const newBudgetDate = transactionDateTo;
            commit('updateField', { path: 'budgetDate', value: newBudgetDate });
        }
    },
    async updateContextAccount(
        { commit, rootState }: Vuex.ActionContext<IBudgetTransactionState, any>,
        account: CashAccount,
    ) {
        if (!account) {
            account = rootState.PermanentData.accountTree.find((item) => item.code === 'ASS');
        }
        commit('updateField', { path: 'contextAccount', value: account });
    },
    async updateIsPageOpen(
        { commit }: Vuex.ActionContext<IBudgetTransactionState, any>,
        isPageOpen: boolean,
    ) {
        commit('updateField', { path: 'isPageOpen', value: isPageOpen });
    },
}
Example #19
Source File: DateTimeRangePicker.tsx    From UUI with MIT License 4 votes vote down vote up
DateTimeRangePicker = UUIFunctionComponent({
  name: 'DateTimeRangePicker',
  nodes: {
    Root: 'div',
    ConnectIcon: Icons.ArrowRight,
    CalendarIcon: Icons.Calendar,
    Popover: UUIPopover,
    TextField: UUITextField,
    Activator: 'div',
    Container: 'div',
    Toolbar: 'div',
    Main: 'div',
    StartSection: 'div',
    EndSection: 'div',
    Section: 'div',
    PickerButtons: UUIPickerButtons,
    YearMonthSelect: UUIYearMonthSelect,
    DateSelect: UUIDateSelect,
    TimeSelect: UUITimeSelect,
    DateTimeShortcut: UUIDateTimeShortcut,
  },
  propTypes: DateTimeRangePickerPropTypes,
}, (props: DateTimeRangePickerFeatureProps, { nodes }) => {
  const {
    Root, ConnectIcon, CalendarIcon, Popover, TextField,
    Activator, Container, Main, StartSection, EndSection, Section,
    YearMonthSelect, DateSelect, TimeSelect, DateTimeShortcut, PickerButtons,
  } = nodes

  const startTimeSelectRef = useRef<any | null>(null)
  const endTimeSelectRef = useRef<any | null>(null)
  const startInputRef = useRef<HTMLInputElement | null>(null)
  const endInputRef = useRef<HTMLInputElement | null>(null)
  const [active, setActive] = useState(false)
  const [whichFocusing, setWhichFocusing] = useState<'start' | 'end'>()
  const [hoverDate, setHoverDate] = useState<Date>()

  const initialInnerValue = useMemo(() => {
    if (props.value === null) {
      return {
        startDate: null,
        endDate: null,
        startInput: '',
        endInput: '',
        startYearMonth: startOfMonth(new Date),
        endYearMonth: add(startOfMonth(new Date), { months: 1 }),
      }
    }
    return {
      startDate: props.value[0],
      endDate: props.value[1],
      startInput: formatDateTime(props.value[0]),
      endInput: formatDateTime(props.value[1]),
      startYearMonth: startOfMonth(props.value[0]),
      endYearMonth: isSameMonth(props.value[0], props.value[1]) ? add(startOfMonth(props.value[1]), { months: 1 }) : props.value[1],
    }
  }, [props.value])
  const [innerValue, setInnerValue, resetInnerValue] = usePendingValue<DateTimeRangePickerInnerValue>(initialInnerValue, (value) => {
    if (value.startDate && value.endDate) {
      handleValueOnChange([value.startDate, value.endDate])
      closePopover()
    }
  }, { resetWhenInitialValueChanged: true })

  const selectedDates = useMemo(() => {
    return compact([innerValue.startDate, innerValue.endDate])
  }, [innerValue.endDate, innerValue.startDate])

  const timeSelectScrollToValue = useCallback((type: 'start' | 'end', value: Date, animate?: boolean) => {
    if (type === 'start' && startTimeSelectRef.current) {
      startTimeSelectRef.current.scrollToValue(value, animate)
    }
    if (type === 'end' && endTimeSelectRef.current) {
      endTimeSelectRef.current.scrollToValue(value, animate)
    }
  }, [])
  const openPopover = useCallback(() => { setActive(true) }, [])
  const closePopover = useCallback(() => { setActive(false) }, [])
  const handleValueOnChange = useCallback((value: DateTimeRangePickerValue | null) => {
    const sortedValue = value?.sort((i, j) => Number(i) - Number(j)) || null
    props.onChange(sortedValue)
  }, [props])

  /**
   *
   */
  const handleInputOnSubmit = useCallback((type: 'start' | 'end') => {
    if (innerValue.startDate && innerValue.endDate) {
      const originalInput = formatDateTime(type === 'start' ? innerValue.startDate : innerValue.endDate)
      const input = type === 'start' ? innerValue.startInput : innerValue.endInput
      if (originalInput === input) return;
      try {
        if (input === '') {
          handleValueOnChange(null)
        } else {
          const result = tryParseDateTimeFromString(input)
          handleValueOnChange(type === 'start' ? [result, innerValue.endDate] : [innerValue.startDate, result])
        }
      } catch {
        resetInnerValue()
      }
    }
  }, [handleValueOnChange, innerValue.endInput, innerValue.endDate, innerValue.startInput, innerValue.startDate, resetInnerValue])
    /**
   * handle user change year or month in YearMonthSelect.
   */
  const handleStartYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const startYearMonthDate = value
      let endYearMonthDate = oldValue.endYearMonth
      if (!isBefore(startYearMonthDate, endYearMonthDate)) {
        endYearMonthDate = add(startYearMonthDate, { months: 1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  const handleEndYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const endYearMonthDate = value
      let startYearMonthDate = oldValue.startYearMonth
      if (!isAfter(endYearMonthDate, startYearMonthDate)) {
        startYearMonthDate = add(endYearMonthDate, { months: -1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  /**
   * handle user select date in DateSelect.
   */
  const handleDateSelect = useCallback((value: Date) => {
    let newStartValue = innerValue.startDate
    let newEndValue = innerValue.endDate
    if (
      (newStartValue !== null && newEndValue !== null) ||
      (newStartValue === null && newEndValue === null)
    ) {
      if (whichFocusing === 'end') {
        newStartValue = null
        newEndValue = value
      } else {
        newStartValue = value
        newEndValue = null
      }
    } else {
      if (newStartValue === null) newStartValue = value
      if (newEndValue === null) newEndValue = value
      if (isAfter(newStartValue, newEndValue)) {
        const tmp = new Date(newStartValue)
        newStartValue = new Date(newEndValue)
        newEndValue = tmp
      }
    }
    setInnerValue((oldValue) => {
      return {
        ...oldValue,
        startDate: newStartValue,
        startInput: formatDateTime(newStartValue),
        endDate: newEndValue,
        endInput: formatDateTime(newEndValue),
      }
    })
  }, [innerValue.endDate, innerValue.startDate, setInnerValue, whichFocusing])
  /**
   * handle user select date in TimeSelect.
   */
  const handleTimeSelect = useCallback((type: 'start' | 'end') => {
    return (value: Date) => {
      setInnerValue((oldValue) => {
        const oldDate = type === 'start' ? oldValue.startDate : oldValue.endDate
        const newDate = set(oldDate || getZeroDate(), {
          hours: value.getHours(),
          minutes: value.getMinutes(),
          seconds: value.getSeconds(),
        })
        const newInput = formatDateTime(newDate)
        return {
          ...oldValue,
          ...(type === 'start' ? {
            startDate: newDate,
            startInput: newInput,
          } : {}),
          ...(type === 'end' ? {
            endDate: newDate,
            endInput: newInput,
          } : {}),
        }
      })
    }
  }, [setInnerValue])

  return (
    <Root>
      <Popover
        placement={'bottom-start'}
        active={active}
        onClickAway={() => {
          resetInnerValue();
          timeSelectScrollToValue('start', props.value ? props.value[0] : getZeroDate(), false)
          timeSelectScrollToValue('end', props.value ? props.value[1] : getZeroDate(), false)
          setTimeout(() => { closePopover() }, 10)
        }}
        activator={
          <Activator
            onClick={() => {
              openPopover()
              setTimeout(() => {
                const focusedElement = ReactHelper.document?.activeElement
                if (startInputRef.current === focusedElement || endInputRef.current === focusedElement) return;
                if (startInputRef.current) {
                  startInputRef.current.focus()
                }
              }, 0)
            }}
          >
            <TextField
              placeholder={props.startPlaceholder}
              value={innerValue.startInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, startInput: value })) }}
              customize={{
                Input: {
                  ref: startInputRef,
                  onFocus: () => {
                    setWhichFocusing('start')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('start')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('start')
                    }
                  }
                }
              }}
            />
            <ConnectIcon />
            <TextField
              placeholder={props.endPlaceholder}
              value={innerValue.endInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, endInput: value })) }}
              customize={{
                Input: {
                  ref: endInputRef,
                  onFocus: () => {
                    setWhichFocusing('end')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('end')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('end')
                    }
                  }
                }
              }}
            />
            <CalendarIcon />
          </Activator>
        }
      >
        <Container>
          <Main tabIndex={-1}>
            {props.shortcuts && (
              <DateTimeShortcut
                options={props.shortcuts}
                onSelect={(value) => {
                  handleValueOnChange(value)
                  timeSelectScrollToValue('start', value ? value[0] : getZeroDate(), false)
                  timeSelectScrollToValue('end', value ? value[1] : getZeroDate(), false)
                  closePopover()
                }}
              />
            )}
            <StartSection>
              <YearMonthSelect
                value={innerValue.startYearMonth}
                onChange={handleStartYearMonthSelect}
              />
              <Section>
                <DateSelect
                  yearMonth={innerValue.startYearMonth}
                  selectedDates={selectedDates}
                  onSelect={handleDateSelect}
                  hoverDate={hoverDate}
                  onHoverDateChange={(date) => { setHoverDate(date) }}
                />
                <TimeSelect
                  ref={startTimeSelectRef}
                  value={innerValue.startDate || getZeroDate()}
                  onChange={handleTimeSelect('start')}
                />
              </Section>
            </StartSection>
            <EndSection>
              <YearMonthSelect
                value={innerValue.endYearMonth}
                onChange={handleEndYearMonthSelect}
              />
              <Section>
                <DateSelect
                  yearMonth={innerValue.endYearMonth}
                  selectedDates={selectedDates}
                  onSelect={handleDateSelect}
                  hoverDate={hoverDate}
                  onHoverDateChange={(date) => { setHoverDate(date) }}
                />
                <TimeSelect
                  ref={endTimeSelectRef}
                  value={innerValue.endDate || getZeroDate()}
                  onChange={handleTimeSelect('end')}
                />
              </Section>
            </EndSection>
          </Main>
          <PickerButtons
            confirmLabel={props.confirmLabel}
            cancelLabel={props.cancelLabel}
            onCancel={() => {
              resetInnerValue()
              timeSelectScrollToValue('start', props.value ? props.value[0] : getZeroDate(), false)
              timeSelectScrollToValue('end', props.value ? props.value[1] : getZeroDate(), false)
              setTimeout(() => { closePopover() }, 10)
            }}
            onConfirm={() => {
              setInnerValue((value) => value, true)
              if (innerValue.startDate && innerValue.endDate) {
                let data = [innerValue.startDate, innerValue.endDate]
                if (isAfter(innerValue.startDate, innerValue.endDate)) {
                  data = data.reverse()
                }
                timeSelectScrollToValue('start', data[0], false)
                timeSelectScrollToValue('end', data[1], false)
              } else {
                timeSelectScrollToValue('start', getZeroDate(), false)
                timeSelectScrollToValue('end', getZeroDate(), false)
              }
              setTimeout(() => { closePopover() }, 10)
            }}
          />
        </Container>
      </Popover>
    </Root>
  )
})
Example #20
Source File: DateSelect.tsx    From UUI with MIT License 4 votes vote down vote up
DateSelect = UUIFunctionComponent({
  name: 'DateSelect',
  nodes: {
    Root: 'div',
    Calendar: 'div',
    WeekGrid: 'div',
    WeekItem: 'div',
    DayGrid: 'div',
    DayItem: 'div',
  },
  propTypes: DateSelectPropTypes,
}, (props: DateSelectFeatureProps, { nodes, NodeDataProps }) => {
  const {
    Root, Calendar,
    WeekGrid, WeekItem,
    DayGrid, DayItem,
  } = nodes

  const betweenIncludeDates = useCallback((date: Date, range: [Date, Date] | Date[]) => {
    return !isBefore(date, range[0]) && !isAfter(date, range[1])
  }, [])

  const dateInfo = useMemo(() => {
    const firstDayInMonth = startOfMonth(props.yearMonth)
    const weekdayOfFirstDayInMonth = getDay(firstDayInMonth)

    const weekdays = range(0, 7).map((i) => {
      let date = new Date(props.yearMonth)
      date = startOfWeek(date)
      date = add(date, { days: i })
      return {
        key: format(date, 'yyyy-MM-dd'),
        date: date,
        label: format(date, 'EEEEEE', { locale: zhCN }),
      };
    });

    const days = range(
      1 - weekdayOfFirstDayInMonth,
      1 - weekdayOfFirstDayInMonth + 6*7,
    ).map((i) => {
      const date = add(firstDayInMonth, { days: i - 1 })
      const selected = props.selectedDates.findIndex((i) => isSameDay(date, i)) !== -1
      const inSelectedRange = (() => {
        if (props.selectedDates.length >= 2) {
          return betweenIncludeDates(date, props.selectedDates)
        }
        return false;
      })()
      return {
        key: format(date, 'yyyy-MM-dd'),
        date: date,
        label: getDate(date),
        active: isSameMonth(props.yearMonth, date),
        selected: selected,
        inSelectedRange: inSelectedRange,
      }
    })

    return {
      weekdays,
      days
    }
  }, [props.yearMonth, props.selectedDates, betweenIncludeDates])

  return (
    <Root>
      <Calendar>
        <WeekGrid>
          {dateInfo.weekdays.map((weekday) => {
            return (
              <WeekItem key={weekday.key}>
                {weekday.label}
              </WeekItem>
            );
          })}
        </WeekGrid>
        <DayGrid
          onMouseLeave={() => {
            props.onHoverDateChange && props.onHoverDateChange(undefined)
          }}
        >
          {dateInfo.days.map((day) => {
            const hovering = props.hoverDate ? isSameDay(day.date, props.hoverDate) : false
            const inHoverRange = (() => {
              if (props.selectedDates.length === 1 && props.hoverDate) {
                return betweenIncludeDates(day.date, [props.selectedDates[0], props.hoverDate].sort((i, j) => Number(i) - Number(j)))
              }
              return false
            })()
            return (
              <DayItem
                {...NodeDataProps({
                  'active': day.active,
                  'selected': day.selected,
                  'in-selected-range': day.inSelectedRange,
                  'in-hover-range': inHoverRange,
                  'hovering': hovering,
                })}
                key={day.key}
                onClick={() => {
                  props.onSelect(day.date)
                }}
                onMouseEnter={() => {
                  props.onHoverDateChange && props.onHoverDateChange(day.date)
                }}
                onMouseLeave={() => {
                  props.onHoverDateChange && props.onHoverDateChange(undefined)
                }}
              >
                {day.label}
              </DayItem>
            )
          })}
        </DayGrid>
      </Calendar>
    </Root>
  )
})
Example #21
Source File: DateRangePicker.tsx    From UUI with MIT License 4 votes vote down vote up
DateRangePicker = UUIFunctionComponent({
  name: 'DateRangePicker',
  nodes: {
    Root: 'div',
    ConnectIcon: Icons.ArrowRight,
    CalendarIcon: Icons.Calendar,
    Popover: UUIPopover,
    TextField: UUITextField,
    Activator: 'div',
    Container: 'div',
    Toolbar: 'div',
    Main: 'div',
    StartSection: 'div',
    EndSection: 'div',
    YearMonthSelect: UUIYearMonthSelect,
    DateSelect: UUIDateSelect,
    DateTimeShortcut: UUIDateTimeShortcut,
  },
  propTypes: DateRangePickerPropTypes,
}, (props: DateRangePickerFeatureProps, { nodes }) => {
  const {
    Root, ConnectIcon, CalendarIcon, Popover, TextField,
    Activator, Container, Toolbar, Main, StartSection, EndSection,
    YearMonthSelect, DateSelect, DateTimeShortcut,
  } = nodes

  const startInputRef = useRef<HTMLInputElement | null>(null)
  const endInputRef = useRef<HTMLInputElement | null>(null)
  const [whichFocusing, setWhichFocusing] = useState<'start' | 'end'>()
  const [active, setActive] = useState(false)

  const [hoverDate, setHoverDate] = useState<Date>()

  const initialInnerValue = useMemo<DateRangePickerInnerValue>(() => {
    if (props.value === null) {
      return {
        startDate: null,
        endDate: null,
        startInput: '',
        endInput: '',
        startYearMonth: startOfMonth(new Date),
        endYearMonth: add(startOfMonth(new Date), { months: 1 }),
      }
    }
    return {
      startDate: props.value[0],
      endDate: props.value[1],
      startInput: formatDate(props.value[0]),
      endInput: formatDate(props.value[1]),
      startYearMonth: startOfMonth(props.value[0]),
      endYearMonth: isSameMonth(props.value[0], props.value[1]) ? add(startOfMonth(props.value[1]), { months: 1 }) : props.value[1],
    }
  }, [props.value])
  const [innerValue, setInnerValue, resetInnerValue] = usePendingValue<DateRangePickerInnerValue>(initialInnerValue, (value) => {
    if (value.startDate && value.endDate) {
      handleValueOnChange([value.startDate, value.endDate])
      setActive(false)
    }
  }, { resetWhenInitialValueChanged: true })

  const selectedDates = useMemo(() => {
    return compact([innerValue.startDate, innerValue.endDate])
  }, [innerValue.endDate, innerValue.startDate])

  const handleValueOnChange = useCallback((value: [Date, Date] | null) => {
    const sortedValue = value?.sort((i, j) => Number(i) - Number(j)) || null
    props.onChange(sortedValue)
  }, [props])
  /**
   *
   */
  const handleInputOnSubmit = useCallback((type: 'start' | 'end') => {
    if (innerValue.startDate && innerValue.endDate) {
      const originalInput = formatDate(props.value && (type === 'start' ? props.value[0] : props.value[1]))
      const input = type === 'start' ? innerValue.startInput : innerValue.endInput
      if (originalInput === input) return;
      try {
        if (input === '') {
          handleValueOnChange(null)
        } else {
          const result = tryParseDateFromString(input)
          handleValueOnChange(type === 'start' ? [result, innerValue.endDate] : [innerValue.startDate, result])
        }
      } catch {
        resetInnerValue()
      }
    }
  }, [handleValueOnChange, innerValue.endInput, innerValue.endDate, innerValue.startInput, innerValue.startDate, props.value, resetInnerValue])
  /**
   * handle user change year or month in YearMonthSelect.
   */
  const handleStartYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const startYearMonthDate = value
      let endYearMonthDate = oldValue.endYearMonth
      if (!isBefore(startYearMonthDate, endYearMonthDate)) {
        endYearMonthDate = add(startYearMonthDate, { months: 1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  const handleEndYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const endYearMonthDate = value
      let startYearMonthDate = oldValue.startYearMonth
      if (!isAfter(endYearMonthDate, startYearMonthDate)) {
        startYearMonthDate = add(endYearMonthDate, { months: -1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  /**
   * handle user select date in DateSelect.
   */
  const handleDateSelect = useCallback((value: Date) => {
    let shouldSubmit = false
    let newStartValue = innerValue.startDate
    let newEndValue = innerValue.endDate
    if (
      (newStartValue !== null && newEndValue !== null) ||
      (newStartValue === null && newEndValue === null)
    ) {
      if (whichFocusing === 'end') {
        newStartValue = null
        newEndValue = value
      } else {
        newStartValue = value
        newEndValue = null
      }
    } else {
      if (newStartValue === null) newStartValue = value
      if (newEndValue === null) newEndValue = value
      if (isAfter(newStartValue, newEndValue)) {
        const tmp = new Date(newStartValue)
        newStartValue = new Date(newEndValue)
        newEndValue = tmp
      }
      shouldSubmit = true
    }
    setInnerValue((oldValue) => {
      return {
        ...oldValue,
        startDate: newStartValue,
        startInput: formatDate(newStartValue),
        endDate: newEndValue,
        endInput: formatDate(newEndValue),
      }
    }, shouldSubmit)
  }, [innerValue.endDate, innerValue.startDate, setInnerValue, whichFocusing])

  return (
    <Root>
      <Popover
        placement={'bottom-start'}
        active={active}
        onClickAway={() => { setActive(false); resetInnerValue(); }}
        activator={
          <Activator
            onClick={() => {
              setActive(true)
              setTimeout(() => {
                if (whichFocusing === undefined && startInputRef.current) {
                  startInputRef.current.focus()
                }
              }, 0)
            }}
          >
            <TextField
              placeholder={props.startPlaceholder}
              value={innerValue.startInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, startInput: value })) }}
              customize={{
                Input: {
                  ref: startInputRef,
                  onFocus: () => {
                    setWhichFocusing('start')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('start')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('start')
                    }
                  }
                }
              }}
            />
            <ConnectIcon />
            <TextField
              placeholder={props.endPlaceholder}
              value={innerValue.endInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, endInput: value })) }}
              customize={{
                Input: {
                  ref: endInputRef,
                  onFocus: () => {
                    setWhichFocusing('end')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('end')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('end')
                    }
                  }
                }
              }}
            />
            <CalendarIcon />
          </Activator>
        }
      >
        <Container>
          <Toolbar>
            {props.shortcuts && (
              <DateTimeShortcut
                options={props.shortcuts}
                onSelect={(value) => {
                  handleValueOnChange(value)
                  setActive(false)
                }}
              />
            )}
          </Toolbar>
          <Main tabIndex={-1}>
            <StartSection>
              <YearMonthSelect
                value={innerValue.startYearMonth}
                onChange={handleStartYearMonthSelect}
              />
              <DateSelect
                yearMonth={innerValue.startYearMonth}
                selectedDates={selectedDates}
                onSelect={handleDateSelect}
                hoverDate={hoverDate}
                onHoverDateChange={(date) => { setHoverDate(date) }}
              />
            </StartSection>
            <EndSection>
              <YearMonthSelect
                value={innerValue.endYearMonth}
                onChange={handleEndYearMonthSelect}
              />
              <DateSelect
                yearMonth={innerValue.endYearMonth}
                selectedDates={selectedDates}
                onSelect={handleDateSelect}
                hoverDate={hoverDate}
                onHoverDateChange={(date) => { setHoverDate(date) }}
              />
            </EndSection>
          </Main>
        </Container>
      </Popover>
    </Root>
  )
})
Example #22
Source File: DonationTable.tsx    From frontend with MIT License 4 votes vote down vote up
function DonationTable({ donations }: DonationTableProps) {
  const { t, i18n } = useTranslation()
  const [fromDate, setFromDate] = React.useState<Date | null>(null)
  const [toDate, setToDate] = React.useState<Date | null>(null)
  const [monthly, setMonthly] = React.useState(true)
  const [oneTime, setOneTime] = React.useState(true)
  const filteredByTypeDonations = useMemo(() => {
    if (monthly && oneTime) {
      return donations
    }
    if (!monthly && !oneTime) {
      return []
    }
    if (monthly) {
      return donations?.filter((d) => d.type !== 'donation')
    }
    if (oneTime) {
      return donations?.filter((d) => d.type === 'donation')
    }
    return donations
  }, [donations, monthly, oneTime])
  const filteredDonations = useMemo(() => {
    if (!fromDate && !toDate) {
      return filteredByTypeDonations
    }
    if (fromDate && toDate) {
      return filteredByTypeDonations?.filter((d) => {
        const createdAtDate = parseISO(d.createdAt)
        return isAfter(createdAtDate, fromDate) && isBefore(createdAtDate, toDate)
      })
    }
    if (fromDate) {
      return filteredByTypeDonations?.filter((d) => {
        const createdAtDate = parseISO(d.createdAt)
        return isAfter(createdAtDate, fromDate)
      })
    }
    if (toDate) {
      return filteredByTypeDonations?.filter((d) => {
        const createdAtDate = parseISO(d.createdAt)
        return isBefore(createdAtDate, toDate)
      })
    }
  }, [filteredByTypeDonations, fromDate, toDate])
  return (
    <Card sx={{ padding: theme.spacing(2) }}>
      <Grid container alignItems={'flex-start'} spacing={theme.spacing(2)}>
        <Grid item xs={6} sm={3}>
          <CheckboxLabel>{t('profile:donations.oneTime')}</CheckboxLabel>
          <Checkbox
            onChange={(e, checked) => setOneTime(checked)}
            checked={oneTime}
            name="oneTime"
          />
        </Grid>
        <Grid item xs={6} sm={3}>
          <CheckboxLabel>{t('profile:donations.monthly')}</CheckboxLabel>
          <Checkbox
            onChange={(e, checked) => setMonthly(checked)}
            checked={monthly}
            name="monthly"
          />
        </Grid>
        <LocalizationProvider
          locale={i18n.language === 'bg' ? bg : enUS}
          dateAdapter={AdapterDateFns}>
          <Grid item xs={12} sm={3}>
            <DatePicker
              label={t('profile:donations.fromDate')}
              value={fromDate}
              onChange={setFromDate}
              renderInput={(params) => <TextField size="small" {...params} />}
            />
          </Grid>
          <Grid item xs={12} sm={3}>
            <DatePicker
              label={t('profile:donations.toDate')}
              value={toDate}
              onChange={setToDate}
              renderInput={(params) => <TextField size="small" {...params} />}
            />
          </Grid>
        </LocalizationProvider>
      </Grid>
      {filteredDonations?.length ? (
        <TableContainer>
          <Table sx={{ minWidth: 650, backgroundColor: 'white' }} aria-label="simple table">
            <TableHead>
              <TableRow>
                <TableCell>№</TableCell>
                <TableCell>{t('profile:donations.date')}</TableCell>
                <TableCell>{t('profile:donations.type')}</TableCell>
                <TableCell>{t('profile:donations.cause')}</TableCell>
                <TableCell>{t('profile:donations.amount')}</TableCell>
                <TableCell>{t('profile:donations.certificate')}</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {filteredDonations.map((donation, index) => (
                <TableRow key={index} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
                  <TableCell component="th" scope="row">
                    {index + 1}
                  </TableCell>
                  <TableCell>
                    {format(parseISO(donation.createdAt), 'd.LL.yyyy', {
                      locale: i18n.language === 'bg' ? bg : enUS,
                    })}
                  </TableCell>
                  <TableCell>
                    <Avatar sx={{ background: darken(theme.palette.secondary.main, 0.175) }}>
                      <StarIcon />
                    </Avatar>
                  </TableCell>
                  <TableCell>{donation.targetVault.campaign.title}</TableCell>
                  <TableCell>{money(donation.amount)}</TableCell>
                  <TableCell>
                    <Button variant="outlined" disabled={donation.status != 'succeeded'}>
                      <Link target="_blank" href={routes.donation.viewCertificate(donation.id)}>
                        {t('profile:donations.download')} <ArrowForwardIcon />
                      </Link>
                    </Button>
                  </TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </TableContainer>
      ) : (
        <Box sx={{ fontSize: 20, mt: 4 }}>Към момента няма направени дарения</Box>
      )}
    </Card>
  )
}