Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactoring of my dashboard iteration #1 #108

Merged
merged 1 commit into from
Sep 8, 2023
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
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
Loading