Improve TypeScript definitions for app context (#4603)

* Refactor AppContext with improved TypeScript definitions for actions
* Sort teams on setAvailableTeams action
* Add case insensitive sort by name to team table on team settings page
This commit is contained in:
gillespi314 2022-03-16 10:01:35 -05:00 committed by GitHub
parent 3e07f4a626
commit a693d82c11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 99 additions and 49 deletions

View file

@ -1,10 +1,49 @@
import React, { createContext, useReducer, ReactNode } from "react";
import { IUser } from "interfaces/user";
import { IConfig } from "interfaces/config";
import { ITeamSummary } from "interfaces/team";
import permissions from "utilities/permissions";
import { IEnrollSecret } from "interfaces/enroll_secret";
import { ITeamSummary } from "interfaces/team";
import { IUser } from "interfaces/user";
import permissions from "utilities/permissions";
import sort from "utilities/sort";
enum ACTIONS {
SET_AVAILABLE_TEAMS = "SET_AVAILABLE_TEAMS",
SET_CURRENT_USER = "SET_CURRENT_USER",
SET_CURRENT_TEAM = "SET_CURRENT_TEAM",
SET_CONFIG = "SET_CONFIG",
SET_ENROLL_SECRET = "SET_ENROLL_SECRET",
}
interface ISetAvailableTeamsAction {
type: ACTIONS.SET_AVAILABLE_TEAMS;
availableTeams: ITeamSummary[];
}
interface ISetConfigAction {
type: ACTIONS.SET_CONFIG;
config: IConfig;
}
interface ISetCurrentTeamAction {
type: ACTIONS.SET_CURRENT_TEAM;
currentTeam: ITeamSummary | undefined;
}
interface ISetCurrentUserAction {
type: ACTIONS.SET_CURRENT_USER;
currentUser: IUser;
}
interface ISetEnrollSecretAction {
type: ACTIONS.SET_ENROLL_SECRET;
enrollSecret: IEnrollSecret[];
}
type IAction =
| ISetAvailableTeamsAction
| ISetConfigAction
| ISetCurrentTeamAction
| ISetCurrentUserAction
| ISetEnrollSecretAction;
type Props = {
children: ReactNode;
@ -70,21 +109,17 @@ const initialState = {
setEnrollSecret: () => null,
};
const actions = {
SET_AVAILABLE_TEAMS: "SET_AVAILABLE_TEAMS",
SET_CURRENT_USER: "SET_CURRENT_USER",
SET_CURRENT_TEAM: "SET_CURRENT_TEAM",
SET_CONFIG: "SET_CONFIG",
SET_ENROLL_SECRET: "SET_ENROLL_SECRET",
};
const detectPreview = () => {
return window.location.origin === "http://localhost:1337";
};
// helper function - this is run every
// time currentUser, currentTeam, config, or teamId is changed
const setPermissions = (user: IUser, config: IConfig, teamId = 0) => {
const setPermissions = (
user: IUser | null,
config: IConfig | null,
teamId = 0
) => {
if (!user || !config) {
return {};
}
@ -113,44 +148,52 @@ const setPermissions = (user: IUser, config: IConfig, teamId = 0) => {
};
};
const reducer = (state: any, action: any) => {
const reducer = (state: InitialStateType, action: IAction) => {
switch (action.type) {
case actions.SET_AVAILABLE_TEAMS:
case ACTIONS.SET_AVAILABLE_TEAMS: {
const { availableTeams } = action;
return {
...state,
availableTeams: action.availableTeams,
};
case actions.SET_CURRENT_USER:
return {
...state,
currentUser: action.currentUser,
...setPermissions(
action.currentUser,
state.config,
state.currentTeam?.id
availableTeams: availableTeams.sort(
(a: ITeamSummary, b: ITeamSummary) =>
sort.caseInsensitiveAsc(a.name, b.name)
),
};
case actions.SET_CURRENT_TEAM:
}
case ACTIONS.SET_CURRENT_USER: {
const { currentUser } = action;
return {
...state,
currentTeam: action.currentTeam,
...setPermissions(
state.currentUser,
state.config,
action.currentTeam?.id
),
currentUser,
...setPermissions(currentUser, state.config, state.currentTeam?.id),
};
case actions.SET_CONFIG:
}
case ACTIONS.SET_CURRENT_TEAM: {
const { currentTeam } = action;
return {
...state,
config: action.config,
...setPermissions(state.currentUser, action.config),
currentTeam,
...setPermissions(state.currentUser, state.config, currentTeam?.id),
};
case actions.SET_ENROLL_SECRET:
}
case ACTIONS.SET_CONFIG: {
const { config } = action;
return {
...state,
enrollSecret: action.enrollSecret,
config,
...setPermissions(state.currentUser, config),
};
}
case ACTIONS.SET_ENROLL_SECRET: {
const { enrollSecret } = action;
return {
...state,
enrollSecret,
};
}
default:
return state;
}
@ -184,19 +227,19 @@ const AppProvider = ({ children }: Props): JSX.Element => {
isOnlyObserver: state.isOnlyObserver,
isNoAccess: state.isNoAccess,
setAvailableTeams: (availableTeams: ITeamSummary[]) => {
dispatch({ type: actions.SET_AVAILABLE_TEAMS, availableTeams });
dispatch({ type: ACTIONS.SET_AVAILABLE_TEAMS, availableTeams });
},
setCurrentUser: (currentUser: IUser) => {
dispatch({ type: actions.SET_CURRENT_USER, currentUser });
dispatch({ type: ACTIONS.SET_CURRENT_USER, currentUser });
},
setCurrentTeam: (currentTeam: ITeamSummary | undefined) => {
dispatch({ type: actions.SET_CURRENT_TEAM, currentTeam });
dispatch({ type: ACTIONS.SET_CURRENT_TEAM, currentTeam });
},
setConfig: (config: IConfig) => {
dispatch({ type: actions.SET_CONFIG, config });
dispatch({ type: ACTIONS.SET_CONFIG, config });
},
setEnrollSecret: (enrollSecret: IEnrollSecret[]) => {
dispatch({ type: actions.SET_ENROLL_SECRET, enrollSecret });
dispatch({ type: ACTIONS.SET_ENROLL_SECRET, enrollSecret });
},
};

View file

@ -20,7 +20,7 @@ export interface ITeamSummary {
id: number;
name: string;
description?: string;
host_count: number;
host_count?: number;
}
/**
@ -32,8 +32,8 @@ export interface ITeam extends ITeamSummary {
count?: number;
created_at?: string;
agent_options?: any;
user_count: number;
host_count: number;
user_count?: number;
host_count?: number;
secrets?: IEnrollSecret[];
role?: string; // role value is included when the team is in the context of a user
}

View file

@ -382,8 +382,11 @@ const TeamDetailsWrapper = ({
}
const hostCount = currentTeam.host_count;
const hostsTotalDisplay =
hostCount >= 2 ? `${hostCount} hosts` : `${hostCount} host`;
let hostsTotalDisplay: string | undefined;
if (hostCount !== undefined) {
hostsTotalDisplay =
hostCount === 1 ? `${hostCount} host` : `${hostCount} hosts`;
}
const adminTeams = isGlobalAdmin
? availableTeams
@ -417,9 +420,11 @@ const TeamDetailsWrapper = ({
onClose={handleTeamMenuClose}
/>
)}
<span className={`${baseClass}__host-count`}>
{hostsTotalDisplay}
</span>
{!!hostsTotalDisplay && (
<span className={`${baseClass}__host-count`}>
{hostsTotalDisplay}
</span>
)}
</div>
<div className={`${baseClass}__team-actions`}>
<Button onClick={toggleAddHostsModal}>Add hosts</Button>

View file

@ -40,6 +40,7 @@ interface IDataColumn {
| ((props: IDropdownCellProps) => JSX.Element);
disableHidden?: boolean;
disableSortBy?: boolean;
sortType?: string;
}
interface ITeamTableData extends ITeam {
@ -56,6 +57,7 @@ const generateTableHeaders = (
title: "Name",
Header: "Name",
disableSortBy: true,
sortType: "caseInsensitive",
accessor: "name",
Cell: (cellProps: ICellProps) => (
<LinkCell