Skip to content

Commit

Permalink
feat: refactor of my dashboard and use react-table (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
NithinKuruba authored Sep 8, 2023
1 parent 6d6880f commit 7deb8d5
Show file tree
Hide file tree
Showing 12 changed files with 695 additions and 210 deletions.
36 changes: 36 additions & 0 deletions app/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import styled from 'styled-components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';

const Wrapper = styled.div`
width: 100%;
position: relative;
`;

const Icon = styled.i`
position: absolute;
right: 0.5em;
top: 0.5em;
color: grey;
`;

const Input = styled.input`
width: 100%;
border: 2px solid #606060;
padding: 0.3em 0.5em;
border-radius: 0.25em;
`;

function SearchBar(props: any) {
return (
<Wrapper>
<Input type="text" maxLength={100} {...props} />
<Icon>
<FontAwesomeIcon icon={faMagnifyingGlass} />
</Icon>
</Wrapper>
);
}

export default SearchBar;
165 changes: 165 additions & 0 deletions app/components/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import {
getCoreRowModel,
useReactTable,
flexRender,
getPaginationRowModel,
FilterFn,
getFilteredRowModel,
} from '@tanstack/react-table';
import type { ColumnDef } from '@tanstack/react-table';
import Pagination from 'react-bootstrap/Pagination';
import styled from 'styled-components';
import Select from 'react-select';
import Grid from '@button-inc/bcgov-theme/Grid';
import StyledTable from 'html-components/Table';
import { useState } from 'react';
import { rankItem } from '@tanstack/match-sorter-utils';
import SearchBar from './SearchBar';

const StyledPagination = styled(Pagination)`
margin: 0 !important;
& li {
margin: 0 !important;
}
`;

const PageInfo = styled.li`
padding-left: 5px;
line-height: 40px;
`;

const StyledSelect = styled(Select)`
width: 150px;
display: inline-block;
`;

const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
// Rank the item
const itemRank = rankItem(row.getValue(columnId), value);

// Store the itemRank info
addMeta({
itemRank,
});

// Return if the item should be filtered in/out
return itemRank.passed;
};

interface ReactTableProps<T extends object> {
data: T[];
columns: ColumnDef<T>[];
noDataFoundMessage: string;
}
const Table = <T extends object>({ data, columns, noDataFoundMessage = 'no data found.' }: ReactTableProps<T>) => {
const numOfItemsPerPage = () => {
const options = [5, 10, 15, 20, 25, 30].map((val) => {
return { value: val, label: `${val} per page` };
});

return options;
};

const [globalFilter, setGlobalFilter] = useState('');

const table = useReactTable({
data,
columns,
initialState: {
pagination: {
pageSize: 5,
},
},
state: {
globalFilter,
},
onGlobalFilterChange: setGlobalFilter,
globalFilterFn: fuzzyFilter,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});

return (
<>
<SearchBar
type="text"
size="small"
maxLength="1000"
value={globalFilter ?? ''}
onChange={(v: any) => setGlobalFilter(String(v.target.value))}
placeholder="Search all columns..."
style={{ marginTop: '10px', maxWidth: '25%' }}
/>
<StyledTable>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.length > 0 ? (
table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
))
) : (
<tr key={noDataFoundMessage}>
<td colSpan={10}>{noDataFoundMessage}</td>{' '}
</tr>
)}
</tbody>
</StyledTable>
<Grid cols={12}>
<Grid.Row collapse="992" gutter={[]} align="center">
<Grid.Col span={8}>
<StyledPagination>
<Pagination.Item
key="prev"
disabled={!table.getCanPreviousPage()}
onClick={() => {
table.previousPage();
}}
>
Previous
</Pagination.Item>
<Pagination.Item
key="next"
disabled={!table.getCanNextPage()}
onClick={() => {
table.nextPage();
}}
>
Next
</Pagination.Item>
<PageInfo>{`${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`}</PageInfo>
</StyledPagination>
</Grid.Col>
<Grid.Col span={4}>
<div style={{ textAlign: 'right' }} data-testid="page-select">
<StyledSelect
menuPosition="fixed"
value={table.getState().pagination.pageSize}
options={numOfItemsPerPage()}
onChange={(e: any) => {
table.setPageSize(Number(e.value));
}}
></StyledSelect>
</div>
</Grid.Col>
</Grid.Row>
</Grid>
</>
);
};

export default Table;
21 changes: 7 additions & 14 deletions app/controllers/realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export async function getAllowedRealms(session: any) {
id,
realm,
product_name,
openshift_namespace,
product_owner_email,
product_owner_idir_userid,
technical_contact_email,
Expand All @@ -27,8 +26,10 @@ export async function getAllowedRealms(session: any) {
division,
branch,
created_at,
updated_at
FROM rosters WHERE LOWER(technical_contact_idir_userid)=LOWER($1) OR LOWER(product_owner_idir_userid)=LOWER($1) ORDER BY id ASC
updated_at,
rc_channel,
rc_channel_owned_by
FROM rosters WHERE LOWER(technical_contact_idir_userid)=LOWER($1) OR LOWER(second_technical_contact_idir_userid)=LOWER($1) OR LOWER(product_owner_idir_userid)=LOWER($1) ORDER BY id ASC
`,
[username],
);
Expand All @@ -41,18 +42,10 @@ export async function getAllowedRealms(session: any) {
if (kcAdminClient) {
for (let x = 0; x < result?.rows.length; x++) {
const realm = result?.rows[x];
const [realmData, poName, techName, secTechName] = await Promise.all([
kcCore.getRealm(realm.realm),
kcCore.getIdirUserName(realm.product_owner_idir_userid),
kcCore.getIdirUserName(realm.technical_contact_idir_userid),
kcCore.getIdirUserName(realm.second_technical_contact_idir_userid),
]);

realm.product_owner_name = poName;
realm.technical_contact_name = techName;
realm.second_technical_contact_name = secTechName;
realm.displayName = realmData?.displayName || '';
const [realmData] = await Promise.all([kcCore.getRealm(realm.realm)]);
realm.idps = realmData?.identityProviders?.map((v) => v.displayName || v.alias) || [];
const distinctProviders = new Set(realmData?.identityProviders?.map((v) => v.providerId) || []);
realm.protocol = Array.from(distinctProviders);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion app/html-components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const Table = styled.table`
box-shadow: none;
text-align: left;
border-collapse: separate;
border-spacing: 0 5px;
border-spacing: 0;
table-layout: fixed;
& thead {
font-size: 12px;
Expand Down Expand Up @@ -38,6 +39,7 @@ const Table = styled.table`
& th,
& td {
border: none;
word-wrap: break-word;
}
`;

Expand Down
4 changes: 4 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
"@tanstack/match-sorter-utils": "^8.8.4",
"@tanstack/react-table": "^8.9.7",
"axios": "^0.27.2",
"bootstrap": "^5.1.3",
"easy-soap-request": "^4.7.0",
Expand All @@ -24,6 +26,7 @@
"lodash.get": "^4.4.2",
"lodash.map": "^4.6.0",
"next": "12.1.6",
"node-cron": "^3.0.2",
"pg": "^8.7.3",
"pg-format": "^1.0.4",
"qs": "^6.10.3",
Expand All @@ -32,6 +35,7 @@
"react-dom": "18.1.0",
"react-hook-form": "^7.31.3",
"react-loader-spinner": "^6.0.0-0",
"react-select": "^5.7.4",
"react-typography": "^0.16.20",
"semantic-ui-react": "^2.1.3",
"store2": "^2.13.2",
Expand Down
60 changes: 20 additions & 40 deletions app/page-partials/my-dashboard/RealmEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,14 @@ function RealmTable({ alert, realm, currentUser, onUpdate, onCancel }: Props) {
<h2>Realm Name: {realm.realm}</h2>
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="displayName">
Realm Descriptive Name
<InfoPopover>This name is the name you've configured in custom realm setting</InfoPopover>
Realm
<InfoPopover>Name of the realm you've configured in custom realm setting</InfoPopover>
</label>
<input
type="text"
placeholder="Realm Descriptive Name"
placeholder="Realm Name"
disabled
{...register('displayName', { required: false, minLength: 2, maxLength: 1000 })}
{...register('realm', { required: false, minLength: 2, maxLength: 1000 })}
/>
<label htmlFor="product_name">
Product Name<span className="required">*</span>
Expand All @@ -196,19 +196,7 @@ function RealmTable({ alert, realm, currentUser, onUpdate, onCancel }: Props) {
<input
type="text"
placeholder="Product Name"
{...register('product_name', { required: false, minLength: 2, maxLength: 1000 })}
/>
<label htmlFor="openshift_namespace">
Openshift Namespace<span className="required">*</span>
<InfoPopover>
If this realm is tied to OS, provide the license plate, if this realm is shared with multiple products type{' '}
<strong>Various</strong>. If OS is not applicable, please help type <strong>NA</strong>
</InfoPopover>
</label>
<input
type="text"
placeholder="Openshift Namespace"
{...register('openshift_namespace', { required: false, minLength: 2, maxLength: 1000 })}
{...register('product_name', { required: true, minLength: 2, maxLength: 1000 })}
/>
{loading ? (
<AlignCenter>
Expand Down Expand Up @@ -283,7 +271,7 @@ function RealmTable({ alert, realm, currentUser, onUpdate, onCancel }: Props) {
type="text"
placeholder="Product Owner Email"
disabled={!isAdmin && !isPO}
{...register('product_owner_email', { required: false, pattern: /^\S+@\S+$/i })}
{...register('product_owner_email', { required: true, pattern: /^\S+@\S+$/i })}
/>
<label htmlFor="product_owner_idir_userid">
Product Owner Idir
Expand Down Expand Up @@ -316,7 +304,7 @@ function RealmTable({ alert, realm, currentUser, onUpdate, onCancel }: Props) {
<input
type="text"
placeholder="Technical Contact Email"
{...register('technical_contact_email', { required: false, pattern: /^\S+@\S+$/i })}
{...register('technical_contact_email', { required: true, pattern: /^\S+@\S+$/i })}
/>
<label htmlFor="technical_contact_idir_userid">
Technical Contact Idir
Expand All @@ -339,7 +327,7 @@ function RealmTable({ alert, realm, currentUser, onUpdate, onCancel }: Props) {
<input
type="text"
placeholder="Second Technical Contact Email"
{...register('second_technical_contact_email', { required: false, pattern: /^\S+@\S+$/i })}
{...register('second_technical_contact_email', { required: true, pattern: /^\S+@\S+$/i })}
/>
<label htmlFor="second_technical_contact_idir_userid">
Second Technical Contact Idir(optional)
Expand All @@ -355,29 +343,21 @@ function RealmTable({ alert, realm, currentUser, onUpdate, onCancel }: Props) {
/>
{isAdmin && (
<>
{/* ADMIN NOTE 1 */}
<label htmlFor="admin_note_1">Admin Note 1</label>
<textarea
rows={6}
placeholder="Admin Note 1"
{/* Rocket.Chat Channel */}
<label htmlFor="rc_channel">Rocket.Chat Channel</label>
<input
type="text"
placeholder="Rocket.Chat Channel"
disabled={!isAdmin && !isPO}
{...register('admin_note_1', { required: false, minLength: 2, maxLength: 2000 })}
{...register('rc_channel', { required: false, minLength: 2, maxLength: 2000 })}
/>
{/* ADMIN NOTE 2 */}
<label htmlFor="admin_note_2">Admin Note 2</label>
<textarea
rows={6}
placeholder="Admin Note 2"
disabled={!isAdmin && !isPO}
{...register('admin_note_2', { required: false, minLength: 2, maxLength: 2000 })}
/>
{/* Next Step */}
<label htmlFor="next_steps">Next Step</label>
<textarea
rows={6}
placeholder="Next Step"
{/* Rocket.Chat Channel Owner */}
<label htmlFor="rc_channel_owned_by">Rocket.Chat Channel Owner</label>
<input
type="text"
placeholder="Rocket.Chat Channel Owner"
disabled={!isAdmin && !isPO}
{...register('next_steps', { required: false, minLength: 2, maxLength: 2000 })}
{...register('rc_channel_owned_by', { required: false, minLength: 2, maxLength: 2000 })}
/>
{/* Material To Send */}
<label htmlFor="material_to_send">Material To Send</label>
Expand Down
Loading

0 comments on commit 7deb8d5

Please sign in to comment.