Skip to content

added SWR for members table #1

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
17 changes: 16 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"reactstrap": "^9.1.2"
"reactstrap": "^9.1.2",
"swr": "^1.3.0"
},
"devDependencies": {
"@types/react": "^18.0.15",
Expand All @@ -37,4 +38,4 @@
"typescript": "^4.6.4",
"vite": "^3.0.0"
}
}
}
112 changes: 61 additions & 51 deletions src/screens/manage-members/manage-members.info.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,72 @@
import { ColumnField } from "../../models/table.model";
import { FilterMatchMode } from 'primereact/api';
import { FilterMatchMode } from "primereact/api";
import { DataTableFilterMeta } from "primereact/datatable";
import ContactModel from "../../models/contact.model";

class ManageMembersInfo {
static getColumns(): ColumnField[] {
return [
{ field: "contact_id", header: "Contact ID" },
{ field: "first_name", header: "First Name" },
{ field: "uh_id", header: "UH ID" },
{ field: "email", header: "Email" },
{ field: "last_name", header: "Last Name" },
{ field: "phone_number", header: "Phone Number" },
{ field: "shirt_size_id", header: "Shirt Size" },
{ field: "timestamp", header: "Timestamp" }
];
}

static getColumns(): ColumnField[] {
return [
{ field: "contact_id", header: "Contact ID" },
{ field: "first_name", header: "First Name" },
{ field: "uh_id", header: "UH ID" },
{ field: "email", header: "Email" },
{ field: "last_name", header: "Last Name" },
{ field: "phone_number", header: "Phone Number" },
{ field: "shirt_size_id", header: "Shirt Size" },
{ field: "timestamp", header: "Timestamp" },
];
}
static getDefaultColumns(): ColumnField[] {
return [
{ field: "first_name", header: "First Name" },
{ field: "last_name", header: "Last Name" },
{ field: "uh_id", header: "UH ID" },
{ field: "email", header: "Email" },
{ field: "phone_number", header: "Phone Number" },
{ field: "shirt_size_id", header: "Shirt Size" }
];
}

static getDefaultColumns(): ColumnField[] {
return [
{ field: "first_name", header: "First Name" },
{ field: "last_name", header: "Last Name" },
{ field: "uh_id", header: "UH ID" },
{ field: "email", header: "Email" },
{ field: "phone_number", header: "Phone Number" },
{ field: "shirt_size_id", header: "Shirt Size" },
];
}
static getShirtSizeOptions() {
return [
{ label: "Not Available", value: undefined },
{ label: "Extra Small", value: "xs" },
{ label: "Small", value: "sm" },
{ label: "Medium", value: "md" },
{ label: "Large", value: "lg" },
{ label: "Extra Large", value: "xl" },
{ label: "Double Extra Large", value: "xxl" },
{ label: "Triple Extra Large", value: "xxxl" }
];
}

static getShirtSizeOptions() {
return [
{ label: "Not Available", value: undefined },
{ label: "Extra Small", value: "xs" },
{ label: "Small", value: "sm" },
{ label: "Medium", value: "md" },
{ label: "Large", value: "lg" },
{ label: "Extra Large", value: "xl" },
{ label: "Double Extra Large", value: "xxl" },
{ label: "Triple Extra Large", value: "xxxl" },
]
}

static getTableFilters(): DataTableFilterMeta {
return { global: { value: null, matchMode: FilterMatchMode.CONTAINS } };
}

static getGlobalFilterFields(): string[] {
return [
"first_name",
"last_name",
"uh_id",
"email",
"phone_number",
"shirt_size_id",
]
}
static getTableFilters(): DataTableFilterMeta {
return {
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
contact_id: { value: null, matchMode: FilterMatchMode.CONTAINS },
timestamp: { value: null, matchMode: FilterMatchMode.CONTAINS },
last_name: { value: null, matchMode: FilterMatchMode.CONTAINS },
first_name: { value: null, matchMode: FilterMatchMode.CONTAINS },
uh_id: { value: null, matchMode: FilterMatchMode.CONTAINS },
email: { value: null, matchMode: FilterMatchMode.CONTAINS },
phone_number: { value: null, matchMode: FilterMatchMode.CONTAINS },
shirt_size_id: { value: null, matchMode: FilterMatchMode.CONTAINS }
};
}

static getGlobalFilterFields(): string[] {
return [
"first_name",
"last_name",
"uh_id",
"email",
"phone_number",
"shirt_size_id",
"contact_id",
"timestamp"
];
}
}

export default ManageMembersInfo;
export default ManageMembersInfo;
150 changes: 114 additions & 36 deletions src/screens/manage-members/manage-members.screen.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,62 @@
import { useEffect, useState } from "react";

import BasicTable from "../../components/basic-table";
import { useState } from "react";
import { Toolbar } from "primereact/toolbar";
import { DataTable, DataTableFilterMeta, DataTableFilterMetaData } from "primereact/datatable";
import { Column } from "primereact/column";
import { InputText } from "primereact/inputtext";
import { Button } from "primereact/button";

import useSWR from "swr";
import { API_BASE_URL } from "../../utils/config";
import ManageMemberDialog from "./manage-members.dialog";
import ContactModel from "../../models/contact.model";
import MemberModel from "../../models/contact.model";
import MemberService from "../../services/MemberService";
import ManageMembersInfo from "./manage-members.info";
import { fetcher } from "../../utils/fetcher";
import { ColumnField } from "../../models/table.model";
import { MultiSelect, MultiSelectChangeParams } from "primereact/multiselect";

const ManageMembers = () => {
const [members, setMembers] = useState<ContactModel[]>([]);
const [selected, setSelected] = useState<MemberModel | undefined>(undefined);

const [memberDialogVisible, setMemberDialogVisible] =
useState<boolean>(false);

useEffect(() => {
const getData = async () => {
const data = await MemberService.getMembers();
setMembers(data);
};
const { data: members, error } = useSWR<ContactModel[], Error>(
`${API_BASE_URL}/member/all`,
fetcher
);

getData();
}, []);
const [selected, setSelected] = useState<MemberModel | undefined>(undefined);
const [filters, setFilters] = useState<DataTableFilterMeta>(ManageMembersInfo.getTableFilters());
const [globalFilterValue, setGlobalFilterValue] = useState("");
const [memberDialogVisible, setMemberDialogVisible] = useState<boolean>(false);
const [selectedColumns, setSelectedColumns] = useState<ColumnField[]>(ManageMembersInfo.getDefaultColumns() ?? ManageMembersInfo.getColumns());

const onAddClick = () => {
console.log("Add clicked!");
};

const onColumnToggle = (event: MultiSelectChangeParams) => {
const selectedColumns = event.value as ColumnField[];
const orderedSelectedColumns = ManageMembersInfo.getColumns().filter(
(col) =>
selectedColumns.some((sCol: ColumnField) => sCol.field === col.field)
);
setSelectedColumns(orderedSelectedColumns);
};

const onManageClick = () => {
setMemberDialogVisible(true);
};

const toolbarLeft = (
<>
<Button label="Add" icon="pi pi-plus" onClick={onAddClick} />
<Button
disabled={!members}
label="Add"
icon="pi pi-plus"
onClick={onAddClick}
/>
<div className="s-1" />
<Button
label="Manage"
icon="pi pi-cog"
disabled={!selected}
disabled={!selected || !members}
className="p-button-secondary"
onClick={onManageClick}
/>
Expand All @@ -59,27 +74,90 @@ const ManageMembers = () => {
);
};

const onGlobalFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
const _filters = { ...filters };
(_filters.global as DataTableFilterMetaData).value = value;

setFilters(_filters);
setGlobalFilterValue(value);
};

const renderHeader = () => {
return (
<div className="flex justify-content-between">
<MultiSelect
value={selectedColumns}
options={ManageMembersInfo.getColumns()}
optionLabel="header"
onChange={onColumnToggle}
style={{ width: '20em' }}
/>
<span className="p-input-icon-left">
<i className="pi pi-search" />
<InputText
value={globalFilterValue}
onChange={onGlobalFilterChange}
placeholder="Keyword Search"
/>
</span>
</div>
);
};
const header = renderHeader();

return (
<main>
<h1>Members</h1>
<p>Create and manage members</p>

<Toolbar left={toolbarLeft} />

<div className="s-1" />

<BasicTable
rows={members}
columns={ManageMembersInfo.getColumns()}
defaultColumns={ManageMembersInfo.getDefaultColumns()}
selected={selected}
setSelected={setSelected}
filters={ManageMembersInfo.getTableFilters()}
globalFilterFields={ManageMembersInfo.getGlobalFilterFields()}
height="450px"
/>

{renderMemberDialog()}
{error ? (
<>
<h1 className="text-danger flex align-items-center">
<i className="pi pi-times-circle" style={{ fontSize: "1em" }} />
<div style={{ marginLeft: ".5em" }}>Error Fetching Members</div>
</h1>
</>
) : (
<>
<Toolbar left={toolbarLeft} />

<div className="s-1" />
<DataTable
value={members}
paginator
showGridlines
stripedRows
responsiveLayout="scroll"
paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords}"
rows={10}
rowsPerPageOptions={[10, 20, 50]}
loading={!members}
dataKey="uh_id"
filters={filters}
globalFilterFields={ManageMembersInfo.getGlobalFilterFields()}
emptyMessage="No members found."
header={header}
selectionMode="single"
selection={selected}
onSelectionChange={(e) => setSelected(e.value)}
>
{selectedColumns.map(({ field, header }) => (
<Column
key={field}
field={field}
header={header}
filter
filterPlaceholder={`Search by ${header}`}
filterField={field}
sortable
/>
))}
</DataTable>

{renderMemberDialog()}
</>
)}
</main>
);
};
Expand Down
3 changes: 3 additions & 0 deletions src/utils/fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import axios from "axios";

export const fetcher = (url: string) => axios.get(url).then((res) => res.data);