From 5a2c6f02469672a8cc04fffa364a230d0da7b099 Mon Sep 17 00:00:00 2001 From: Xiao Peng Date: Thu, 19 Dec 2024 16:10:12 -0800 Subject: [PATCH] feat: modal to add favourite activity --- .../endpoint/FavouriteActivityEndpoint.java | 16 +- .../FavouriteActivityRepository.java | 2 + .../service/FavouriteActivityService.java | 60 ++++-- .../src/api-service/favouriteActivitiesAPI.ts | 4 +- frontend/src/assets/img/fav-icon.svg | 4 + .../FavouriteCard/FavouriteConsepCard.tsx | 121 +++++++++++ .../components/Card/FavouriteCard/styles.scss | 93 ++++++++- .../components/FavouriteActivities/index.tsx | 121 ++++++----- frontend/src/components/PageTitle/index.tsx | 6 +- frontend/src/config/FavouriteActivityMap.ts | 190 +++++++++--------- frontend/src/routes/constants.ts | 50 ++++- .../FavouriteActivityModal.tsx | 189 +++++++++++++++++ .../CONSEP/FavouriteActivity/constants.tsx | 20 ++ .../CONSEP/FavouriteActivity/definitions.tsx | 29 +++ .../views/CONSEP/FavouriteActivity/index.tsx | 100 +++++++-- .../CONSEP/FavouriteActivity/styles.scss | 97 ++++++++- 16 files changed, 892 insertions(+), 210 deletions(-) create mode 100644 frontend/src/assets/img/fav-icon.svg create mode 100644 frontend/src/components/Card/FavouriteCard/FavouriteConsepCard.tsx create mode 100644 frontend/src/views/CONSEP/FavouriteActivity/FavouriteActivityModal.tsx create mode 100644 frontend/src/views/CONSEP/FavouriteActivity/constants.tsx create mode 100644 frontend/src/views/CONSEP/FavouriteActivity/definitions.tsx diff --git a/backend/src/main/java/ca/bc/gov/backendstartapi/endpoint/FavouriteActivityEndpoint.java b/backend/src/main/java/ca/bc/gov/backendstartapi/endpoint/FavouriteActivityEndpoint.java index 3bc5cbd73..989bd0f51 100644 --- a/backend/src/main/java/ca/bc/gov/backendstartapi/endpoint/FavouriteActivityEndpoint.java +++ b/backend/src/main/java/ca/bc/gov/backendstartapi/endpoint/FavouriteActivityEndpoint.java @@ -49,24 +49,24 @@ public class FavouriteActivityEndpoint { */ @PostMapping(consumes = "application/json", produces = "application/json") @Operation( - summary = "Creates a Favourite Activity", + summary = "Creates a Favourite Activities in bulk", description = """ - Creates a Favourite Activity to the logged user based on the activity - title or page name, with an optional isConsep flag. + Creates Favourite Activities for the logged user in bulk based on an array of activity titles + or page names, with optional isConsep flags. """) @ApiResponses( value = { @ApiResponse( responseCode = "201", - description = "The Favourite Activity entity was successfully created", + description = "The Favourite Activities were successfully created", content = @Content( mediaType = "application/json", schema = @Schema(implementation = FavouriteActivityEntity.class))), @ApiResponse( responseCode = "400", - description = "The activity doesn't exists or is already defined to that user", + description = "One or more activities failed validation or already exist", content = @Content( mediaType = "application/json", @@ -82,7 +82,7 @@ public class FavouriteActivityEndpoint { content = @Content(schema = @Schema(implementation = Void.class))) }) @RoleAccessConfig({"SPAR_TSC_ADMIN", "SPAR_MINISTRY_ORCHARD", "SPAR_NONMINISTRY_ORCHARD"}) - public ResponseEntity createUserActivity( + public ResponseEntity> createUserActivities( @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "Body containing the activity name that will be created", required = true, @@ -90,8 +90,8 @@ public ResponseEntity createUserActivity( @Content(schema = @Schema(implementation = FavouriteActivityCreateDto.class))) @Valid @RequestBody - FavouriteActivityCreateDto createDto) { - FavouriteActivityEntity entity = favouriteActivityService.createUserActivity(createDto); + List createDtos) { + List entity = favouriteActivityService.createUserActivities(createDtos); return ResponseEntity.status(HttpStatus.CREATED).body(entity); } diff --git a/backend/src/main/java/ca/bc/gov/backendstartapi/repository/FavouriteActivityRepository.java b/backend/src/main/java/ca/bc/gov/backendstartapi/repository/FavouriteActivityRepository.java index 7d25b6ae0..625089a7d 100644 --- a/backend/src/main/java/ca/bc/gov/backendstartapi/repository/FavouriteActivityRepository.java +++ b/backend/src/main/java/ca/bc/gov/backendstartapi/repository/FavouriteActivityRepository.java @@ -14,6 +14,8 @@ public interface FavouriteActivityRepository extends CrudRepository findByActivity(String activity); + boolean existsByUserIdAndActivity(String userId, String activity); + @Modifying @Query("update FavouriteActivityEntity set highlighted = false where userId = ?1") void removeAllHighlightedByUser(String userId); diff --git a/backend/src/main/java/ca/bc/gov/backendstartapi/service/FavouriteActivityService.java b/backend/src/main/java/ca/bc/gov/backendstartapi/service/FavouriteActivityService.java index 56042d3d7..99ea4e8e0 100644 --- a/backend/src/main/java/ca/bc/gov/backendstartapi/service/FavouriteActivityService.java +++ b/backend/src/main/java/ca/bc/gov/backendstartapi/service/FavouriteActivityService.java @@ -9,6 +9,8 @@ import ca.bc.gov.backendstartapi.repository.FavouriteActivityRepository; import ca.bc.gov.backendstartapi.security.LoggedUserService; import jakarta.transaction.Transactional; + +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -36,35 +38,51 @@ public FavouriteActivityService( this.favouriteActivityRepository = favouriteActivityRepository; } +/** + * Validates the activity input. + */ + private void validateActivityInput(FavouriteActivityCreateDto activityDto) { + if (Objects.isNull(activityDto.activity()) || activityDto.activity().isBlank()) { + throw new InvalidActivityException(); + } + } + + /** + * Builds a FavouriteActivityEntity. + */ + private FavouriteActivityEntity buildFavouriteActivityEntity(String userId, FavouriteActivityCreateDto dto) { + FavouriteActivityEntity entity = new FavouriteActivityEntity(); + entity.setUserId(userId); + entity.setActivity(dto.activity()); + entity.setIsConsep(Optional.ofNullable(dto.isConsep()).orElse(false)); + return entity; + } + /** * Create a user's activity in the database. * * @param activityDto a {@link FavouriteActivityCreateDto} containing the activity title * @return the {@link FavouriteActivityEntity} created */ - public FavouriteActivityEntity createUserActivity(FavouriteActivityCreateDto activityDto) { + public List createUserActivities(List activityDtos) { String userId = loggedUserService.getLoggedUserId(); - SparLog.info("Creating activity {} for user {}", activityDto.activity(), userId); - - if (Objects.isNull(activityDto.activity()) || activityDto.activity().isBlank()) { - throw new InvalidActivityException(); + SparLog.info("Creating activities for user {}", userId); + + List createdActivities = new ArrayList<>(); + + for (FavouriteActivityCreateDto dto : activityDtos) { + try { + validateActivityInput(dto); + if (favouriteActivityRepository.existsByUserIdAndActivity(userId, dto.activity())) { + continue; + } + FavouriteActivityEntity entity = buildFavouriteActivityEntity(userId, dto); + createdActivities.add(favouriteActivityRepository.save(entity)); + } catch (InvalidActivityException | FavoriteActivityExistsToUser e) { + SparLog.error("Error creating activity: {}", e.getMessage()); + } } - - List userFavList = favouriteActivityRepository.findAllByUserId(userId); - if (userFavList.stream().anyMatch(ac -> ac.getActivity().equals(activityDto.activity()))) { - SparLog.info("Activity {} already exists for user {}!", activityDto.activity(), userId); - throw new FavoriteActivityExistsToUser(); - } - - FavouriteActivityEntity activityEntity = new FavouriteActivityEntity(); - activityEntity.setUserId(userId); - activityEntity.setActivity(activityDto.activity()); - - activityEntity.setIsConsep(activityDto.isConsep() != null ? activityDto.isConsep() : false); - - FavouriteActivityEntity activityEntitySaved = favouriteActivityRepository.save(activityEntity); - SparLog.info("Activity {} created for user {}", activityDto.activity(), userId); - return activityEntitySaved; + return createdActivities; } /** diff --git a/frontend/src/api-service/favouriteActivitiesAPI.ts b/frontend/src/api-service/favouriteActivitiesAPI.ts index 427c0828c..fa4570c68 100644 --- a/frontend/src/api-service/favouriteActivitiesAPI.ts +++ b/frontend/src/api-service/favouriteActivitiesAPI.ts @@ -36,9 +36,9 @@ export const getFavAct = () => { }); }; -export const postFavAct = (newAct: FavActivityPostType) => { +export const postFavAct = (newActs: FavActivityPostType[]) => { const url = ApiConfig.favouriteActivities; - return api.post(url, newAct); + return api.post(url, newActs); }; export const patchFavAct = (field: string, activity: FavActivityType) => { diff --git a/frontend/src/assets/img/fav-icon.svg b/frontend/src/assets/img/fav-icon.svg new file mode 100644 index 000000000..effcccacf --- /dev/null +++ b/frontend/src/assets/img/fav-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/components/Card/FavouriteCard/FavouriteConsepCard.tsx b/frontend/src/components/Card/FavouriteCard/FavouriteConsepCard.tsx new file mode 100644 index 000000000..875e48ebc --- /dev/null +++ b/frontend/src/components/Card/FavouriteCard/FavouriteConsepCard.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { + Tile, OverflowMenu, OverflowMenuItem, Column +} from '@carbon/react'; +import * as Icons from '@carbon/icons-react'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { FavActivityType } from '../../../types/FavActivityTypes'; +import { patchFavAct, deleteFavAct } from '../../../api-service/favouriteActivitiesAPI'; +import useWindowSize from '../../../hooks/UseWindowSize'; +import { MEDIUM_SCREEN_WIDTH } from '../../../shared-constants/shared-constants'; + +import SmallCard from '../SmallCard'; + +import './styles.scss'; + +type FavouriteCardProps = { + favObject: FavActivityType +} + +const FavouriteCard = ({ + favObject +}: FavouriteCardProps) => { + const Icon = Icons[favObject.image]; + const navigate = useNavigate(); + const favActQueryKey = ['favourite-activities']; + const queryClient = useQueryClient(); + + const windowSize = useWindowSize(); + + const highlightFavAct = useMutation({ + mutationFn: () => patchFavAct('highlighted', favObject), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: favActQueryKey }); + } + }); + + const removeFavAct = useMutation({ + mutationFn: () => deleteFavAct(favObject.id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: favActQueryKey }); + } + }); + + const ActionBtn = ( + ) => { + e.stopPropagation(); + }} + onClick={(e: React.MouseEvent) => { + e.stopPropagation(); + }} + > + ) => { + e.stopPropagation(); + highlightFavAct.mutate(); + }} + /> + ) => { + e.stopPropagation(); + removeFavAct.mutate(); + }} + /> + + ); + + if (windowSize.innerWidth < MEDIUM_SCREEN_WIDTH) { + return ( + + ); + } + + return ( + + {' '} + navigate(favObject.link)} + tabIndex={0} + aria-label={`Go to ${favObject.header}`} + onKeyDown={(e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + navigate(favObject.link); + } + }} + > +
+ + {ActionBtn} +
+
+

{favObject.header}

+
+ +
+
+ + ); +}; + +export default FavouriteCard; diff --git a/frontend/src/components/Card/FavouriteCard/styles.scss b/frontend/src/components/Card/FavouriteCard/styles.scss index 0a79a3179..21d930d02 100644 --- a/frontend/src/components/Card/FavouriteCard/styles.scss +++ b/frontend/src/components/Card/FavouriteCard/styles.scss @@ -1,6 +1,6 @@ -@use '@bcgov-nr/nr-theme/design-tokens/variables.scss' as vars; -@use '@bcgov-nr/nr-theme/design-tokens/colors.scss' as colors; -@use '@carbon/type'; +@use "@bcgov-nr/nr-theme/design-tokens/variables.scss" as vars; +@use "@bcgov-nr/nr-theme/design-tokens/colors.scss" as colors; +@use "@carbon/type"; .fav-card-header { display: inline-flex; @@ -63,7 +63,7 @@ } p.fav-card-title-large { - @include type.type-style('heading-02'); + @include type.type-style("heading-02"); font-weight: 700; display: block; white-space: nowrap; @@ -73,7 +73,7 @@ p.fav-card-title-large { } .fav-card-content > p.fav-card-content-description { - @include type.type-style('body-compact-01'); + @include type.type-style("body-compact-01"); margin-bottom: auto; color: var(--#{vars.$bcgov-prefix}-text-secondary); display: -webkit-box; @@ -91,21 +91,23 @@ p.fav-card-title-large { .fav-card-main-highlighted .fav-card-icon, .fav-card-main-highlighted .fav-card-content > p, .fav-card-main-highlighted .fav-card-header > p { - color: var(--#{vars.$bcgov-prefix}-text-on-color) + color: var(--#{vars.$bcgov-prefix}-text-on-color); } .fav-card-main-highlighted .fav-card-overflow > svg { fill: var(--#{vars.$bcgov-prefix}-icon-on-color); } -.#{vars.$bcgov-prefix}--overflow-menu.#{vars.$bcgov-prefix}--overflow-menu--open >svg { +.#{vars.$bcgov-prefix}--overflow-menu.#{vars.$bcgov-prefix}--overflow-menu--open + > svg { fill: var(--#{vars.$bcgov-prefix}-icon-primary); } .fav-card-main-highlighted:hover, .fav-card-main-highlighted:focus { border-color: var(--#{vars.$bcgov-prefix}-icon-on-color); - box-shadow: inset 0 0 0 1px var(--#{vars.$bcgov-prefix}-icon-on-color), 0 0 0 3px var(--#{vars.$bcgov-prefix}-button-primary); + box-shadow: inset 0 0 0 1px var(--#{vars.$bcgov-prefix}-icon-on-color), + 0 0 0 3px var(--#{vars.$bcgov-prefix}-button-primary); cursor: pointer; outline: 0; outline-offset: 0; @@ -142,9 +144,80 @@ p.fav-card-title-large { justify-content: flex-start; align-items: center; } - } -.fav-card-menu-options{ +.fav-card-menu-options { width: fit-content; } + +.consep-fav-card-header { + display: inline-flex; + width: 100%; + max-height: 2rem; + justify-content: space-between; +} + +.consep-fav-card, +.consep-fav-card-highlighted { + padding: 1rem; + margin-top: 2rem; + background: var(--#{vars.$bcgov-prefix}-layer-02); + border: 1px solid var(--#{vars.$bcgov-prefix}-border-subtle-02); +} + +.consep-fav-card-highlighted { + border: 2px solid #0f62fe; + background: var(--#{vars.$bcgov-prefix}-button-primary); +} + +.consep-fav-card-content { + margin-top: 4rem; +} + +.consep-fav-card-highlighted .fav-card-icon, +.consep-fav-card-highlighted .consep-fav-card-content > p, +.consep-fav-card-highlighted .fav-card-header > p { + color: var(--#{vars.$bcgov-prefix}-text-on-color); +} + +.consep-fav-card-highlighted .fav-card-overflow > svg { + fill: var(--#{vars.$bcgov-prefix}-icon-on-color); +} + +.consep-fav-card-highlighted .fav-card-overflow:hover, +.consep-fav-card-highlighted .fav-card-overflow:focus { + outline: 2px solid colors.$white; +} + +.consep-fav-card-highlighted .#{vars.$bcgov-prefix}--overflow-menu:hover { + background-color: transparent; +} + +.consep-fav-card:hover { + outline: 2px solid var(--#{vars.$bcgov-prefix}-focus); + cursor: pointer; +} + +.consep-fav-card-highlighted:hover, +.consep-fav-card-highlighted:focus { + border-color: var(--#{vars.$bcgov-prefix}-icon-on-color); + box-shadow: inset 0 0 0 1px var(--#{vars.$bcgov-prefix}-icon-on-color), + 0 0 0 3px var(--#{vars.$bcgov-prefix}-button-primary); + cursor: pointer; + outline: 0; + outline-offset: 0; +} + +.consep-fav-card-highlighted .fav-card-overflow:hover, +.consep-fav-card-highlighted .fav-card-overflow:focus { + outline: 2px solid colors.$white; +} + +.consep-fav-card-highlighted .fav-card-overflow:hover, +.consep-fav-card-highlighted .fav-card-overflow:focus { + outline: 2px solid colors.$white; +} + +.consep-fav-card-highlighted .#{vars.$bcgov-prefix}--overflow-menu:hover { + background-color: transparent; +} diff --git a/frontend/src/components/FavouriteActivities/index.tsx b/frontend/src/components/FavouriteActivities/index.tsx index f01aff318..1e0b9cc56 100644 --- a/frontend/src/components/FavouriteActivities/index.tsx +++ b/frontend/src/components/FavouriteActivities/index.tsx @@ -10,6 +10,8 @@ import { import { Information } from '@carbon/icons-react'; import FavouriteCard from '../Card/FavouriteCard'; +import FavouriteConsepCard from '../Card/FavouriteCard/FavouriteConsepCard'; + import EmptySection from '../EmptySection'; import Subtitle from '../Subtitle'; import { getFavAct } from '../../api-service/favouriteActivitiesAPI'; @@ -33,60 +35,87 @@ const FavouriteActivities: React.FC = ({ isConsep }) = }); return ( - + <> {!isConsep && ( - -

My favourite activities

- - - - -
- )} + + {!isConsep && ( + +

My favourite activities

+ + + + +
+ )} - - - { - favActQuery.isLoading ? : null - } - { - favActQuery.isSuccess - && favActQuery.data - && ( - favActQuery.data.length === 0 - ? ( - - ) : favActQuery.data.filter((fav) => fav.isConsep === isConsep).map((favObject) => ( - + + + { + favActQuery.isLoading ? : null + } + { + favActQuery.isSuccess + && favActQuery.data + && ( + favActQuery.data.length === 0 + ? ( + + ) + : favActQuery.data.filter((fav) => fav.isConsep === isConsep).map((favObject) => ( - - )) - ) - } + )) + ) + } + + +
+ )} + + {isConsep && ( + + {favActQuery.isSuccess + && favActQuery.data + && ( + favActQuery.data.length === 0 + ? ( + + ) + : favActQuery.data.filter((fav) => fav.isConsep === isConsep).map((favObject) => ( + + )))} - -
+ )} + + ); }; diff --git a/frontend/src/components/PageTitle/index.tsx b/frontend/src/components/PageTitle/index.tsx index 9f707e35e..dc53e8238 100644 --- a/frontend/src/components/PageTitle/index.tsx +++ b/frontend/src/components/PageTitle/index.tsx @@ -37,8 +37,8 @@ const PageTitle = ({ queryFn: getFavAct }); - const highlightFavAct = useMutation({ - mutationFn: (actObj: FavActivityPostType) => postFavAct(actObj), + const addFavAct = useMutation({ + mutationFn: (actObjs: FavActivityPostType[]) => postFavAct(actObjs), onSuccess: () => { queryClient.invalidateQueries(favActQueryKey); } @@ -71,7 +71,7 @@ const PageTitle = ({ onClick={ isFavourited ? () => removeFavAct.mutate(thisFavAct.id) - : () => highlightFavAct.mutate({ activity }) + : () => addFavAct.mutate([{ activity }]) } > { diff --git a/frontend/src/config/FavouriteActivityMap.ts b/frontend/src/config/FavouriteActivityMap.ts index 2a64df4a3..2a65b5668 100644 --- a/frontend/src/config/FavouriteActivityMap.ts +++ b/frontend/src/config/FavouriteActivityMap.ts @@ -51,8 +51,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'actualGerminationCount', image: 'TableSplit', - header: 'Actual Germination Count', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Actual germination count', + link: ROUTES.ACTUAL_GERMINATION_COUNT, highlighted: false, isConsep: true, department: 'Testing' @@ -61,8 +61,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'calculateCropAverage', image: 'TableSplit', - header: 'Calculate Crop Average', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Calculate crop average', + link: ROUTES.CALCULATE_CROP_AVERAGE, highlighted: false, isConsep: true, department: 'Administrative' @@ -71,8 +71,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'cancelledRequestsReport', image: 'TableSplit', - header: 'Cancelled Requests Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Cancelled requests report', + link: ROUTES.CANCELLED_REQUESTS_REPORT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -81,8 +81,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'coneAndSeedProcessingReport', image: 'TableSplit', - header: 'Cone and Seed Processing Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Cone and seed processing report', + link: ROUTES.CONE_AND_SEED_PROCESSING_REPORT, highlighted: false, isConsep: true, department: 'Processing' @@ -91,8 +91,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'coneAndSeedShipmentReceipt', image: 'TableSplit', - header: 'Cone and Seed Shipment Receipt', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Cone and seed shipment receipt', + link: ROUTES.CONE_AND_SEED_SHIPMENT_RECEIPT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -101,8 +101,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'createGerminationTray', image: 'TableSplit', - header: 'Create Germination Tray', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Create germination tray', + link: ROUTES.CREATE_GERMINATION_TRAY, highlighted: false, isConsep: true, department: 'Testing' @@ -111,8 +111,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'cspRequest', image: 'TableSplit', - header: 'CSP Request', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'CSP request', + link: ROUTES.CSP_REQUEST, highlighted: false, isConsep: true, department: 'Processing' @@ -121,8 +121,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'doNotStartList', image: 'TableSplit', - header: 'Do Not Start List', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Do not start list', + link: ROUTES.DO_NOT_START_LIST, highlighted: false, isConsep: true, department: 'Testing' @@ -131,8 +131,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'familyLot', image: 'TableSplit', - header: 'Family Lot', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Family lot', + link: ROUTES.FAMILY_LOT, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -141,8 +141,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'familyLotSummaryReport', image: 'TableSplit', - header: 'Family Lot Summary Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Family lot summary report', + link: ROUTES.FAMILY_LOT_SUMMARY_REPORT, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -151,8 +151,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'germinationSpeciesAverage', image: 'TableSplit', - header: 'Germination Species Average', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Germination species average', + link: ROUTES.GERMINATION_SPECIES_AVERAGE, highlighted: false, isConsep: true, department: 'Testing' @@ -161,8 +161,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'germCountPredictions', image: 'TableSplit', - header: 'Germ Count Predictions', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Germ count predictions', + link: ROUTES.GERM_COUNT_PREDICTIONS, highlighted: false, isConsep: true, department: 'Testing' @@ -171,8 +171,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'identifyAvailableLongTermLocation', image: 'TableSplit', - header: 'Identify Available Long-Term Location', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Identify available long-term location', + link: ROUTES.IDENTIFY_AVAILABLE_LONG_TERM_LOCATION, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -181,8 +181,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'inHouseInventory', image: 'TableSplit', - header: 'In-House Inventory', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'In-house inventory', + link: ROUTES.IN_HOUSE_INVENTORY, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -191,8 +191,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'inventoryLocationReport', image: 'TableSplit', - header: 'Inventory Location Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Inventory location report', + link: ROUTES.INVENTORY_LOCATION_REPORT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -201,8 +201,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'kilnPrograms', image: 'TableSplit', - header: 'Kiln Programs', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Kiln programs', + link: ROUTES.KILN_PROGRAMS, highlighted: false, isConsep: true, department: 'Administrative' @@ -211,8 +211,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'maintainClientLocation', image: 'TableSplit', - header: 'Maintain Client Location', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Maintain client location', + link: ROUTES.MAINTAIN_CLIENT_LOCATION, highlighted: false, isConsep: true, department: 'Administrative' @@ -221,8 +221,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'maintainGerminationTrayScreen', image: 'TableSplit', - header: 'Maintain Germination Tray Screen', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Maintain germination tray screen', + link: ROUTES.MAINTAIN_GERMINATION_TRAY_SCREEN, highlighted: false, isConsep: true, department: 'Testing' @@ -231,8 +231,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'maintainLocalContacts', image: 'TableSplit', - header: 'Maintain Local Contacts', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Maintain local contacts', + link: ROUTES.MAINTAIN_LOCAL_CONTACTS, highlighted: false, isConsep: true, department: 'Administrative' @@ -241,8 +241,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'maintainStandardActivities', image: 'TableSplit', - header: 'Maintain Standard Activities', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Maintain standard activities', + link: ROUTES.MAINTAIN_STANDARD_ACTIVITIES, highlighted: false, isConsep: true, department: 'Administrative' @@ -251,8 +251,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'maintainStatHolidays', image: 'TableSplit', - header: 'Maintain Stat Holidays', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Maintain stat holidays', + link: ROUTES.MAINTAIN_STAT_HOLIDAYS, highlighted: false, isConsep: true, department: 'Testing' @@ -261,8 +261,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'maintainWorkPlans', image: 'TableSplit', - header: 'Maintain Work Plans', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Maintain work plans', + link: ROUTES.MAINTAIN_WORK_PLANS, highlighted: false, isConsep: true, department: 'Administrative' @@ -271,8 +271,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'manualMoistureContent', image: 'TableSplit', - header: 'Manual Moisture Content', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Manual moisture content', + link: ROUTES.MANUAL_MOISTURE_CONTENT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -281,8 +281,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'processingActivities', image: 'TableSplit', - header: 'Processing Activities', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Processing activities', + link: ROUTES.PROCESSING_ACTIVITIES, highlighted: false, isConsep: true, department: 'Processing' @@ -291,8 +291,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'recordStockCountResults', image: 'TableSplit', - header: 'Record Stock Count Results', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Record stock count results', + link: ROUTES.RECORD_STOCK_COUNT_RESULTS, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -301,8 +301,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'requestChangesReport', image: 'TableSplit', - header: 'Request Changes Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Request changes report', + link: ROUTES.REQUEST_CHANGES_REPORT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -311,8 +311,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'returnedSeed', image: 'TableSplit', - header: 'Returned Seed', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Returned seed', + link: ROUTES.RETURNED_SEED, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -321,8 +321,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'reviewPendingRequest', image: 'TableSplit', - header: 'Review Pending Request', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Review pending request', + link: ROUTES.REVIEW_PENDING_REQUEST, highlighted: false, isConsep: true, department: 'Testing' @@ -331,8 +331,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'scheduleOrReviseRequestItemActivity', image: 'TableSplit', - header: 'Schedule or Revise Request Item Activity', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Schedule or revise request item activity', + link: ROUTES.SCHEDULE_OR_REVISE_REQUEST_ITEM_ACTIVITY, highlighted: false, isConsep: true, department: 'Testing' @@ -341,8 +341,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedAndFamilyLotReport', image: 'TableSplit', - header: 'Seed and Family Lot Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seed and family lot report', + link: ROUTES.SEED_AND_FAMILY_LOT_REPORT, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -351,8 +351,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedSaleOrTransferActivitiesScreen', image: 'TableSplit', - header: 'Seed Sale or Transfer Activities Screen', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seed sale or transfer activities screen', + link: ROUTES.SEED_SALE_OR_TRANSFER_ACTIVITIES_SCREEN, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -361,8 +361,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedlotCharacteristics', image: 'TableSplit', - header: 'Seedlot Characteristics', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seedlot characteristics', + link: ROUTES.SEEDLOT_CHARACTERISTICS, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -371,8 +371,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedlotLocationHistoryReport', image: 'TableSplit', - header: 'Seedlot Location History Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seedlot location history report', + link: ROUTES.SEEDLOT_LOCATION_HISTORY_REPORT, highlighted: false, isConsep: true, department: 'Testing' @@ -381,8 +381,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedlotObservationHistory', image: 'TableSplit', - header: 'Seedlot Observation History', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seedlot observation history', + link: ROUTES.SEEDLOT_OBSERVATION_HISTORY, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -391,8 +391,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedlotOrRequestItemInventoryLocation', image: 'TableSplit', - header: 'Seedlot or Request Item Inventory Location', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seedlot or request item inventory location', + link: ROUTES.SEEDLOT_OR_REQUEST_ITEM_INVENTORY_LOCATION, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -401,8 +401,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedlotOwner', image: 'TableSplit', - header: 'Seedlot Owner', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seedlot owner', + link: ROUTES.SEEDLOT_OWNER, highlighted: false, isConsep: true, department: 'Seed and family lot' @@ -411,8 +411,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'seedlotTestHistoryReport', image: 'TableSplit', - header: 'Seedlot Test History Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Seedlot test history report', + link: ROUTES.SEEDLOT_TEST_HISTORY_REPORT, highlighted: false, isConsep: true, department: 'Testing' @@ -421,8 +421,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'stockCountStatusReport', image: 'TableSplit', - header: 'Stock Count Status Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Stock count status report', + link: ROUTES.STOCK_COUNT_STATUS_REPORT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -431,8 +431,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'testingActivitiesList', image: 'TableSplit', - header: 'Testing Activities List', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Testing activities list', + link: ROUTES.TESTING_ACTIVITIES_LIST, highlighted: false, isConsep: true, department: 'Testing' @@ -441,8 +441,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'testingActivitiesSummaryReport', image: 'TableSplit', - header: 'Testing Activities Summary Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Testing activities summary report', + link: ROUTES.TESTING_ACTIVITIES_SUMMARY_REPORT, highlighted: false, isConsep: true, department: 'Testing' @@ -451,8 +451,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'testingRequestsReport', image: 'TableSplit', - header: 'Testing Requests Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Testing requests report', + link: ROUTES.TESTING_REQUESTS_REPORT, highlighted: false, isConsep: true, department: 'Testing' @@ -462,7 +462,7 @@ const FavouriteActivityMap: Record = { type: 'waybills', image: 'TableSplit', header: 'Waybills', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + link: ROUTES.WAYBILLS, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -471,8 +471,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'withdrawalDate', image: 'TableSplit', - header: 'Withdrawal Date', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Withdrawal date', + link: ROUTES.WITHDRAWAL_DATE, highlighted: false, isConsep: true, department: 'Testing' @@ -481,8 +481,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'withdrawalRequest', image: 'TableSplit', - header: 'Withdrawal Request', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Withdrawal request', + link: ROUTES.WITHDRAWAL_REQUEST, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -491,8 +491,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'withdrawalRequestDetailsReport', image: 'TableSplit', - header: 'Withdrawal Request Details Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Withdrawal request details report', + link: ROUTES.WITHDRAWAL_REQUEST_DETAILS_REPORT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -501,8 +501,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'withdrawalRequestReport', image: 'TableSplit', - header: 'Withdrawal Request Report', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Withdrawal request report', + link: ROUTES.WITHDRAWAL_REQUEST_REPORT, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -511,8 +511,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'withdrawalResults', image: 'TableSplit', - header: 'Withdrawal Results', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Withdrawal results', + link: ROUTES.WITHDRAWAL_RESULTS, highlighted: false, isConsep: true, department: 'Withdrawal' @@ -521,8 +521,8 @@ const FavouriteActivityMap: Record = { id: -1, type: 'withdrawalResultObservations', image: 'TableSplit', - header: 'Withdrawal Result Observations', - link: ROUTES.CONSEP_FAVOURITE_ACTIVITIES, + header: 'Withdrawal result observations', + link: ROUTES.WITHDRAWAL_RESULT_OBSERVATIONS, highlighted: false, isConsep: true, department: 'Withdrawal' diff --git a/frontend/src/routes/constants.ts b/frontend/src/routes/constants.ts index 08f444332..6bb16707c 100644 --- a/frontend/src/routes/constants.ts +++ b/frontend/src/routes/constants.ts @@ -15,7 +15,55 @@ const ROUTES = { FOUR_OH_FOUR: '/404', FOUR_OH_THREE: '/403', SERVICE_STATUS: '/service-status', - CONSEP_FAVOURITE_ACTIVITIES: '/consep/favourite-activities' + CONSEP_FAVOURITE_ACTIVITIES: '/consep/favourite-activities', + ACTUAL_GERMINATION_COUNT: '/consep/actual-germination-count', + CALCULATE_CROP_AVERAGE: '/consep/calculate-crop-average', + CANCELLED_REQUESTS_REPORT: '/consep/cancelled-requests-report', + CONE_AND_SEED_PROCESSING_REPORT: '/consep/cone-and-seed-processing-report', + CONE_AND_SEED_SHIPMENT_RECEIPT: '/consep/cone-and-seed-shipment-receipt', + CREATE_GERMINATION_TRAY: '/consep/create-germination-tray', + CSP_REQUEST: '/consep/csp-request', + DO_NOT_START_LIST: '/consep/do-not-start-list', + FAMILY_LOT: '/consep/family-lot', + FAMILY_LOT_SUMMARY_REPORT: '/consep/family-lot-summary-report', + GERMINATION_SPECIES_AVERAGE: '/consep/germination-species-average', + GERM_COUNT_PREDICTIONS: '/consep/germ-count-predictions', + IDENTIFY_AVAILABLE_LONG_TERM_LOCATION: '/consep/identify-available-long-term-location', + IN_HOUSE_INVENTORY: '/consep/in-house-inventory', + INVENTORY_LOCATION_REPORT: '/consep/inventory-location-report', + KILN_PROGRAMS: '/consep/kiln-programs', + MAINTAIN_CLIENT_LOCATION: '/consep/maintain-client-location', + MAINTAIN_GERMINATION_TRAY_SCREEN: '/consep/maintain-germination-tray-screen', + MAINTAIN_LOCAL_CONTACTS: '/consep/maintain-local-contacts', + MAINTAIN_STANDARD_ACTIVITIES: '/consep/maintain-standard-activities', + MAINTAIN_STAT_HOLIDAYS: '/consep/maintain-stat-holidays', + MAINTAIN_WORK_PLANS: '/consep/maintain-work-plans', + MANUAL_MOISTURE_CONTENT: '/consep/manual-moisture-content', + PROCESSING_ACTIVITIES: '/consep/processing-activities', + RECORD_STOCK_COUNT_RESULTS: '/consep/record-stock-count-results', + REQUEST_CHANGES_REPORT: '/consep/request-changes-report', + RETURNED_SEED: '/consep/returned-seed', + REVIEW_PENDING_REQUEST: '/consep/review-pending-request', + SCHEDULE_OR_REVISE_REQUEST_ITEM_ACTIVITY: '/consep/schedule-or-revise-request-item-activity', + SEED_AND_FAMILY_LOT_REPORT: '/consep/seed-and-family-lot-report', + SEED_SALE_OR_TRANSFER_ACTIVITIES_SCREEN: '/consep/seed-sale-or-transfer-activities-screen', + SEEDLOT_CHARACTERISTICS: '/consep/seedlot-characteristics', + SEEDLOT_LOCATION_HISTORY_REPORT: '/consep/seedlot-location-history-report', + SEEDLOT_OBSERVATION_HISTORY: '/consep/seedlot-observation-history', + SEEDLOT_OR_REQUEST_ITEM_INVENTORY_LOCATION: '/consep/seedlot-or-request-item-inventory-location', + SEEDLOT_OWNER: '/consep/seedlot-owner', + SEEDLOT_TEST_HISTORY_REPORT: '/consep/seedlot-test-history-report', + STOCK_COUNT_STATUS_REPORT: '/consep/stock-count-status-report', + TESTING_ACTIVITIES_LIST: '/consep/testing-activities-list', + TESTING_ACTIVITIES_SUMMARY_REPORT: '/consep/testing-activities-summary-report', + TESTING_REQUESTS_REPORT: '/consep/testing-requests-report', + WAYBILLS: '/consep/waybills', + WITHDRAWAL_DATE: '/consep/withdrawal-date', + WITHDRAWAL_REQUEST: '/consep/withdrawal-request', + WITHDRAWAL_REQUEST_DETAILS_REPORT: '/consep/withdrawal-request-details-report', + WITHDRAWAL_REQUEST_REPORT: '/consep/withdrawal-request-report', + WITHDRAWAL_RESULTS: '/consep/withdrawal-results', + WITHDRAWAL_RESULT_OBSERVATIONS: '/consep/withdrawal-result-observations' }; export default ROUTES; diff --git a/frontend/src/views/CONSEP/FavouriteActivity/FavouriteActivityModal.tsx b/frontend/src/views/CONSEP/FavouriteActivity/FavouriteActivityModal.tsx new file mode 100644 index 000000000..32774e409 --- /dev/null +++ b/frontend/src/views/CONSEP/FavouriteActivity/FavouriteActivityModal.tsx @@ -0,0 +1,189 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import React, { useEffect, useState } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; + +import { + Modal, Table, TableHead, TableRow, TableHeader, Pagination, + TableBody, TableCell, TableContainer, DataTable, TableSelectRow, TableSelectAll +} from '@carbon/react'; +import * as Icons from '@carbon/icons-react'; + +import { getFavAct, postFavAct, deleteFavAct } from '../../../api-service/favouriteActivitiesAPI'; +import { FavActivityPostType } from '../../../types/FavActivityTypes'; + +import { textConfig, favActHeaders } from './constants'; +import { + HeaderProps, RowProps, FavouriteActivityModalProps, FavActivityTableProps +} from './definitions'; +import './styles.scss'; + +import FavouriteActivityMap from '../../../config/FavouriteActivityMap'; + +const allRows: RowProps[] = Object.keys(FavouriteActivityMap).map((key) => { + const activity = FavouriteActivityMap[key]; + if (activity.isConsep) { + return { + id: key, + activityName: activity.header, + department: activity.department || 'Unknown' + }; + } + return null; +}).filter((activity) => activity !== null); + +type Department = 'Testing' | 'Administrative' | 'Withdrawal' | 'Processing' | 'Seed and family lot'; + +const headerIconMap: Record = { + Testing: 'Chemistry', + Administrative: 'Tools', + Withdrawal: 'StayInside', + Processing: 'Industry', + 'Seed and family lot': 'SoilMoistureGlobal' +}; + +const FavouriteActivityModal = ({ open, setOpen }: FavouriteActivityModalProps) => { + const [selectedRows, setSelectedRows] = useState([]); + const [initActs, setInitActs] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + const [itemsPerPage, setItemsPerPage] = useState(10); + + const currentRows = allRows.slice( + (currentPage - 1) * itemsPerPage, + currentPage * itemsPerPage + ); + + const handleSelectRow = (row: RowProps) => { + setSelectedRows((prev: string[]) => (prev.includes(row.id) + ? prev.filter((id: string) => id !== row.id) + : [...prev, row.id])); + }; + + const queryClient = useQueryClient(); + + const favActQueryKey = ['favourite-activities']; + + const favActQuery = useQuery({ + queryKey: favActQueryKey, + queryFn: getFavAct + }); + + const addFavAct = useMutation({ + mutationFn: (actObjs: FavActivityPostType[]) => postFavAct(actObjs), + onSuccess: () => { + queryClient.invalidateQueries(favActQueryKey); + } + }); + + const removeFavAct = useMutation({ + mutationFn: (id: number) => deleteFavAct(id), + onSuccess: () => { + queryClient.invalidateQueries(favActQueryKey); + } + }); + + const handleSubmit = () => { + const newActs = selectedRows.filter((act) => !initActs.includes(act)) + .map((act) => ({ activity: act, isConsep: true })); + const removedActs = initActs.filter((act) => !selectedRows.includes(act)) + .map((act) => favActQuery.data?.filter((fav) => fav.type === act)[0]?.id) + .filter((id) => id !== undefined); + addFavAct.mutate(newActs); + removedActs.forEach((id) => removeFavAct.mutate(id)); + setInitActs(selectedRows); + setOpen(false); + }; + + useEffect( + () => { + if (favActQuery.isSuccess + && favActQuery.data) { + const consepData = favActQuery.data.filter((fav) => fav.isConsep === true) + .map((fav) => fav.type); + setSelectedRows(consepData); + setInitActs(consepData); + } + }, + [favActQuery.isSuccess] + ); + + return ( + { + setOpen(false); + }} + onRequestSubmit={handleSubmit} + size="sm" + > +

+ {textConfig.description} +

+ ( + + + + + + {headers.map((header: HeaderProps) => ( + + {header.header} + + ))} + + + + {rows.map((row: RowProps) => ( + handleSelectRow(row)} + > + = 12 && !selectedRows.includes(row.id)} + /> + {row.cells?.map((cell) => { + const Icon = Icons[headerIconMap[cell.value as keyof typeof headerIconMap]]; + return ( + + {Icon && ()} + {cell.value} + + ); + })} + + ))} + +
+
+ )} + /> + + { + setCurrentPage(page); + setItemsPerPage(pageSize); + }} + /> +
+ ); +}; + +export default FavouriteActivityModal; diff --git a/frontend/src/views/CONSEP/FavouriteActivity/constants.tsx b/frontend/src/views/CONSEP/FavouriteActivity/constants.tsx new file mode 100644 index 000000000..49e7a900f --- /dev/null +++ b/frontend/src/views/CONSEP/FavouriteActivity/constants.tsx @@ -0,0 +1,20 @@ +import { HeaderProps } from './definitions'; + +export const textConfig = { + title: 'Add favourite activity', + description: 'You can select up to 12 activities', + buttons: { + cancel: 'Cancel', + confirm: 'Add to favourites' + } +}; + +export const favActHeaders: HeaderProps[] = [ + { + key: 'activityName', + header: 'Activity Name' + }, + { + key: 'department', + header: 'Department' + }]; diff --git a/frontend/src/views/CONSEP/FavouriteActivity/definitions.tsx b/frontend/src/views/CONSEP/FavouriteActivity/definitions.tsx new file mode 100644 index 000000000..e4cf859fe --- /dev/null +++ b/frontend/src/views/CONSEP/FavouriteActivity/definitions.tsx @@ -0,0 +1,29 @@ +export type HeaderProps = { + key: string; + header: string; +}; + +export type CellProps = { + id: string; + value: string; +}; + +export type RowProps = { + id: string; + activityName: string; + department: string; + cells?: CellProps[]; +}; + +export type FavouriteActivityModalProps = { + open: boolean; + setOpen: Function; +}; + +export type FavActivityTableProps = { + rows: RowProps[]; + headers: HeaderProps[]; + getHeaderProps: (props: { header: HeaderProps }) => any; + getSelectionProps: (props: { row: RowProps }) => any; + getRowProps: (props: { row: RowProps }) => any; +}; diff --git a/frontend/src/views/CONSEP/FavouriteActivity/index.tsx b/frontend/src/views/CONSEP/FavouriteActivity/index.tsx index 382a0a9d3..f77d7f731 100644 --- a/frontend/src/views/CONSEP/FavouriteActivity/index.tsx +++ b/frontend/src/views/CONSEP/FavouriteActivity/index.tsx @@ -1,33 +1,91 @@ -import React from 'react'; +import React, { useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; import { FlexGrid, Row, - Column + Column, + Button, + Tooltip } from '@carbon/react'; +import { Information } from '@carbon/icons-react'; -import PageTitle from '../../../components/PageTitle'; import FavouriteActivities from '../../../components/FavouriteActivities'; +import FavouriteActivityModal from './FavouriteActivityModal'; +import { getFavAct } from '../../../api-service/favouriteActivitiesAPI'; + +import FavIcon from '../../../assets/img/fav-icon.svg'; import './styles.scss'; -const FavouriteActivity = () => ( - - - - - - - - -
- -
-
-
-
-); +const FavouriteActivity = () => { + const [open, setOpen] = useState(false); + + const handleSetOpen = (value: boolean) => { + setOpen(value); + }; + + const favActQueryKey = ['favourite-activities']; + + const favActQuery = useQuery({ + queryKey: favActQueryKey, + queryFn: getFavAct + }); + + return ( + + {favActQuery.isSuccess && favActQuery.data + && favActQuery.data.filter((fav) => fav.isConsep).length > 0 ? ( + <> + + + +

My favourite activities

+ + + + + +
+ +
+ + + +
+ + +
+ +
+
+
+ + ) : ( + + + My Icon +

You don’t have any favorites to show yet!

+

+ You can favorite your activities by clicking on add + favorite activity or by clicking on the heart icon inside each page +

+ +
+
+ )} + + +
+ ); +}; export default FavouriteActivity; diff --git a/frontend/src/views/CONSEP/FavouriteActivity/styles.scss b/frontend/src/views/CONSEP/FavouriteActivity/styles.scss index 3c6112b41..38b20455f 100644 --- a/frontend/src/views/CONSEP/FavouriteActivity/styles.scss +++ b/frontend/src/views/CONSEP/FavouriteActivity/styles.scss @@ -1,5 +1,5 @@ -@use '@bcgov-nr/nr-theme/design-tokens/variables.scss' as vars; -@use '@carbon/type'; +@use "@bcgov-nr/nr-theme/design-tokens/variables.scss" as vars; +@use "@carbon/type"; .dashboard-page { padding: 0 0 2.5rem 0; @@ -25,8 +25,99 @@ } p.fav-act-page-title-large { - @include type.type-style('heading-06'); + @include type.type-style("heading-06"); white-space: nowrap; max-width: 12.5rem; margin-left: 2.5rem; } + +.consep-fav-header-row { + justify-content: space-between; + .right-align { + text-align: right; + margin-right: 1rem; + } +} + +.favourite-activity-modal { + .#{vars.$bcgov-prefix}--tooltip-trigger__wrapper { + margin-top: 0.5rem; + } + + .#{vars.$bcgov-prefix}--btn { + block-size: 0; + } + + .#{vars.$bcgov-prefix}--modal-content { + padding-bottom: 2.5rem; + } + + .#{vars.$bcgov-prefix}--modal-footer { + height: auto; + } + + .#{vars.$bcgov-prefix}--modal-container { + width: 60rem; + } + + p.favourite-activity-modal-description { + @include type.type-style("body-short-01"); + margin-top: -0.5rem; + margin-bottom: 2rem; + } + + .fav-act-row > svg { + vertical-align: middle; + margin-right: 0.4rem; + color: #0073e6; + } + .fav-act-row > span { + vertical-align: middle; + } + .fav-act-row { + vertical-align: middle; + } +} + +.consep-favourite-activities-title { + margin-left: 0.2rem; +} + +.consep-favourite-activity-tooltip { + top: 0.375rem; + padding-left: 0.625rem; +} + +.consep-add-fav-action { + text-align: right; +} + +.consep-add-fav-btn { + width: fit-content; + max-width: 10rem; + white-space: nowrap; +} + +th > .bx--checkbox--inline > .bx--checkbox-label { + display: none; + visibility: hidden; +} + +.consep-fav-non-content-section { + padding: 4rem; + + .consep-fav-non-content-title { + @include type.type-style("heading-04"); + margin-top: 2rem; + } + + .consep-fav-non-content-subtitle { + @include type.type-style("body-compact-02"); + margin-top: 0.5rem; + } + + .consep-fav-non-content-btn { + margin-top: 2rem; + max-width: 16rem; + } +} \ No newline at end of file