Skip to content

Commit

Permalink
feat: estimate aliases in new filter
Browse files Browse the repository at this point in the history
  • Loading branch information
asabotovich committed Oct 3, 2023
1 parent 0a8c6bd commit ddcf04c
Show file tree
Hide file tree
Showing 25 changed files with 486 additions and 422 deletions.
2 changes: 1 addition & 1 deletion prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const userPassword = 'taskany';
data: {
mode: 'Global',
title: 'Active',
params: 'stateType=InProgress,NotStarted',
params: 'stateType=InProgress,NotStarted&estimate=@current',
default: true,
},
}),
Expand Down
6 changes: 0 additions & 6 deletions src/components/Estimate/Estimate.i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
{
"Year title": "Year",
"Year clue": "If you are selecting only year it means the deadline will be the end of the year. For example: {end}.",
"Quarter title": "Quarter",
"Quarter clue": "This is the date shortcut: Q1, Q2, Q3, Q4. Currently is {quarter}. End is {end}.",
"Date title": "Strict date",
"Date clue": "Or you can choose a date if you have strict deadline.",
"Date is past": "Selected date is past"
}
6 changes: 0 additions & 6 deletions src/components/Estimate/Estimate.i18n/ru.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
{
"Year title": "Год",
"Year clue": "Если выбрать только год, то датой будет выбран конец года, например: {end}.",
"Quarter title": "Квартал",
"Quarter clue": "Можно указать квартал: Q1, Q2, Q3, Q4. Сейчас {quarter}, заканчивается {end}.",
"Date title": "Дата",
"Date clue": "Или можно указать точную дату вручную, если есть строгий дедлайн.",
"Date is past": "Выбранная дата уже прошла"
}
174 changes: 71 additions & 103 deletions src/components/Estimate/Estimate.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { ReactNode, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { gapXs, gapS, warn0 } from '@taskany/colors';
import { gapXs, warn0 } from '@taskany/colors';
import { nullable, Text } from '@taskany/bricks';
import { IconExclamationCircleSolid } from '@taskany/icons';

import {
createDateRange,
isPastDate,
getQuarterFromDate,
getYearFromDate,
getDateString,
getRelativeQuarterRange,
createLocaleDate,
createYearRange,
} from '../../utils/dateTime';
import { useLocale } from '../../hooks/useLocale';
import { DateType, DateRange } from '../../types/date';
import { EstimateDate } from '../EstimateDate/EstimateDate';
import { EstimateQuarter } from '../EstimateQuarter';
import { EstimateYear } from '../EstimateYear';
import { createDateRange, isPastDate, getQuarterFromDate, getYearFromDate, getDateString } from '../../utils/dateTime';
import { DateType, DateRange, QuartersAliases } from '../../types/date';

import { tr } from './Estimate.i18n';
import { EstimateContextProvider } from './EstimateProvider';

const StyledWrapper = styled.div`
display: flex;
flex-direction: column;
gap: ${gapS};
gap: ${gapXs};
`;

const StyledWarningWrapper = styled.div`
Expand All @@ -41,11 +29,13 @@ const StyledIconExclamationCircleSolid = styled(IconExclamationCircleSolid)`
export type EstimateValue = {
type: DateType;
range: DateRange;
alias?: QuartersAliases;
};

export interface EstimateProps {
value?: EstimateValue;
onChange?: (value?: EstimateValue) => void;
children: ReactNode;
}

export const getReadOnlyFields = (type: EstimateValue['type']) => {
Expand All @@ -58,72 +48,81 @@ export const getReadOnlyFields = (type: EstimateValue['type']) => {
return { year: false, quarter: true, date: true };
};

const currentQuarterRange = getRelativeQuarterRange('current');
const currentQuarter = getQuarterFromDate(currentQuarterRange.end);
const currentYearRange = createYearRange(getYearFromDate(new Date()));

export const Estimate = React.forwardRef<HTMLDivElement, EstimateProps>(({ value, onChange }, ref) => {
const locale = useLocale();
export const Estimate = React.forwardRef<HTMLDivElement, EstimateProps>(({ value, onChange, children }, ref) => {
const [readOnly, setReadOnly] = useState(
value ? getReadOnlyFields(value.type) : { year: true, quarter: true, date: true },
);
const [year, setYear] = useState(value ? getYearFromDate(value.range.end) : undefined);
const [quarter, setQuarter] = useState(value ? getQuarterFromDate(value.range.end) : undefined);
const [quarterAlias, setQuarterAlias] = useState(value?.alias);
const [date, setDate] = useState(value ? value.range.end : undefined);

const onChangeHandler = useCallback(
(newValue?: EstimateValue) => {
const oldStartDate = value && value.range.start ? getDateString(value.range.start) : null;
const newStartDate = newValue && newValue.range.start ? getDateString(newValue.range.start) : null;

const oldEndDate = value ? getDateString(value.range.end) : null;
const newEndDate = newValue ? getDateString(newValue.range.end) : null;

if (value?.type !== newValue?.type || oldStartDate !== newStartDate || oldEndDate !== newEndDate) {
onChange?.(newValue);
}
},
[value, onChange],
);

useEffect(() => {
const countedValue = useMemo(() => {
if (readOnly.quarter && readOnly.year && readOnly.date) {
return;
return value;
}

if (!readOnly.quarter) {
onChangeHandler?.(
year
? {
range: createDateRange(year, quarter),
type: quarter ? 'Quarter' : 'Year',
}
: undefined,
);
} else if (!readOnly.year) {
onChangeHandler?.(
year
? {
range: createDateRange(year),
type: 'Year',
}
: undefined,
);
} else {
onChangeHandler?.(
date
? {
range: { end: date },
type: 'Strict',
}
: undefined,
);
return year
? {
range: createDateRange(year, quarter),
type: quarter ? DateType.Quarter : DateType.Year,
alias: quarter ? quarterAlias : undefined,
}
: undefined;
}
}, [year, quarter, date, onChangeHandler, readOnly]);
if (!readOnly.year) {
return year
? {
range: createDateRange(year),
type: DateType.Quarter,
}
: undefined;
}
return date
? {
range: { end: date },
type: DateType.Strict,
}
: undefined;
}, [year, quarter, date, quarterAlias, readOnly, value]);

useEffect(() => {
const oldStartDate = value && value.range.start ? getDateString(value.range.start) : null;
const newStartDate = countedValue && countedValue.range.start ? getDateString(countedValue.range.start) : null;

const oldEndDate = value ? getDateString(value.range.end) : null;
const newEndDate = countedValue ? getDateString(countedValue.range.end) : null;

if (
value?.type !== countedValue?.type ||
value?.alias !== countedValue?.alias ||
oldStartDate !== newStartDate ||
oldEndDate !== newEndDate
) {
onChange?.(countedValue);
}
}, [countedValue, value, onChange]);

const warning = useMemo(
() => (value && isPastDate(value.range.end) ? { message: tr('Date is past') } : undefined),
[value],
() => (countedValue && isPastDate(countedValue.range.end) ? { message: tr('Date is past') } : undefined),
[countedValue],
);

const context = useMemo(
() => ({
readOnly,
setReadOnly,
year,
setYear,
quarter,
setQuarter,
quarterAlias,
setQuarterAlias,
date,
setDate,
}),
[readOnly, year, quarter, quarterAlias, date],
);

return (
Expand All @@ -136,39 +135,8 @@ export const Estimate = React.forwardRef<HTMLDivElement, EstimateProps>(({ value
</Text>
</StyledWarningWrapper>
))}
<EstimateYear
title={tr('Year title')}
clue={tr
.raw('Year clue', {
end: createLocaleDate(currentYearRange.end, { locale }),
})
.join('')}
value={year}
readOnly={readOnly.year}
onChange={setYear}
setReadOnly={setReadOnly}
/>
<EstimateQuarter
title={tr('Quarter title')}
clue={`${tr
.raw('Quarter clue', {
quarter: currentQuarter,
end: createLocaleDate(currentQuarterRange.end, { locale }),
})
.join('')}`}
value={quarter}
readOnly={readOnly.quarter}
onChange={setQuarter}
setReadOnly={setReadOnly}
/>
<EstimateDate
title={tr('Date title')}
clue={tr('Date clue')}
value={date}
readOnly={readOnly.date}
onChange={setDate}
setReadOnly={setReadOnly}
/>

<EstimateContextProvider value={context}>{children}</EstimateContextProvider>
</StyledWrapper>
);
});
41 changes: 41 additions & 0 deletions src/components/Estimate/EstimateProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useContext, createContext, FC, ReactNode } from 'react';

import { QuartersAliases, QuartersKeys } from '../../types/date';

type ReadOnlyConfig = {
year: boolean;
quarter: boolean;
date: boolean;
};

type EstimateContext = {
readOnly: ReadOnlyConfig;
setReadOnly: (update: ReadOnlyConfig | ((cfg: ReadOnlyConfig) => ReadOnlyConfig)) => void;
year?: number;
setYear: (year?: number) => void;
quarter?: QuartersKeys;
setQuarter: (quarter?: QuartersKeys) => void;
quarterAlias?: QuartersAliases;
setQuarterAlias: (alias?: QuartersAliases) => void;
date?: Date | undefined;
setDate: (date?: Date) => void;
};

const estimateContext = createContext<EstimateContext>({
readOnly: {
year: true,
quarter: true,
date: true,
},
setReadOnly: () => {},
setYear: () => {},
setQuarter: () => {},
setQuarterAlias: () => {},
setDate: () => {},
});

export const useEstimateContext = () => useContext(estimateContext);

export const EstimateContextProvider: FC<{ value: EstimateContext; children: ReactNode }> = ({ value, children }) => (
<estimateContext.Provider value={value}>{children}</estimateContext.Provider>
);
4 changes: 3 additions & 1 deletion src/components/EstimateDate/EstimateDate.i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"Date input mask": "99/99/9999",
"Date input mask placeholder": "mm/dd/yyyy"
"Date input mask placeholder": "mm/dd/yyyy",
"Date title": "Strict date",
"Date clue": "Or you can choose a date if you have strict deadline."
}
4 changes: 3 additions & 1 deletion src/components/EstimateDate/EstimateDate.i18n/ru.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"Date input mask": "99.99.9999",
"Date input mask placeholder": "dd.mm.yyyy"
"Date input mask placeholder": "dd.mm.yyyy",
"Date title": "Дата",
"Date clue": "Или можно указать точную дату вручную, если есть строгий дедлайн."
}
Loading

0 comments on commit ddcf04c

Please sign in to comment.