Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/common/components/Table/EmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { theme } from 'src/app/theme';
import styled from 'styled-components';
import Background from './assets/bg_empty_state.png';

const StyledEmptyState = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
padding-top: ${theme.space.md};
`;

export const EmptyState = ({
colCount,
children,
}: {
colCount: number;
children: React.ReactNode;
}) => (
<tr>
<td colSpan={colCount}>
<StyledEmptyState>
<>
<img
src={Background}
alt="table empty"
style={{ marginBottom: theme.space.lg }}
/>
{children}
</>
</StyledEmptyState>
</td>
</tr>
);
25 changes: 25 additions & 0 deletions src/common/components/Table/LoadingState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Skeleton } from '@appquality/unguess-design-system';

export const LoadingState = ({
colCount,
rowHeight,
rowCount = 10,
}: {
colCount: number;
rowHeight?: string;
rowCount?: number;
}) => (
<tbody>
<tr>
<td colSpan={colCount}>
{[...Array(rowCount)].map(() => (
<Skeleton
height={rowHeight || '30px'}
width="100%"
style={{ borderRadius: 4, marginBottom: 0 }}
/>
))}
</td>
</tr>
</tbody>
);
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 37 additions & 15 deletions src/common/components/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import styled from 'styled-components';
import { SM } from '@appquality/unguess-design-system';
import { theme as appTheme } from 'src/app/theme';
import { TableRow } from './TableRow';
import { LoadingState } from './LoadingState';
import { EmptyState } from './EmptyState';

interface TableData {
id: string;
Expand All @@ -29,6 +31,10 @@ type TableProps<T extends TableData, K extends keyof T> = {
selectedRow?: string | null;
isSticky?: boolean;
maxHeight?: string;
isLoading?: boolean;
loadingRowHeight?: string;
loadingRowCount?: number;
emptyState?: ReactNode;
};

const TableWrapper = styled.div<{ maxHeight?: string }>`
Expand All @@ -54,6 +60,10 @@ const Table = <T extends TableData, K extends keyof T>({
onRowClick,
isSticky,
maxHeight,
isLoading,
loadingRowHeight,
loadingRowCount,
emptyState,
}: TableProps<T, K>) => (
<TableWrapper maxHeight={maxHeight}>
<ZendeskTable>
Expand All @@ -68,21 +78,33 @@ const Table = <T extends TableData, K extends keyof T>({
))}
</HeaderRow>
</StyledHead>
<Body>
{data.map((row) => (
<TableRow
id={row.id}
onClick={onRowClick}
isSelected={row.id === selectedRow}
isHighlighted={row.isHighlighted}
borderColor={row.borderColor}
>
{columns.map((column) => (
<Cell>{row[column.key]}</Cell>
))}
</TableRow>
))}
</Body>
{isLoading ? (
<LoadingState
colCount={columns.length}
rowCount={loadingRowCount}
rowHeight={loadingRowHeight}
/>
) : (
<Body>
{!data || !data.length ? (
<EmptyState colCount={columns.length}>{emptyState}</EmptyState>
) : (
data.map((row) => (
<TableRow
id={row.id}
onClick={onRowClick}
isSelected={row.id === selectedRow}
isHighlighted={row.isHighlighted}
borderColor={row.borderColor}
>
{columns.map((column) => (
<Cell>{row[column.key]}</Cell>
))}
</TableRow>
))
)}
</Body>
)}
</ZendeskTable>
</TableWrapper>
);
Expand Down
12 changes: 11 additions & 1 deletion src/features/bugsPage/bugsPageSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ const bugPageSlice = createSlice({
),
};
},
resetFilters: (state) => {
if (!state.currentCampaign) return;
state.campaigns[state.currentCampaign] = {
...TypeFilter.reset(state.campaigns[state.currentCampaign]),
...SeverityFilter.reset(state.campaigns[state.currentCampaign]),
...ReadFilter.reset(state.campaigns[state.currentCampaign]),
...UniqueFilter.reset(state.campaigns[state.currentCampaign]),
...SearchFilter.reset(),
};
},
},
});

Expand Down Expand Up @@ -121,5 +131,5 @@ export const getCurrentCampaignData = () => {
return campaign;
};

export const { selectCampaign, updateFilters, selectBug } =
export const { selectCampaign, updateFilters, selectBug, resetFilters } =
bugPageSlice.actions;
6 changes: 6 additions & 0 deletions src/features/bugsPage/readFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export type ReadFilterType = {
};

export const ReadFilter = {
reset: (state: ReadFilterType) => ({
read: {
...ReadFilter.getCurrent(state),
selected: undefined,
},
}),
getCurrent: (state?: ReadFilterType) => ({
available: state
? state.read.available
Expand Down
5 changes: 4 additions & 1 deletion src/features/bugsPage/searchFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ export type SearchFilterType = {
};

export const SearchFilter = {
reset: () => ({
search: '',
}),
getCurrent: (state?: SearchFilterType) => state?.search,
setAvailable: (state: SearchFilterType) => ({
search: SearchFilter.getCurrent(state),
}),
filter: (state: SearchFilterType, search?: SearchFilterType['search']) => ({
search,
search: typeof search === 'undefined' ? state.search : search,
}),
getValue: () => {
const bugsPageSlice = useAppSelector((state) => state.bugsPage);
Expand Down
6 changes: 6 additions & 0 deletions src/features/bugsPage/severityFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export type SeverityFilterType = {
};

export const SeverityFilter = {
reset: (state: SeverityFilterType) => ({
severities: {
...SeverityFilter.getCurrent(state),
selected: [],
},
}),
getCurrent: (state?: SeverityFilterType) => ({
available: state ? state.severities.available : [],
selected: state ? state.severities.selected : [],
Expand Down
6 changes: 6 additions & 0 deletions src/features/bugsPage/typeFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export type TypeFilterType = {
};

export const TypeFilter = {
reset: (state: TypeFilterType) => ({
types: {
...TypeFilter.getCurrent(state),
selected: [],
},
}),
getCurrent: (state?: TypeFilterType) => ({
available: state?.types?.available ? state.types.available : [],
selected: state?.types?.selected ? state.types.selected : [],
Expand Down
6 changes: 6 additions & 0 deletions src/features/bugsPage/uniqueFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export type UniqueFilterType = {
};

export const UniqueFilter = {
reset: (state: UniqueFilterType) => ({
unique: {
...UniqueFilter.getCurrent(state),
selected: 'unique' as const,
},
}),
getCurrent: (state?: UniqueFilterType) => ({
available: state
? state.unique.available
Expand Down
47 changes: 47 additions & 0 deletions src/pages/Bugs/BugsTable/SearchEmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Button, LG, MD } from '@appquality/unguess-design-system';
import { useTranslation } from 'react-i18next';
import { useAppDispatch } from 'src/app/hooks';
import { theme } from 'src/app/theme';
import { resetFilters } from 'src/features/bugsPage/bugsPageSlice';

export const SearchEmptyState = ({ searchTerm }: { searchTerm?: string }) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
return (
<>
<LG
isBold
style={{ color: theme.palette.blue[600], marginBottom: theme.space.xs }}
>
{searchTerm
? `${t(
'__PAGE_BUG_SEARCH_EMPTY_STATE_MAIN_SEARCHTERM',
"we couldn't find anything for"
)} "${searchTerm}"`
: t(
'__PAGE_BUG_SEARCH_EMPTY_STATE_MAIN_GENERIC',
"we couldn't find anything with the selected criteria"
)}
</LG>
<MD
style={{ color: theme.palette.grey[500], marginBottom: theme.space.md }}
>
{t(
'__PAGE_BUG_SEARCH_EMPTY_STATE_SUB',
'Try looking for something different'
)}
</MD>
<Button
isPrimary
isPill
themeColor={theme.palette.water[600]}
type="reset"
onClick={() => {
dispatch(resetFilters());
}}
>
{t('__PAGE_BUG_EMPTY_STATE_CTA', 'Reset')}
</Button>
</>
);
};
10 changes: 8 additions & 2 deletions src/pages/Bugs/BugsTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ import {
getSelectedBugId,
selectBug,
} from 'src/features/bugsPage/bugsPageSlice';
import { SearchEmptyState } from './SearchEmptyState';
import { useTableData } from './useTableData';

const BugsTable = ({ campaignId }: { campaignId: number }) => {
const { columns, data, isLoading } = useTableData(campaignId);
const { columns, data, isLoading, filterBy } = useTableData(campaignId);
const dispatch = useAppDispatch();
const currentBugId = getSelectedBugId();
if (isLoading) return <div>Loading...</div>;

return (
<Table
columns={columns}
data={data}
selectedRow={currentBugId ? currentBugId.toString() : null}
onRowClick={(bug_id) => dispatch(selectBug({ bug_id }))}
isSticky
isLoading={isLoading}
loadingRowHeight="70px"
emptyState={
filterBy && <SearchEmptyState searchTerm={filterBy?.search} />
}
/>
);
};
Expand Down
18 changes: 14 additions & 4 deletions src/pages/Bugs/BugsTable/useTableData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@ const columns: ColumnDefinitionType<TableDatum, keyof TableDatum>[] = [
{
key: 'severity',
header: 'Severity',
width: '85px',
width: '90px',
},
{
key: 'bugId',
header: 'Bug ID',
width: '80px',
width: '90px',
},
];

export const useTableData = (campaignId: number) => {
const { t } = useTranslation();

const filterBy = getSelectedFiltersIds();

const {
isLoading,
isFetching,
Expand All @@ -51,6 +52,9 @@ export const useTableData = (campaignId: number) => {
: {}),
},
...(filterBy?.search ? { search: filterBy.search } : {}),

orderBy: 'severity_id',
order: 'DESC',
});

const mapBugsToTableData = useMemo<TableDatum[]>(() => {
Expand Down Expand Up @@ -93,11 +97,17 @@ export const useTableData = (campaignId: number) => {
isHighlighted: !bug.read,
created: bug.created,
updated: bug.updated,
borderColor: theme.colors.bySeverity[bug.severity.name as Severities],
borderColor:
theme.colors.bySeverity[bug.severity.name.toLowerCase() as Severities],
}));
}, [bugs]);

if (isLoading || isFetching || !bugs || !bugs.items)
return { columns, data: [], isLoading: true };
return { columns, data: mapBugsToTableData, isLoading: false };
return {
columns,
data: mapBugsToTableData,
isLoading: false,
filterBy,
};
};
4 changes: 1 addition & 3 deletions src/pages/Bugs/Filters/ReadFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ export const ReadFilter = () => {
}}
>
<Field>
<Select
isPrimary={!!data.read.selected && data.read.selected !== 'all'}
>
<Select isPrimary={data.read.selected === 'unread'}>
{data.read.selected === 'unread'
? t('__BUGS_READ_FILTER_ITEM_UNREAD')
: t('__BUGS_READ_FILTER_ITEM_PLACEHOLDER')}
Expand Down
Loading