Skip to content

Commit

Permalink
feat: Added Grouping functionality for Review Data (#446)
Browse files Browse the repository at this point in the history
  • Loading branch information
chavda-bhavik authored Dec 26, 2023
2 parents 01911ef + 9c00b5e commit 30cddef
Show file tree
Hide file tree
Showing 16 changed files with 155 additions and 105 deletions.
14 changes: 11 additions & 3 deletions apps/api/src/app/review/review.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { APIMessages } from '@shared/constants';
import { RecordEntity, UploadEntity } from '@impler/dal';
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
import { validateUploadStatus } from '@shared/helpers/upload.helpers';
import { Defaults, ACCESS_KEY_NAME, UploadStatusEnum } from '@impler/shared';
import { Defaults, ACCESS_KEY_NAME, UploadStatusEnum, ReviewDataTypesEnum } from '@impler/shared';

import {
DoReview,
Expand Down Expand Up @@ -53,21 +53,29 @@ export class ReviewController {
type: Number,
description: 'Size of data to return',
})
@ApiQuery({
name: 'type',
required: false,
type: String,
enum: ReviewDataTypesEnum,
description: 'Type of data filter to apply',
})
@ApiOkResponse({
description: 'Paginated reviewed data',
type: PaginationResponseDto,
})
async getReview(
@Param('uploadId') _uploadId: string,
@Query('page') page = Defaults.ONE,
@Query('limit') limit = Defaults.PAGE_LIMIT
@Query('limit') limit = Defaults.PAGE_LIMIT,
@Query('type') type = ReviewDataTypesEnum.ALL
) {
const uploadData = await this.getUpload.execute({
uploadId: _uploadId,
});
if (!uploadData) throw new BadRequestException(APIMessages.UPLOAD_NOT_FOUND);

return await this.getFileInvalidData.execute(_uploadId, page, limit, uploadData.totalRecords);
return await this.getFileInvalidData.execute(_uploadId, page, limit, uploadData.totalRecords, type);
}

@Post(':uploadId')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class DoReReview extends BaseReview {
const uniqueFields = (JSON.parse(uploadInfo.customSchema) as ITemplateSchemaItem[])
.filter((column) => column.isUnique)
.map((column) => column.key);
const uniqueFieldData = await this.dalService.getFieldData(_uploadId, uniqueFields);
const uniqueFieldData = uniqueFields.length ? await this.dalService.getFieldData(_uploadId, uniqueFields) : [];

uniqueFieldData.forEach((item) => {
uniqueFields.forEach((field) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { Injectable } from '@nestjs/common';
import { DalService } from '@impler/dal';
import { PaginationResult } from '@impler/shared';
import { PaginationResult, ReviewDataTypesEnum } from '@impler/shared';

@Injectable()
export class GetUploadData {
constructor(private dalService: DalService) {}

async execute(_uploadId: string, page: number, limit: number, totalRecords: number): Promise<PaginationResult> {
const data = await this.dalService.getRecords(_uploadId, page, limit);
async execute(
_uploadId: string,
page: number,
limit: number,
totalRecords: number,
type: ReviewDataTypesEnum
): Promise<PaginationResult> {
const data = await this.dalService.getRecords(_uploadId, page, limit, type);

return {
data,
Expand Down
53 changes: 18 additions & 35 deletions apps/widget/src/components/widget/Phases/Phase3/Phase3.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import { Group, Text } from '@mantine/core';
import { Stack } from '@mantine/core';
import { HotTable } from '@handsontable/react';
import { useRef, useState, useEffect } from 'react';

import { PhasesEum } from '@types';
import { colors, TEXTS, variables } from '@config';
import { IUpload, numberFormatter, replaceVariablesInString } from '@impler/shared';
import { CheckIcon, Warning } from '@icons';
import { IUpload, numberFormatter } from '@impler/shared';
import { logAmplitudeEvent } from '@amplitude';
import { usePhase3 } from '@hooks/Phase3/usePhase3';

import useStyles from './Styles';
import { Pagination } from '@ui/Pagination';
import { ConfirmModal } from '../ConfirmModal';
import { Table } from 'components/Common/Table';
import { Footer } from 'components/Common/Footer';

import { Pagination } from '@ui/Pagination';
import { LoadingOverlay } from '@ui/LoadingOverlay';
import { logAmplitudeEvent } from '@amplitude';
import { SegmentedControl } from '@ui/SegmentedControl';

interface IPhase3Props {
onNextClick: (uploadData: IUpload) => void;
onPrevClick: () => void;
}

export function Phase3(props: IPhase3Props) {
const { classes } = useStyles();
const tableRef = useRef<HotTable>(null);
const { onNextClick, onPrevClick } = props;
const {
page,
type,
headings,
columnDefs,
totalPages,
reviewData,
onTypeChange,
reReviewData,
updateRecord,
onPageChange,
Expand All @@ -53,7 +53,7 @@ export function Phase3(props: IPhase3Props) {
useEffect(() => {
// setting wrapper height
setTableWrapperDimentions({
height: tableWrapperRef.current.getBoundingClientRect().height - 40,
height: tableWrapperRef.current.getBoundingClientRect().height - 50,
width: tableWrapperRef.current.getBoundingClientRect().width,
});
}, []);
Expand All @@ -66,32 +66,15 @@ export function Phase3(props: IPhase3Props) {
return (
<>
<LoadingOverlay visible={isReviewDataLoading || isDoReviewLoading || isConfirmReviewLoading} />
{typeof invalidRecords === 'undefined' || typeof totalRecords === 'undefined' ? null : (
<Group align="center" spacing="xs">
{invalidRecords === variables.baseIndex ? (
<>
<CheckIcon fill={colors.success} className={classes.successIcon} />
<Text size="xs" inline color={colors.success} style={{ flex: 1 }}>
{replaceVariablesInString(TEXTS.PHASE3.VALID_DATA_INFO, {
total: numberFormatter(totalRecords),
})}
</Text>
</>
) : (
<>
<Warning fill={colors.red} className={classes.warningIcon} />
<Text size="xs" inline color={colors.red} style={{ flex: 1 }}>
{replaceVariablesInString(TEXTS.PHASE3.INVALID_DATA_INFO, {
total: numberFormatter(totalRecords),
invalid: numberFormatter(invalidRecords),
})}
</Text>
</>
)}
</Group>
)}

<div ref={tableWrapperRef} style={{ flexGrow: 1 }}>
<Stack ref={tableWrapperRef} style={{ flexGrow: 1 }} spacing="xs" align="flex-start">
<SegmentedControl
value={type}
onChange={onTypeChange}
allDataLength={numberFormatter(totalRecords)}
invalidDataLength={numberFormatter(invalidRecords)}
validDataLength={numberFormatter(totalRecords - invalidRecords)}
/>
<Table
width={tableWrapperDimensions.width}
height={tableWrapperDimensions.height}
Expand All @@ -116,7 +99,7 @@ export function Phase3(props: IPhase3Props) {
headings={headings}
columnDefs={columnDefs}
/>
</div>
</Stack>
<Pagination page={page} total={totalPages} onChange={onPageChange} />

<Footer
Expand Down
34 changes: 0 additions & 34 deletions apps/widget/src/components/widget/Phases/Phase3/Styles.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions apps/widget/src/config/texts.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ export const TEXTS = {
NAME_IN_SHEET_TITLE: 'Column in your sheet',
},
PHASE3: {
VALID_DATA_INFO: 'All {total} row(s) are found valid!',
INVALID_DATA_INFO:
'Out of {total} row(s), {invalid} row(s) have invalid data. Please update the data and try again.',
EXPORT_DATA: 'Export Data',
RE_REVIEW_DATA: 'Re-Review Data',
COMPLETE: 'Complete',
Expand Down
1 change: 0 additions & 1 deletion apps/widget/src/design-system/Modal/Modal.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { createStyles, MantineTheme } from '@mantine/core';
import React from 'react';

export const getHeaderStyles = (theme: MantineTheme): React.CSSProperties => ({
marginBottom: theme.spacing.xs,
marginRight: 0,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { colors } from '../../config/colors.config';
import { createStyles } from '@mantine/core';

export default createStyles(() => {
return {
label: {
marginBottom: 0,
},
control: {
'&:last-of-type .mantine-SegmentedControl-label': {
color: colors.red,
},
},
};
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ReviewDataTypesEnum } from '@impler/shared';
import { SegmentedControl as MantineSegmentedControl } from '@mantine/core';

import useStyles from './SegmentedControl.styles';

interface SegmentedControlProps {
value: string;
allDataLength?: string;
validDataLength?: string;
invalidDataLength?: string;
onChange: (value: ReviewDataTypesEnum) => void;
}

export function SegmentedControl({
onChange,
value,
allDataLength = '',
validDataLength = '',
invalidDataLength = '',
}: SegmentedControlProps) {
const { classes } = useStyles();

return (
<MantineSegmentedControl
data={[
{ value: 'all', label: `All ${allDataLength}` },
{ value: 'valid', label: `Valid ${validDataLength}` },
{ value: 'invalid', label: `Invalid ${invalidDataLength}` },
]}
radius="xl"
value={value}
onChange={onChange}
classNames={classes}
/>
);
}
1 change: 1 addition & 0 deletions apps/widget/src/design-system/SegmentedControl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SegmentedControl';
54 changes: 37 additions & 17 deletions apps/widget/src/hooks/Phase3/usePhase3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import { HotItemSchema } from '@types';
import { logAmplitudeEvent } from '@amplitude';
import { useAPIState } from '@store/api.context';
import { useAppState } from '@store/app.context';
import { ColumnTypesEnum, ISchemaColumn, IErrorObject, IReviewData, IUpload, IRecord } from '@impler/shared';
import {
ColumnTypesEnum,
ISchemaColumn,
IErrorObject,
IReviewData,
IUpload,
IRecord,
ReviewDataTypesEnum,
} from '@impler/shared';

interface IUsePhase3Props {
onNext: (uploadData: IUpload) => void;
Expand All @@ -23,6 +31,7 @@ export function usePhase3({ onNext }: IUsePhase3Props) {
const [reviewData, setReviewData] = useState<IRecord[]>([]);
const [columnDefs, setColumnDefs] = useState<HotItemSchema[]>([]);
const [totalPages, setTotalPages] = useState<number>(defaultPage);
const [type, setType] = useState<ReviewDataTypesEnum>(ReviewDataTypesEnum.ALL);
const [showAllDataValidModal, setShowAllDataValidModal] = useState<boolean | undefined>(undefined);

useQuery<unknown, IErrorObject, ISchemaColumn[], [string, string]>(
Expand Down Expand Up @@ -101,21 +110,26 @@ export function usePhase3({ onNext }: IUsePhase3Props) {
const { mutate: refetchReviewData, isLoading: isReviewDataLoading } = useMutation<
IReviewData,
IErrorObject,
number,
[number, string],
[string]
>(['review'], (reviewPageNumber) => api.getReviewData(uploadInfo._id, reviewPageNumber), {
onSuccess(data) {
setReviewData(data.data);
logAmplitudeEvent('VALIDATE', {
invalidRecords: data.totalRecords,
});
setPage(Number(data.page));
setTotalPages(data.totalPages);
},
onError(error: IErrorObject) {
notifier.showError({ message: error.message, title: error.error });
},
});
>(
['review'],
([reviewPageNumber, reviewDataType]) =>
api.getReviewData({ uploadId: uploadInfo._id, page: reviewPageNumber, type: reviewDataType }),
{
onSuccess(data) {
setReviewData(data.data);
logAmplitudeEvent('VALIDATE', {
invalidRecords: data.totalRecords,
});
setPage(Number(data.page));
setTotalPages(data.totalPages);
},
onError(error: IErrorObject) {
notifier.showError({ message: error.message, title: error.error });
},
}
);
const { refetch: reReviewData, isFetching: isDoReviewLoading } = useQuery<
unknown,
IErrorObject,
Expand All @@ -126,7 +140,7 @@ export function usePhase3({ onNext }: IUsePhase3Props) {
staleTime: 0,
onSuccess() {
fetchUploadInfo();
refetchReviewData(defaultPage);
refetchReviewData([defaultPage, type]);
},
onError(error: IErrorObject) {
notifier.showError({ message: error.message, title: error.error });
Expand Down Expand Up @@ -160,18 +174,24 @@ export function usePhase3({ onNext }: IUsePhase3Props) {
api.updateRecord(uploadInfo._id, record)
);

const onTypeChange = (newType: ReviewDataTypesEnum) => {
setType(newType);
refetchReviewData([page, newType]);
};
const onPageChange = (newPageNumber: number) => {
refetchReviewData(newPageNumber);
refetchReviewData([newPageNumber, type]);
};

return {
page,
type,
headings,
totalPages,
columnDefs,
reReviewData,
updateRecord,
onPageChange,
onTypeChange,
setReviewData,
isDoReviewLoading,
isReviewDataLoading,
Expand Down
Loading

0 comments on commit 30cddef

Please sign in to comment.