Skip to content

Commit

Permalink
feat(EstimateComboBox): added the ability to select the annual quarter
Browse files Browse the repository at this point in the history
  • Loading branch information
troff8 authored and Troff8 committed Jun 19, 2023
1 parent cafaea1 commit d598801
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 36 deletions.
3 changes: 3 additions & 0 deletions prisma/migrations/20230615101320_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Estimate" ALTER COLUMN "q" DROP NOT NULL,
ALTER COLUMN "date" DROP NOT NULL;
4 changes: 2 additions & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ model Project {

model Estimate {
id Int @id @default(autoincrement())
q String
q String?
y String
date String
date String?
goal EstimateToGoal[]
goalId String
activity Activity @relation(fields: [activityId], references: [id])
Expand Down
105 changes: 76 additions & 29 deletions src/components/EstimateComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface EstimateComboBoxProps {
placeholder?: string;
error?: React.ComponentProps<typeof ComboBox>['error'];

onChange?: (estimate?: { date: string; q: string; y: string }) => void;
onChange?: (estimate?: { date?: string; q?: string; y: string }) => void;
}

const StyledInput = styled(Input)`
Expand All @@ -49,6 +49,10 @@ const StyledButtonsContainer = styled.div`
grid-gap: 6px;
margin: 6px 2px;
`;
const StyledItemsYearContainer = styled.div`
display: flex;
margin: 0px 2px;
`;

const StyledCleanButton = styled.div`
display: none;
Expand Down Expand Up @@ -109,9 +113,10 @@ export const EstimateComboBox = React.forwardRef<HTMLDivElement, EstimateComboBo
const { locale } = usePageContext();
const inputVal = parseLocaleDate(value?.date || defaultValuePlaceholder?.date, { locale });
const [inputState, setInputState] = useState(inputVal ? createLocaleDate(inputVal, { locale }) : '');
const [selectedQ, setSelectedQ] = useState(value?.q || defaultValuePlaceholder?.q);
const [selectedQ, setSelectedQ] = useState(value?.q || defaultValuePlaceholder?.q || undefined);
const [changed, setChanged] = useState(false);
const [buttonText, setButtonText] = useState(text);
const [currentYear, setCurrentYear] = useState(inputVal.getFullYear().toString());

const quarterInfo = useMemo(() => {
const quarterInfo: Record<string, { date: string; q: string; y?: string }> = {
Expand All @@ -138,54 +143,77 @@ export const EstimateComboBox = React.forwardRef<HTMLDivElement, EstimateComboBo
return quarterInfo;
}, [defaultValuePlaceholder, locale]);

const handlerOnKeyDown = useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
e.preventDefault();
}, []);

const onQButtonClick = useCallback(
(nextQ: string) => () => {
(nextQ: string | undefined) => () => {
setSelectedQ(nextQ);
setChanged(true);
if (nextQ === undefined) {
setButtonText(currentYear);
} else {
setChanged(true);
}
},
[],
[currentYear],
);

const onInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setChanged(true);
setInputState(e.target.value);
const onInputYearChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setCurrentYear(e.target.value);
}, []);

useEffect(() => {
if (Object.keys(quarterInfo).length) {
setInputState(createLocaleDate(parseLocaleDate(quarterInfo[selectedQ].date, { locale }), { locale }));
}
}, [selectedQ, locale, quarterInfo]);
const onInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setChanged(true);
setInputState(e.target.value);
if (isValidDate(e.target.value)) {
setSelectedQ(quarterFromDate(parseLocaleDate(inputState, { locale })));
}
},
[inputState, locale],
);

useEffect(() => {
if (isValidDate(inputState)) {
setSelectedQ(quarterFromDate(parseLocaleDate(inputState, { locale })));
if (selectedQ !== undefined) {
setInputState(
createLocaleDate(
parseLocaleDate(quarterInfo[selectedQ].date, {
locale,
}),
{ locale },
),
);
}
}, [inputState, locale]);
}, [selectedQ, locale, quarterInfo, currentYear]);

useEffect(() => {
if (changed && isValidDate(inputState)) {
if (changed && isValidDate(inputState) && selectedQ !== undefined) {
const v = createValue(inputState, locale);
setButtonText(formatEstimate(v, locale));

onChange?.(v);
}
}, [changed, selectedQ, inputState, locale, onChange]);
if (selectedQ === undefined) {
onChange?.({ q: undefined, y: currentYear, date: undefined });
}
}, [changed, selectedQ, inputState, locale, onChange, currentYear]);

useEffect(() => {
if (value) {
setButtonText(
formatEstimate(
{
q: quarterInfo[value.q].q,
y: quarterInfo[value.q].y || value.y,
date: quarterInfo[value.q].date,
},
locale,
),
selectedQ === undefined
? currentYear
: formatEstimate(
{
q: quarterInfo[selectedQ].q,
y: quarterInfo[selectedQ].y || value.y,
date: quarterInfo[selectedQ].date,
},
locale,
),
);
}
}, [value, locale, quarterInfo]);
}, [value, locale, quarterInfo, selectedQ, currentYear]);

const onCleanClick = useCallback(() => {
setButtonText(text);
Expand Down Expand Up @@ -243,7 +271,26 @@ export const EstimateComboBox = React.forwardRef<HTMLDivElement, EstimateComboBo
/>
)}
renderItems={(children) => (
<StyledButtonsContainer>{children as React.ReactNode}</StyledButtonsContainer>
<>
<StyledButtonsContainer>{children as React.ReactNode}</StyledButtonsContainer>
<StyledItemsYearContainer>
<Input
onChange={onInputYearChange}
value={currentYear}
brick={'right'}
type={'number'}
min={quarterInfo[defaultValuePlaceholder.q].y}
onKeyDown={handlerOnKeyDown}
/>
<CheckableButton
text={'Y'}
checked={selectedQ === undefined}
brick="left"
size={'s'}
onClick={onQButtonClick(undefined)}
/>
</StyledItemsYearContainer>
</>
)}
/>
);
Expand Down
8 changes: 4 additions & 4 deletions src/schema/goal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export const goalCommonSchema = z.object({
priority: z.string().nullable().optional(),
estimate: z
.object({
date: z.string(),
q: z.string(),
date: z.string().optional().nullable(),
q: z.string().optional().nullable(),
y: z.string(),
id: z.number().nullish(),
})
Expand Down Expand Up @@ -132,8 +132,8 @@ export const goalUpdateSchema = z.object({
priority: z.string().nullable(),
estimate: z
.object({
date: z.string(),
q: z.string(),
date: z.string().optional().nullable(),
q: z.string().optional().nullable(),
y: z.string(),
id: z.number().nullish(),
})
Expand Down
3 changes: 3 additions & 0 deletions src/utils/dateTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ const createValue = (date: string | Date, locale: TLocale) => {
};

export const formatEstimate = (estimate: ReturnType<typeof createValue>, locale: TLocale) => {
if (!estimate.q && !estimate.date) {
return estimate.y;
}
const { date, q, y } = createValue(estimate.date, locale);

return date === createLocaleDate(endOfQuarter(q), { locale }) ? `${q}/${y}` : date;
Expand Down
7 changes: 6 additions & 1 deletion src/utils/estimateToString.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Estimate } from '@prisma/client';

export const estimateToString = (estimate: { q: Estimate['q']; y: Estimate['y'] }) => `${estimate.q}/${estimate.y}`;
export const estimateToString = (estimate: { q: Estimate['q']; y: Estimate['y'] }) => {
if (!estimate.q) {
return estimate.y;
}
return `${estimate.q}/${estimate.y}`;
};

0 comments on commit d598801

Please sign in to comment.