Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Feature/fix issue #40

Merged
merged 4 commits into from
Jan 6, 2021
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
3 changes: 1 addition & 2 deletions package-lock.json

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

5 changes: 5 additions & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export const DAY_FORMAT = "MM/DD/YYYY";
*/
export const TEAM_MEMBERS_PER_PAGE = 5;

/**
* How many teams show per page by default
*/
export const TEAMS_PER_PAGE = 20;

/**
* How many position candidates show per page by default
*/
Expand Down
7 changes: 1 addition & 6 deletions src/routes/MyTeamsDetails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@ import TeamSummary from "./components/TeamSummary";
import TeamMembers from "./components/TeamMembers";
import TeamPositions from "./components/TeamPositions";
import { useAsync } from "react-use";
import {
getAuthUserTokens,
} from "@topcoder/micro-frontends-navbar-app";

const MyTeamsDetails = ({ teamId }) => {
const authUserTokens = useAsync(getAuthUserTokens);
const tokenV3 = authUserTokens.value ? authUserTokens.value.tokenV3 : null;
const [team, loadingError] = useData(getTeamById, tokenV3, teamId);
const [team, loadingError] = useData(getTeamById, teamId);

return (
<LayoutContainer>
Expand Down
84 changes: 69 additions & 15 deletions src/routes/MyTeamsList/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,89 @@
*
* Page for the list of teams.
*/
import React from "react";
import React, { useCallback, useState, useEffect } from "react";
import _ from "lodash";
import LayoutContainer from "components/LayoutContainer";
import PageHeader from "components/PageHeader";
import { useData } from "../../hooks/useData";
import Input from "components/Input";
import Pagination from "components/Pagination";
import { getMyTeams } from "../../services/teams";
import TeamCard from "./components/TeamCard";
import TeamCardGrid from "./components/TeamCardGrid";
import LoadingIndicator from "../../components/LoadingIndicator";
import { useAsync } from "react-use";
import {
getAuthUserTokens,
} from "@topcoder/micro-frontends-navbar-app";
import { useDebounce } from "react-use";
import { TEAMS_PER_PAGE } from "constants";
import "./styles.module.scss";

const MyTeamsList = () => {
const authUserTokens = useAsync(getAuthUserTokens);
const tokenV3 = authUserTokens.value ? authUserTokens.value.tokenV3 : null;
const [myTeams, loadingError] = useData(getMyTeams, tokenV3);
let [myTeams, setMyTeams] = useState(null);
const [filter, setFilter] = useState("");
const [tempFilter, setTempFilter] = React.useState('');
const [loadingError, setLoadingError] = useState(false);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const onFilterChange = (evt) => {
setTempFilter(evt.target.value)
}

useDebounce((value) => {
console.log('xxxx', value)
setFilter(tempFilter);
setPage(1);
}, 200, [tempFilter]);

useEffect(() => {
setMyTeams(null);
getMyTeams(filter, page, TEAMS_PER_PAGE)
.then((response) => {
setMyTeams(response.data);
setTotal(response.headers["x-total"]);
})
.catch((responseError) => {
setLoadingError(responseError);
});
}, [filter, page]);

const onPageClick = useCallback(
(newPage) => {
setPage(newPage);
},
[setPage]
);

return (
<LayoutContainer>
<PageHeader title="My Teams" />
<PageHeader
title="My Teams"
aside={
<Input
placeholder="Filter by team name"
styleName="filter-input"
onChange={onFilterChange}
/>
}
/>
{myTeams && myTeams.length === 0 && (<div styleName="empty">No teams found</div>)}
{!myTeams ? (
<LoadingIndicator error={loadingError && loadingError.toString()} />
) : (
<TeamCardGrid>
{myTeams.map((team) => (
<TeamCard key={team.id} team={team} />
))}
</TeamCardGrid>
<>
<TeamCardGrid>
{myTeams.map((team) => (
<TeamCard key={team.id} team={team} />
))}
</TeamCardGrid>
{myTeams.length > 0 && (
<div styleName="pagination-wrapper">
<Pagination
total={total}
currentPage={page}
perPage={TEAMS_PER_PAGE}
onPageClick={onPageClick}
/>
</div>
)}
</>
)}
</LayoutContainer>
);
Expand Down
36 changes: 36 additions & 0 deletions src/routes/MyTeamsList/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@import "styles/include";

.filter-input {
width: 380px;
background-color: #FFFFFF;
border: 1px solid #AAAAAA;
border-radius: 6px;
box-sizing: border-box;
color: #2A2A2A;
font-size: 14px;
height: 40px;
line-height: 38px;
outline: none;
padding: 0 15px;

&::placeholder {
color: #AAAAAA;
}
}

.empty {
text-align: center;
}

.pagination-wrapper {
margin-top: 20px;
margin-right: 20px;
display: flex;
justify-content: flex-end;
}

@media (max-width: 650px) {
.filter-input {
width: 100%;
}
}
12 changes: 2 additions & 10 deletions src/routes/PositionDetails/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Position Details page actions
*/
import { getPositionDetails, patchPositionCandidate } from "services/teams";
import { getAuthUserTokens } from "@topcoder/micro-frontends-navbar-app";
import { ACTION_TYPE } from "constants";

/**
Expand All @@ -16,12 +15,7 @@ import { ACTION_TYPE } from "constants";
export const loadPosition = (teamId, positionId) => ({
type: ACTION_TYPE.LOAD_POSITION,
payload: async () => {
const tokens = await getAuthUserTokens();
const response = await getPositionDetails(
tokens.tokenV3,
teamId,
positionId
);
const response = await getPositionDetails(teamId, positionId);

return response.data;
},
Expand All @@ -42,9 +36,7 @@ export const loadPosition = (teamId, positionId) => ({
export const updateCandidate = (candidateId, partialCandidateData) => ({
type: ACTION_TYPE.UPDATE_CANDIDATE,
payload: async () => {
const tokens = await getAuthUserTokens();
const response = await patchPositionCandidate(
tokens.tokenV3,
candidateId,
partialCandidateData
);
Expand All @@ -61,4 +53,4 @@ export const updateCandidate = (candidateId, partialCandidateData) => ({
*/
export const resetPositionState = () => ({
type: ACTION_TYPE.RESET_POSITION_STATE,
})
});
2 changes: 1 addition & 1 deletion src/routes/PositionDetails/hooks/useTeamPositionsState.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const useTeamPositionsState = (teamId, positionId) => {
// clear state when we leave the page
return () => {
dispatch(resetPositionState());
}
};
}, [dispatch, teamId, positionId]);

// bind actions to dispatch method
Expand Down
14 changes: 5 additions & 9 deletions src/routes/PositionDetails/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,10 @@ const reducer = (state = initialState, action) => {
});

case ACTION_TYPE.UPDATE_CANDIDATE_SUCCESS:
return patchCandidateInState(
state,
action.meta.candidateId,
{
updating: false,
...action.payload,
}
);
return patchCandidateInState(state, action.meta.candidateId, {
updating: false,
...action.payload,
});

case ACTION_TYPE.UPDATE_CANDIDATE_ERROR:
return patchCandidateInState(state, action.meta.candidateId, {
Expand All @@ -92,7 +88,7 @@ const reducer = (state = initialState, action) => {
});

default:
return state
return state;
}
};

Expand Down
21 changes: 21 additions & 0 deletions src/services/requestInterceptor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import axios from "axios";
import store from "../store";
import { getAuthUserTokens } from "@topcoder/micro-frontends-navbar-app";

export const axiosInstance = axios.create({
headers: {
"Content-Type": "application/json",
},
});

// request interceptor to pass auth token
axiosInstance.interceptors.request.use((config) => {
return getAuthUserTokens()
.then(({ tokenV3: token }) => {
config.headers["Authorization"] = `Bearer ${token}`;
return config;
})
.catch((err) => {
return config;
});
});
57 changes: 16 additions & 41 deletions src/services/teams.js
Original file line number Diff line number Diff line change
@@ -1,84 +1,59 @@
/**
* Topcoder TaaS Service
*/
import axios from "axios";
import { axiosInstance as axios } from "./requestInterceptor";
import config from "../../config";

/**
* Get my teams.
*
* @param {string} tokenV3 login token
* @param {string|number} name team name
* @param {number} page current page
* @param {number} perPage perPage
*
* @returns {Promise<object[]>} list of teams
*/
export const getMyTeams = (tokenV3) => {
if (!tokenV3) {
return Promise.resolve({
data: null,
});
export const getMyTeams = (name, page = 1, perPage) => {
let query = `page=${page}&perPage=${perPage}`;
if (name) {
query += `&name=${name}`;
}
return axios.get(`${config.API.V5}/taas-teams`, {
headers: { Authorization: `Bearer ${tokenV3}` },
});

return axios.get(`${config.API.V5}/taas-teams?${query}`);
};

/**
* Get team by id.
*
* @param {string} tokenV3 login token
* @param {string|number} teamId team id
*
* @returns {Promise<{}>} team object
*/
export const getTeamById = (tokenV3, teamId) => {
if (!tokenV3) {
return Promise.resolve({
data: null,
});
}
return axios.get(`${config.API.V5}/taas-teams/${teamId}`, {
headers: { Authorization: `Bearer ${tokenV3}` },
});
export const getTeamById = (teamId) => {
return axios.get(`${config.API.V5}/taas-teams/${teamId}`);
};

/**
* Get team position details.
*
* @param {string} tokenV3 login token
* @param {string|number} teamId team id
* @param {string|number} positionId position id
*
* @returns {Promise<object{}>} job object
*/
export const getPositionDetails = (tokenV3, teamId, positionId) => {
if (!tokenV3) {
return Promise.resolve({
data: null,
});
}
return axios.get(`${config.API.V5}/taas-teams/${teamId}/jobs/${positionId}`, {
headers: { Authorization: `Bearer ${tokenV3}` },
});
export const getPositionDetails = (teamId, positionId) => {
return axios.get(`${config.API.V5}/taas-teams/${teamId}/jobs/${positionId}`);
};

/**
* Patch Position Candidate
*
* @param {string} tokenV3 login token
* @param {string} candidateId position candidate id
*
* @returns {Promise<object{}>} position candidate
*/
export const patchPositionCandidate = (
tokenV3,
candidateId,
partialCandidateData
) => {
export const patchPositionCandidate = (candidateId, partialCandidateData) => {
return axios.patch(
`${config.API.V5}/jobCandidates/${candidateId}`,
partialCandidateData,
{
headers: { Authorization: `Bearer ${tokenV3}` },
}
partialCandidateData
);
};
4 changes: 3 additions & 1 deletion src/utils/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,7 @@ export const formatReportIssueUrl = (subject) => {
* @returns {string} request an extension URL
*/
export const formatRequestExtensionUrl = (subject) => {
return `mailto:${EMAIL_REQUEST_EXTENSION}?subject=${encodeURIComponent(subject)}`;
return `mailto:${EMAIL_REQUEST_EXTENSION}?subject=${encodeURIComponent(
subject
)}`;
};