diff --git a/src/pages/ExperiencePoints/ExperiencePointsFilters.tsx b/src/pages/ExperiencePoints/ExperiencePointsFilters/index.tsx similarity index 50% rename from src/pages/ExperiencePoints/ExperiencePointsFilters.tsx rename to src/pages/ExperiencePoints/ExperiencePointsFilters/index.tsx index b7b5f35a4..70587ffb0 100644 --- a/src/pages/ExperiencePoints/ExperiencePointsFilters.tsx +++ b/src/pages/ExperiencePoints/ExperiencePointsFilters/index.tsx @@ -6,38 +6,121 @@ import { } from "@appquality/appquality-design-system"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { shallowEqual, useSelector } from "react-redux"; import useDebounce from "src/hooks/useDebounce"; import { setSelectedActivity, setSelectedCampaign, setSelectedDate, -} from "../../redux/experiencePoints/actionCreator"; -import { useAppDispatch } from "../../redux/provider"; +} from "src/redux/experiencePoints/actionCreator"; +import { mapActivityName } from "src/redux/experiencePoints/utils"; +import { useAppDispatch } from "src/redux/provider"; +import { useGetUsersMeExperienceQuery } from "src/services/tryberApi"; +import dateFormatter from "src/utils/dateFormatter"; -interface ExperiencePointsFiltersProps { - campaigns: SelectType.Option[]; - activities: SelectType.Option[]; - dates: SelectType.Option[]; - selectedCampaign?: SelectType.Option; - selectedActivity?: SelectType.Option; - selectedDate?: SelectType.Option; - search?: string; -} +const useSelectValues = () => { + const { t } = useTranslation(); + + const { selectedActivity, selectedCampaign, selectedDate, search } = + useSelector((state: GeneralState) => state.experiencePoints, shallowEqual); + + const { data, isLoading } = useGetUsersMeExperienceQuery({ + searchBy: "note", + search: search, + filterBy: { + campaign: selectedCampaign?.value, + activity: selectedActivity?.value, + date: selectedDate?.value, + }, + }); + if (isLoading || !data) return { campaigns: [], activities: [], dates: [] }; + + const campaigns: SelectType.Option[] = data.results + .filter( + ( + r + ): r is { + id: number; + activity: { id: number }; + campaign: { id: number; title?: string | undefined }; + date: string; + amount: number; + note?: string | undefined; + } => typeof r.campaign.title !== "undefined" + ) + .map((r) => { + return { + value: r.campaign.id.toString(), + label: `CP${r.campaign.id} - ${r.campaign.title}`, + }; + }) + .filter( + (value, index, self) => + index === self.findIndex((t) => t.value === value.value) + ); + + const activities: SelectType.Option[] = data.results + .filter( + ( + r + ): r is { + id: number; + activity: { id: number }; + campaign: { id: number; title?: string | undefined }; + date: string; + amount: number; + note?: string | undefined; + } => typeof r.activity !== "undefined" + ) + .map((r) => { + return { + value: r.activity.id.toString(), + label: mapActivityName(r.activity.id, t) || " ", + }; + }) + .filter( + (value, index, self) => + index === self.findIndex((t) => t.value === value.value) + ); -const ExperiencePointsFilters = ({ - search, - campaigns, - dates, - activities, - selectedCampaign, - selectedActivity, - selectedDate, -}: ExperiencePointsFiltersProps) => { + const dates: SelectType.Option[] = data.results + .filter( + ( + r + ): r is { + id: number; + activity: { id: number }; + campaign: { id: number; title?: string | undefined }; + date: string; + amount: number; + note?: string | undefined; + } => typeof r.date !== "undefined" + ) + .map((r) => { + return { + value: r.date, + label: dateFormatter(r.date), + }; + }) + .filter( + (value, index, self) => + index === self.findIndex((t) => t.value === value.value) + ); + + return { campaigns, activities, dates }; +}; + +const ExperiencePointsFilters = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); + + const { selectedActivity, selectedCampaign, selectedDate, search } = + useSelector((state: GeneralState) => state.experiencePoints, shallowEqual); const [currentSearch, setCurrentSearch] = useState(search); const debouncedSearch = useDebounce(currentSearch, 500); + const { campaigns, activities, dates } = useSelectValues(); + const allCampaign = t("All", { context: "female" }); const allActivities = t("All", { context: "female" }); const allDates = t("All", { context: "female" }); diff --git a/src/pages/ExperiencePoints/ExperiencePointsTable.tsx b/src/pages/ExperiencePoints/ExperiencePointsTable.tsx deleted file mode 100644 index fe21e680a..000000000 --- a/src/pages/ExperiencePoints/ExperiencePointsTable.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { - Table, - Pagination, - TableType, - SortTableSelect, -} from "@appquality/appquality-design-system"; -import { useTranslation } from "react-i18next"; - -interface ExperiencePointsTableProps { - data: TableType.Row[]; - page: number; - setPage: (page: number) => void; - totalEntries: number; - limit: number; - loading: boolean; - columns: TableType.Column[]; - order: ApiComponents["parameters"]["order"]; - orderBy: string; -} - -const ExperiencePointsTable = ({ - data, - page, - setPage, - totalEntries, - limit, - loading, - order, - orderBy, - columns, -}: ExperiencePointsTableProps) => { - const { t } = useTranslation(); - - return ( - <> - - - - t(`Page %current% / %total%`) - .replace("%current%", current.toString()) - .replace("%total%", total ? total.toString() : "0") - } - /> - - ); -}; - -export default ExperiencePointsTable; diff --git a/src/pages/ExperiencePoints/ExperiencePointsTable/index.tsx b/src/pages/ExperiencePoints/ExperiencePointsTable/index.tsx new file mode 100644 index 000000000..75d258ab9 --- /dev/null +++ b/src/pages/ExperiencePoints/ExperiencePointsTable/index.tsx @@ -0,0 +1,66 @@ +import { + Pagination, + SortTableSelect, + Table, +} from "@appquality/appquality-design-system"; +import { useTranslation } from "react-i18next"; +import { shallowEqual, useSelector } from "react-redux"; +import { updateExperiencePointsPagination } from "src/redux/experiencePoints/actionCreator"; +import { useAppDispatch } from "src/store"; +import { useExperiencePointsColumns } from "../columns"; +import { useResetPaginationOnFilterChange } from "./useResetPaginationOnFilterChange"; +import { useRows } from "./useRows"; + +const ExperiencePointsTable = () => { + const limit = 25; + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const { rows, loading, totalEntries } = useRows(); + const columns = useExperiencePointsColumns(); + + const setPage = (newPage: number) => { + const newStart = limit * (newPage - 1); + dispatch(updateExperiencePointsPagination(newStart)); + }; + + useResetPaginationOnFilterChange({ setPage }); + + const { order, orderBy, start } = useSelector( + (state: GeneralState) => state.experiencePoints.expList, + shallowEqual + ); + + const page = (start ?? 0) / limit + 1; + + return ( + <> + +
+ + t(`Page %current% / %total%`) + .replace("%current%", current.toString()) + .replace("%total%", total ? total.toString() : "0") + } + /> + + ); +}; + +export default ExperiencePointsTable; diff --git a/src/pages/ExperiencePoints/ExperiencePointsTable/useResetPaginationOnFilterChange.tsx b/src/pages/ExperiencePoints/ExperiencePointsTable/useResetPaginationOnFilterChange.tsx new file mode 100644 index 000000000..b4d584989 --- /dev/null +++ b/src/pages/ExperiencePoints/ExperiencePointsTable/useResetPaginationOnFilterChange.tsx @@ -0,0 +1,25 @@ +import { useEffect } from "react"; +import { shallowEqual, useSelector } from "react-redux"; + +const useResetPaginationOnFilterChange = ({ + setPage, +}: { + setPage: (newPage: number) => void; +}) => { + const { selectedActivity, selectedCampaign, selectedDate, search } = + useSelector((state: GeneralState) => state.experiencePoints, shallowEqual); + + useEffect(() => { + if ( + selectedCampaign || + selectedActivity || + selectedDate || + search || + search === "" + ) { + setPage(1); + } + }, [selectedCampaign, selectedActivity, selectedDate, search]); +}; + +export { useResetPaginationOnFilterChange }; diff --git a/src/pages/ExperiencePoints/ExperiencePointsTable/useRows.tsx b/src/pages/ExperiencePoints/ExperiencePointsTable/useRows.tsx new file mode 100644 index 000000000..963208216 --- /dev/null +++ b/src/pages/ExperiencePoints/ExperiencePointsTable/useRows.tsx @@ -0,0 +1,77 @@ +import { useTranslation } from "react-i18next"; +import { shallowEqual, useSelector } from "react-redux"; +import { mapActivityName } from "src/redux/experiencePoints/utils"; +import { useGetUsersMeExperienceQuery } from "src/services/tryberApi"; +import dateFormatter from "src/utils/dateFormatter"; + +const useRows = () => { + const limit = 25; + const { t } = useTranslation(); + + const { order, orderBy, start } = useSelector( + (state: GeneralState) => state.experiencePoints.expList, + shallowEqual + ); + const { selectedActivity, selectedCampaign, selectedDate, search } = + useSelector((state: GeneralState) => state.experiencePoints, shallowEqual); + + const { + data, + error, + isLoading: loading, + } = useGetUsersMeExperienceQuery({ + limit: limit, + start: start, + order: order, + orderBy: orderBy, + search: search, + searchBy: "note", + filterBy: { + campaign: selectedCampaign?.value, + activity: selectedActivity?.value, + date: selectedDate?.value, + }, + }); + + if (error) { + return { + rows: [], + loading: false, + totalEntries: 0, + }; + } + + const { results, total } = data ?? {}; + + const rows = (results ?? []).map((res) => { + return { + key: res.id, + amount: { + title: `${res.amount > 0 ? `+${res.amount}` : res.amount}pts`, + content: + res.amount === 0 ? ( + {res.amount}pts + ) : res.amount > 0 ? ( + +{res.amount}pts + ) : ( + {res.amount}pts + ), + }, + date: dateFormatter(res.date), + activity: mapActivityName(res.activity.id, t), + campaign: + res.campaign.title && res.campaign.id > 0 + ? `CP${res.campaign.id}` + : `-`, + note: res.note?.replace(/\\(.)/gm, "$1"), + }; + }); + + return { + rows, + loading, + totalEntries: total ?? 0, + }; +}; + +export { useRows }; diff --git a/src/pages/ExperiencePoints/columns.ts b/src/pages/ExperiencePoints/columns.ts index b571d50f8..382e88237 100644 --- a/src/pages/ExperiencePoints/columns.ts +++ b/src/pages/ExperiencePoints/columns.ts @@ -1,11 +1,11 @@ import { Column } from "@appquality/appquality-design-system/dist/stories/table/_types"; -import { TFunction } from "react-i18next"; +import { useTranslation } from "react-i18next"; +import { useAppDispatch } from "src/store"; import { updateExperiencePointsSortingOptions } from "../../redux/experiencePoints/actionCreator"; -export const ExperiencePointsColumns = ( - dispatch: AppDispatch, - t: TFunction<"translation"> -): Column[] => { +export const useExperiencePointsColumns = (): Column[] => { + const { t } = useTranslation("translation"); + const dispatch = useAppDispatch(); return [ { title: t("Points"), diff --git a/src/pages/ExperiencePoints/index.tsx b/src/pages/ExperiencePoints/index.tsx index a2e16bc06..c57893201 100644 --- a/src/pages/ExperiencePoints/index.tsx +++ b/src/pages/ExperiencePoints/index.tsx @@ -3,101 +3,15 @@ import { BSGrid, Button, Card, - TableType, Text, } from "@appquality/appquality-design-system"; -import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { shallowEqual, useSelector } from "react-redux"; import { PageTemplate } from "src/features/PageTemplate"; -import { - fetchExperiencePoints, - fetchExperiencePointsFilters, - updateExperiencePointsPagination, -} from "../../redux/experiencePoints/actionCreator"; -import { mapActivityName } from "../../redux/experiencePoints/utils"; -import { useAppDispatch } from "../../redux/provider"; -import dateFormatter from "../../utils/dateFormatter"; -import { ExperiencePointsColumns } from "./columns"; import ExperiencePointsFilters from "./ExperiencePointsFilters"; import ExperiencePointsTable from "./ExperiencePointsTable"; export default function ExperiencePoints() { const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const [columns, setcolumns] = useState( - ExperiencePointsColumns(dispatch, t) - ); - const [rows, setRows] = useState([]); - - const { - expList, - campaigns, - activities, - dates, - selectedCampaign, - selectedActivity, - selectedDate, - search, - isLoading, - } = useSelector( - (state: GeneralState) => state.experiencePoints, - shallowEqual - ); - - const { results, limit, total, start, order, orderBy } = expList; - - const changePagination = (newPage: number) => { - const newStart = limit * (newPage - 1); - dispatch(updateExperiencePointsPagination(newStart)); - }; - - useEffect(() => { - setRows( - results.map((res) => { - return { - key: res.id, - amount: { - title: `${res.amount > 0 ? `+${res.amount}` : res.amount}pts`, - content: - res.amount === 0 ? ( - {res.amount}pts - ) : res.amount > 0 ? ( - +{res.amount}pts - ) : ( - {res.amount}pts - ), - }, - date: dateFormatter(res.date), - activity: mapActivityName(res.activity.id, t), - campaign: - res.campaign.title && res.campaign.id > 0 - ? `CP${res.campaign.id}` - : `-`, - note: res.note?.replace(/\\(.)/gm, "$1"), - }; - }) - ); - }, [results]); - - useEffect(() => { - if ( - selectedCampaign || - selectedActivity || - selectedDate || - search || - search === "" - ) { - changePagination(1); - dispatch(fetchExperiencePointsFilters(t)); - } - }, [selectedCampaign, selectedActivity, selectedDate, search]); - - useEffect(() => { - dispatch(fetchExperiencePoints()); - dispatch(fetchExperiencePointsFilters(t)); - }, []); - return ( - +
- +
diff --git a/src/redux/experiencePoints/actionCreator.ts b/src/redux/experiencePoints/actionCreator.ts index 43c4b37a8..ff1b40362 100644 --- a/src/redux/experiencePoints/actionCreator.ts +++ b/src/redux/experiencePoints/actionCreator.ts @@ -1,76 +1,5 @@ import { SelectType } from "@appquality/appquality-design-system"; -import { TFunction } from "i18next"; import { ThunkAction } from "redux-thunk"; -import API from "../../utils/api"; -import dateFormatter from "../../utils/dateFormatter"; -import { addMessage } from "../siteWideMessages/actionCreators"; -import { mapActivityName } from "./utils"; - -export const fetchExperiencePoints = - (): ThunkAction< - Promise, - GeneralState, - unknown, - ExperiencePointsActions - > => - async (dispatch, getState) => { - const { - experiencePoints: { - expList, - selectedCampaign, - selectedActivity, - selectedDate, - search, - }, - } = getState(); - dispatch({ - type: "experiencePoints/setIsLoading", - payload: true, - }); - try { - const query: ApiOperations["get-users-me-experience"]["parameters"]["query"] = - { - order: expList.order, - orderBy: expList.orderBy, - limit: expList.limit, - start: expList.start, - filterBy: { - ...(selectedCampaign?.value && { - campaign: selectedCampaign.value, - }), - ...(selectedActivity?.value && { - activity: selectedActivity.value, - }), - ...(selectedDate?.value && { date: selectedDate.value }), - }, - search: search, - searchBy: search && "note", - }; - const data = await API.experiencePoints({ query }); - dispatch({ - type: "experiencePoints/updateExpList", - payload: data, - }); - } catch (e) { - const error = e as HttpError; - if (error.statusCode === 404) { - const { start, limit, size } = expList; - if ((start || 0) - limit >= 0) { - dispatch(updateExperiencePointsPagination((start || 0) - limit)); - } - dispatch({ - type: "experiencePoints/updateExpList", - payload: { - size: size, - start: start, - results: [], - }, - }); - } else { - addMessage(error.message, "danger", false); - } - } - }; export const updateExperiencePointsPagination = ( @@ -86,7 +15,6 @@ export const updateExperiencePointsPagination = type: "experiencePoints/updateExpListQuery", payload: { start: newStart }, }); - return dispatch(fetchExperiencePoints()); }; export const updateExperiencePointsSortingOptions = @@ -104,107 +32,6 @@ export const updateExperiencePointsSortingOptions = type: "experiencePoints/updateExpListQuery", payload: { order: order, orderBy: orderBy }, }); - return dispatch(fetchExperiencePoints()); - }; - -export const fetchExperiencePointsFilters = - ( - t: TFunction - ): ThunkAction< - Promise, - GeneralState, - unknown, - ExperiencePointsActions - > => - async (dispatch, getState) => { - const { - experiencePoints: { - expList, - selectedCampaign, - selectedActivity, - selectedDate, - search, - }, - } = getState(); - dispatch({ - type: "experiencePoints/setIsLoading", - payload: true, - }); - try { - const query: ApiOperations["get-users-me-experience"]["parameters"]["query"] = - { - orderBy: "id", - order: "DESC", - filterBy: { - ...(selectedCampaign?.value && { - campaign: selectedCampaign.value, - }), - ...(selectedActivity?.value && { - activity: selectedActivity.value, - }), - ...(selectedDate?.value && { date: selectedDate.value }), - }, - search: search, - searchBy: search && "note", - }; - const data = await API.experiencePoints({ query }); - - let _campaigns: SelectType.Option[] = []; - let _activities: SelectType.Option[] = []; - let _dates: SelectType.Option[] = []; - - data.results.forEach((res: any) => { - if ( - typeof res.campaign === "undefined" || - typeof res.activity === "undefined" || - typeof res.date === "undefined" - ) - return; - if (res.campaign?.id && res.campaign?.title) { - _campaigns[res.campaign.id] = { - label: `CP${res.campaign?.id} - ${res.campaign?.title}` || "", - value: res.campaign.id.toString(), - }; - } - if (res.activity?.id) { - _activities[res.activity.id] = { - label: mapActivityName(res.activity.id, t) || "", - value: res.activity.id.toString(), - }; - } - if (res.date) { - let d = new Date(res.date); - _dates[d.getTime()] = { - label: dateFormatter(res.date) || "", - value: res.date, - }; - } - }); - - let datesFilter = Object.values(_dates).filter((el) => el != null); - if (expList.orderBy === "date" && expList.order === "ASC") - datesFilter = datesFilter.reverse(); - - dispatch({ - type: "experiencePoints/setCampaigns", - payload: _campaigns.filter((el) => el != null).reverse(), - }); - dispatch({ - type: "experiencePoints/setActivities", - payload: _activities.filter((el) => el != null), - }); - dispatch({ - type: "experiencePoints/setDates", - payload: datesFilter, - }); - } catch (e) { - const error = e as HttpError; - console.log(error); - } - dispatch({ - type: "experiencePoints/setIsLoading", - payload: false, - }); }; export const setSelectedCampaign =