Skip to content

Commit

Permalink
Convert brand store members to TypeScript (#4310)
Browse files Browse the repository at this point in the history
  • Loading branch information
steverydz authored Jun 28, 2023
1 parent 936b869 commit 390b249
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { ChangeEvent, useEffect, useState } from "react";
import { useParams, Navigate } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import {
Expand Down Expand Up @@ -28,33 +28,56 @@ import { fetchInvites } from "../../slices/invitesSlice";

import SectionNav from "../SectionNav";

import type { InvitesSelector } from "../../types/shared";

type Members = {
members: {
members: Array<Member>;
loading: boolean;
notFound: boolean;
};
};

type Member = {
displayname: string;
email: string;
id: string;
roles: string[];
username: string;
current_user: boolean;
};

function Members() {
const members = useSelector(membersSelector);
const membersLoading = useSelector((state) => state.members.loading);
const membersLoading = useSelector((state: Members) => state.members.loading);
const invites = useSelector(invitesSelector);
const brandStoresList = useSelector(brandStoresListSelector);
const invitesLoading = useSelector((state) => state.members.loading);
const membersNotFound = useSelector((state) => state.members.notFound);
const invitesNotFound = useSelector((state) => state.invites.notFound);
const invitesLoading = useSelector((state: Members) => state.members.loading);
const membersNotFound = useSelector(
(state: Members) => state.members.notFound
);
const invitesNotFound = useSelector(
(state: InvitesSelector) => state.invites.notFound
);
const dispatch = useDispatch();
const { id } = useParams();
const [filteredMembers, setFilteredMembers] = useState([]);
const [filteredMembers, setFilteredMembers]: any = useState([]);
const [sidePanelOpen, setSidePanelOpen] = useState(false);
const [newMemberEmail, setNewMemberEmail] = useState("");
const [newMemberRoles, setNewMemberRoles] = useState([]);
const [newMemberRoles, setNewMemberRoles]: any = useState([]);
const [isSaving, setIsSaving] = useState(false);
const [showSuccessNotification, setShowSuccessNotification] = useState(false);
const [showErrorNotification, setShowErrorNotification] = useState(false);
const [showInviteForm, setShowInviteForm] = useState(false);
const [storeName, setStoreName] = useState("");
const [storeName, setStoreName]: any = useState("");
const [memberButtonDisabled, setMemberButtonDisabled] = useState(false);
const [changedMembers, setChangedMembers] = useState([]);
const [notificationText, setNotificationText] = useState(
"Changes have been saved"
);
const [currentMember, setCurrentMember] = useState(null);
const [currentMember, setCurrentMember]: any = useState(null);

const handleInvite = (action) => {
const handleInvite = (action: string) => {
setIsSaving(true);

const memberData = new FormData();
Expand Down Expand Up @@ -90,8 +113,8 @@ function Members() {
setSidePanelOpen(false);
setNewMemberEmail("");
setNewMemberRoles([]);
dispatch(fetchMembers(id));
dispatch(fetchInvites(id));
dispatch(fetchMembers(id as string) as any);
dispatch(fetchInvites(id as string) as any);
setShowSuccessNotification(true);
setNotificationText("Member has been added to the store");
setShowInviteForm(false);
Expand All @@ -112,12 +135,12 @@ function Members() {
});
};

const handleRoleChange = (e) => {
const handleRoleChange = (e: ChangeEvent) => {
const role = e.target.id;
let roles = newMemberRoles;
let roles: Array<string> = newMemberRoles;

if (!roles.includes(role)) {
roles = [].concat(roles, [role]);
roles = [...roles, role];
} else {
roles = roles.filter((item) => item !== role);
}
Expand All @@ -129,8 +152,8 @@ function Members() {
currentMember?.roles.length === 1 && currentMember?.roles.includes("view");

useEffect(() => {
dispatch(fetchMembers(id));
dispatch(fetchInvites(id));
dispatch(fetchMembers(id as string) as any);
dispatch(fetchInvites(id as string) as any);
setStoreName(() => {
const store = brandStoresList.find((item) => item.id === id);

Expand Down Expand Up @@ -175,7 +198,7 @@ function Members() {
) : (
<>
<Row>
<Col size="6">
<Col size={6}>
<Button
onClick={() => {
setSidePanelOpen(true);
Expand All @@ -184,7 +207,7 @@ function Members() {
Add new member
</Button>
</Col>
<Col size="6">
<Col size={6}>
<SearchBox
placeholder="Search and filter"
autocomplete="off"
Expand Down Expand Up @@ -275,7 +298,7 @@ function Members() {
permissions:
</p>
<ul>
{newMemberRoles.map((role) => (
{newMemberRoles.map((role: string) => (
<li key={role}>
<div>{ROLES[role].name}</div>
<small className="u-text-muted">
Expand Down Expand Up @@ -386,7 +409,7 @@ function Members() {
setIsSaving(true);

const memberData = new FormData();
const members = changedMembers.map((m) => {
const members = changedMembers.map((m: Member) => {
return {
email: m.email,
roles: m.roles,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import { MainTable, CheckboxInput } from "@canonical/react-components";
import ROLES from "./memberRoles";

function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
const [members, setMembers] = useState(filteredMembers);
import type { Member } from "../../types/shared";

const checkArrayEqual = (array1, array2) => {
type Props = {
filteredMembers: Array<Member>;
changedMembers: Array<Member>;
setChangedMembers: Function;
};

function MembersTable({
filteredMembers,
changedMembers,
setChangedMembers,
}: Props) {
const [members, setMembers]: any = useState(filteredMembers);

const checkArrayEqual = (array1: Array<string>, array2: Array<string>) => {
if (array1.length !== array2.length) {
return false;
}
Expand All @@ -19,9 +30,9 @@ function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
.every((val, idx) => val === array2Sorted[idx]);
};

const handleRoleChange = (currentMember, role) => {
const updatedMembers = members.map((member) => {
let updatedItem = {};
const handleRoleChange = (currentMember: Member, role: string) => {
const updatedMembers = members.map((member: Member) => {
let updatedItem = { ...member };

if (member.id !== currentMember.id) {
return member;
Expand All @@ -35,33 +46,31 @@ function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
} else {
updatedItem = {
...member,
roles: [].concat(member.roles, [role]),
roles: [...member.roles, role],
};
}

const changedMember = changedMembers.find(
(m) => m.id === currentMember.id
(m: Member) => m.id === currentMember.id
);

const originalMember = filteredMembers.find(
(m) => m.id === currentMember.id
(m: Member) => m.id === currentMember.id
);

if (changedMember) {
if (changedMember && originalMember) {
if (checkArrayEqual(originalMember.roles, updatedItem.roles)) {
setChangedMembers(
changedMembers.filter((m) => m.id !== currentMember.id)
);
} else {
setChangedMembers(
[].concat(
changedMembers.filter((m) => m.id !== currentMember.id),
[updatedItem]
)
);
setChangedMembers([
...changedMembers.filter((m) => m.id !== currentMember.id),
updatedItem,
]);
}
} else {
setChangedMembers([].concat(changedMembers, [updatedItem]));
setChangedMembers([...changedMembers, updatedItem]);
}

return updatedItem;
Expand All @@ -74,11 +83,6 @@ function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
setMembers(filteredMembers);
}, [filteredMembers]);

const mobileLabelStyles = {
display: "inline-block",
marginLeft: "0.5rem",
};

return (
<MainTable
responsive={true}
Expand Down Expand Up @@ -139,7 +143,7 @@ function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
),
},
]}
rows={members.map((member) => {
rows={members.map((member: Member) => {
return {
columns: [
{
Expand All @@ -153,7 +157,6 @@ function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
"aria-label": "Email",
},
{
"aria-label": "Admin",
content: (
<>
<CheckboxInput
Expand All @@ -164,58 +167,47 @@ function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
onChange={() => {
handleRoleChange(member, "admin");
}}
label={<span className="u-hide--large">Admin</span>}
/>
<span className="u-hide--large" style={mobileLabelStyles}>
Admin
</span>
</>
),
},
{
"aria-label": "Reviewer",
content: (
<>
<CheckboxInput
defaultChecked={member.roles.includes("review")}
onChange={() => {
handleRoleChange(member, "review");
}}
label={<span className="u-hide--large">Reviewer</span>}
/>
<span className="u-hide--large" style={mobileLabelStyles}>
Reviewer
</span>
</>
),
},
{
"aria-label": "Viewer",
content: (
<>
<CheckboxInput
defaultChecked={member.roles.includes("view")}
onChange={() => {
handleRoleChange(member, "view");
}}
label={<span className="u-hide--large">Viewer</span>}
/>
<span className="u-hide--large" style={mobileLabelStyles}>
Viewer
</span>
</>
),
},
{
"aria-label": "Publisher",
content: (
<>
<CheckboxInput
defaultChecked={member.roles.includes("access")}
onChange={() => {
handleRoleChange(member, "access");
}}
label={<span className="u-hide--large">Publisher</span>}
/>
<span className="u-hide--large" style={mobileLabelStyles}>
Publisher
</span>
</>
),
},
Expand All @@ -226,10 +218,4 @@ function MembersTable({ filteredMembers, changedMembers, setChangedMembers }) {
);
}

MembersTable.propTypes = {
filteredMembers: PropTypes.array.isRequired,
changedMembers: PropTypes.array.isRequired,
setChangedMembers: PropTypes.func.isRequired,
};

export default MembersTable;
26 changes: 15 additions & 11 deletions static/js/brand-store/types/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,6 @@ export type RouteParams = {
id: string;
};

export type Member = {
displayname: string;
email: string;
id: string;
roles: string[];
username: string;
current_user: boolean;
};

export type BrandStores = {
brandStores: {
brandStoresList: Stores;
Expand All @@ -37,7 +28,7 @@ export type Snaps = {

export type InvitesSelector = {
invites: {
invites: Array<Invite>;
invites: Invites;
loading: boolean;
notFound: boolean;
};
Expand All @@ -52,12 +43,25 @@ export type Invite = {
"expiration-date": string;
};

export type MembersList = Array<Member>;

export type Members = {
members: {
members: Array<Member>;
members: MembersList;
loading: boolean;
notFound: boolean;
};
};

export type Member = {
displayname: string;
email: string;
id: string;
roles: string[];
username: string;
current_user: boolean;
};

export type Store = {
id?: string;
name?: string;
Expand Down

0 comments on commit 390b249

Please sign in to comment.