Skip to content

Commit

Permalink
Merge branch 'main' into ORV2-2223-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
praju-aot authored Jul 16, 2024
2 parents 59a5da8 + cdc1d7a commit e2a8028
Show file tree
Hide file tree
Showing 39 changed files with 3,167 additions and 60 deletions.
1 change: 0 additions & 1 deletion .github/workflows/pr-close.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ concurrency:
jobs:
cleanup:
name: Cleanup OpenShift and/or Promote Images
needs: [sleep]
uses: bcgov/quickstart-openshift-helpers/.github/workflows/[email protected]
secrets:
oc_namespace: ${{ vars.OC_NAMESPACE }}
Expand Down
2 changes: 1 addition & 1 deletion database/mssql/scripts/versions/v_28_ddl.sql
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ EXEC sys.sp_addextendedproperty
@level2type=N'COLUMN',
@level2name=N'DATE'
EXEC sys.sp_addextendedproperty
@name=N'MS_Description',
@name=N'MS_Description',
@value=N'Type of activity (e.g. ONHOLD, HOLDRMVD, CLOSED, REOPENED, OPENED).' ,
@level0type=N'SCHEMA',
@level0name=N'permit',
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { BrowserRouter as Router } from "react-router-dom";
import { AppRoutes } from "./routes/Routes";
import { ThemeProvider } from "@mui/material/styles";
import { createContext, Dispatch, useCallback, useEffect, useMemo, useState } from "react";
import {
createContext,
Dispatch,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AuthProvider, AuthProviderProps } from "react-oidc-context";
import { WebStorageStateStore } from "oidc-client-ts";
Expand Down Expand Up @@ -70,7 +77,8 @@ const App = () => {
const [onRouteBCClientNumber, setOnRouteBCClientNumber] =
useState<Optional<string>>();
const [companyLegalName, setCompanyLegalName] = useState<Optional<string>>();
const [isCompanySuspended, setIsCompanySuspended] = useState<Optional<boolean>>();
const [isCompanySuspended, setIsCompanySuspended] =
useState<Optional<boolean>>();
const [userDetails, setUserDetails] =
useState<Optional<BCeIDUserDetailContext>>();
const [idirUserDetails, setIDIRUserDetails] =
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/common/authentication/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export const ROLES = {
WRITE_LOA: "ORBC-WRITE-LOA",
READ_SUSPEND: "ORBC-READ-SUSPEND",
WRITE_SUSPEND: "ORBC-WRITE-SUSPEND",
READ_CREDIT_ACCOUNT: "ORBC-READ-CREDIT-ACCOUNT",
WRITE_CREDIT_ACCOUNT: "ORBC-WRITE-CREDIT-ACCOUNT",
} as const;

/**
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/common/components/snackbar/CustomSnackBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { BC_COLOURS } from "../../../themes/bcGovStyles";

export type SnackbarAlertType = "info" | "error" | "success";

/**
* Type for displaying snackbar (aka toast message) after an operation.
*/
export interface SnackBarOptions {
showSnackbar: boolean;
setShowSnackbar: React.Dispatch<React.SetStateAction<boolean>>;
message: string;
alertType: "info" | "error" | "success";
alertType: SnackbarAlertType;
}

/**
Expand Down Expand Up @@ -53,6 +55,9 @@ export const CustomSnackbar = ({
<Alert
onClose={handleClose}
severity={alertType}
sx={{
boxShadow: "0 0 1rem #31313229",
}}
// hide close button
action={<></>}
iconMapping={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export const IDIRCompanySearchResults = memo(
isSuspended,
};
});

setIsCompanySuspended?.(() => isSuspended);

navigate(routes.IDIR_ROUTES.CREATE_COMPANY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import { BCEID_PROFILE_TABS } from "../../types/manageProfile.d";
import { ERROR_ROUTES, PROFILE_ROUTES } from "../../../../routes/constants";
import { getDefaultRequiredVal } from "../../../../common/helpers/util";
import { isIDIR } from "../../../../common/authentication/auth-walls/BCeIDAuthWall";
import { canViewCreditAccountTab } from "../../../settings/helpers/permissions";
import { CreditAccount } from "../../../settings/pages/CreditAccount";
import { useGetCreditAccountQuery } from "../../../settings/hooks/creditAccount";
import { useFeatureFlagsQuery } from "../../../../common/hooks/hooks";
import { CREDIT_ACCOUNT_USER_TYPE } from "../../../settings/types/creditAccount";

interface ProfileDashboardTab {
label: string;
Expand Down Expand Up @@ -52,12 +57,28 @@ export const ManageProfilesDashboard = React.memo(() => {
});

const navigate = useNavigate();
const { userRoles } = useContext(OnRouteBCContext);
const { userRoles, companyId: companyIdFromContext } =
useContext(OnRouteBCContext);
const companyId = getDefaultRequiredVal(0, companyIdFromContext);
const { user } = useAuth();
const { data: creditAccount } = useGetCreditAccountQuery(companyId);
const { data: featureFlags } = useFeatureFlagsQuery();
const populatedUserRoles = getDefaultRequiredVal([], userRoles);
const isIDIRUser = isIDIR(user?.profile?.identity_provider as string);
const isBCeIDAdmin = isBCeIDOrgAdmin(populatedUserRoles);
const shouldAllowUserManagement = isBCeIDAdmin || isIDIRUser;
const creditAccountHolder = creditAccount?.creditAccountUsers.find(
(user) => user.userType === CREDIT_ACCOUNT_USER_TYPE.HOLDER,
);
const isCreditAccountHolder = companyId === creditAccountHolder?.companyId;

const showCreditAccountTab = Boolean(
canViewCreditAccountTab(userRoles) &&
creditAccount &&
companyId &&
isCreditAccountHolder &&
featureFlags?.["CREDIT-ACCOUNT"] === "ENABLED",
);

const { state: stateFromNavigation } = useLocation();

Expand All @@ -67,17 +88,28 @@ export const ManageProfilesDashboard = React.memo(() => {
component: <CompanyInfo companyInfoData={companyInfoData} />,
componentKey: BCEID_PROFILE_TABS.COMPANY_INFORMATION,
},
!isIDIRUser ? {
label: "My Information",
component: <MyInfo />,
componentKey: BCEID_PROFILE_TABS.MY_INFORMATION,
} : null,
shouldAllowUserManagement ? {
label: "User Management",
component: <UserManagement />,
componentKey: BCEID_PROFILE_TABS.USER_MANAGEMENT,
} : null
].filter(tab => Boolean(tab)) as ProfileDashboardTab[];
!isIDIRUser
? {
label: "My Information",
component: <MyInfo />,
componentKey: BCEID_PROFILE_TABS.MY_INFORMATION,
}
: null,
shouldAllowUserManagement
? {
label: "User Management",
component: <UserManagement />,
componentKey: BCEID_PROFILE_TABS.USER_MANAGEMENT,
}
: null,
showCreditAccountTab
? {
label: "Credit Account",
component: <CreditAccount companyId={companyId} />,
componentKey: BCEID_PROFILE_TABS.CREDIT_ACCOUNT,
}
: null,
].filter((tab) => Boolean(tab)) as ProfileDashboardTab[];

const getSelectedTabFromNavigation = (): number => {
const tabIndex = tabs.findIndex(
Expand All @@ -91,22 +123,21 @@ export const ManageProfilesDashboard = React.memo(() => {
const showAddUserButton = (selectedTabIndex: number) => {
// Get index of User Management tab, if it exists
const userManagementTabIndex = tabs.findIndex(
tab => tab.componentKey === BCEID_PROFILE_TABS.USER_MANAGEMENT,
(tab) => tab.componentKey === BCEID_PROFILE_TABS.USER_MANAGEMENT,
);

return shouldAllowUserManagement && selectedTabIndex === userManagementTabIndex;
return (
shouldAllowUserManagement && selectedTabIndex === userManagementTabIndex
);
};

const initialSelectedTabIndex = getSelectedTabFromNavigation();
const [shouldShowAddUserButton, setShouldShowAddUserButton] = useState<boolean>(
showAddUserButton(initialSelectedTabIndex),
);
const [shouldShowAddUserButton, setShouldShowAddUserButton] =
useState<boolean>(showAddUserButton(initialSelectedTabIndex));

// Set whether or not to show "Add User" button when tab changes
const handleTabChange = (selectedTabIndex: number) => {
setShouldShowAddUserButton(
showAddUserButton(selectedTabIndex),
);
setShouldShowAddUserButton(showAddUserButton(selectedTabIndex));
};

if (isPending) {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/features/manageProfile/types/manageProfile.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ export type CompanyProfile = {
*/
export type UpdateCompanyProfileRequest = Omit<
CompanyProfile,
"clientNumber"
| "isSuspended"
"clientNumber" | "isSuspended"
>;

/**
Expand Down Expand Up @@ -174,4 +173,5 @@ export const BCEID_PROFILE_TABS = {
MY_INFORMATION: "MyInformationTab",
USER_MANAGEMENT: "UserManagementTab",
PAYMENT_INFORMATION: "PaymentInformationTab",
CREDIT_ACCOUNT: "CreditAccountTab",
} as const;
138 changes: 138 additions & 0 deletions frontend/src/features/settings/apiManager/creditAccount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
CreditAccountData,
CreditAccountLimitType,
CreditAccountStatusType,
} from "../types/creditAccount";
import { CREDIT_ACCOUNT_API_ROUTES } from "../apiManager/endpoints/endpoints";
import {
httpDELETERequest,
httpGETRequest,
httpPOSTRequest,
httpPUTRequest,
} from "../../../common/apiManager/httpRequestHandler";
import { CompanyProfile } from "../../manageProfile/types/manageProfile";

/**
* Backend request to create a credit account.
* @param creditAccountData Information about the credit account action for the company
* @returns Result of the credit account action, or error on fail
*/
export const createCreditAccount = async (data: {
companyId: number;
creditLimit: CreditAccountLimitType;
}) => {
const { companyId, creditLimit } = data;
return await httpPOSTRequest(
CREDIT_ACCOUNT_API_ROUTES.CREATE_CREDIT_ACCOUNT(companyId),
{ creditLimit },
);
};

/**
* Get credit account information for related to the given company ID
* @returns Credit account information for the company
*/
export const getCreditAccount = async (
companyId: number,
): Promise<CreditAccountData> => {
const response = await httpGETRequest(
CREDIT_ACCOUNT_API_ROUTES.GET_CREDIT_ACCOUNT(companyId),
);
return response.data;
};

/**
* Get credit account users for the given credit account ID
* @param companyId Identifier of the company with which the credit Account is associated
* @param creditAccountId Identifier of the credit account to retrieve
* @returns List of credit account users for the credit account
*/
export const getCreditAccountUsers = async (data: {
companyId: number;
creditAccountId: number;
}) => {
const { companyId, creditAccountId } = data;
const response = await httpGETRequest(
CREDIT_ACCOUNT_API_ROUTES.GET_CREDIT_ACCOUNT_USERS(
companyId,
creditAccountId,
),
);
return response.data;
};

/**
* Add a user to credit account
* @param companyId Identifier of the company with which the credit Account is associated
* @param creditAccountId Id of the credit account we wish to add a user to
* @param userData Id of the company who is being added to the credit account
* @returns companyProfile
*/
export const addCreditAccountUser = async (data: {
companyId: number;
creditAccountId: number;
userData: CompanyProfile;
}) => {
const { companyId, creditAccountId, userData } = data;
const response = await httpPUTRequest(
CREDIT_ACCOUNT_API_ROUTES.ADD_CREDIT_ACCOUNT_USER(
companyId,
creditAccountId,
),
{
companyId: userData.companyId,
},
);
return response;
};

/**
* Removes active users of a credit account identified by their client numbers.
* @param companyId Identifier of the company with which the credit Account is associated
* @param userClientNumbers The array of user client numbers of the users to be removed.
* @returns A promise indicating the success or failure of the remove operation.
*/
export const removeCreditAccountUsers = async (data: {
companyId: number;
creditAccountId: number;
companyIds: number[];
}) => {
const { companyId, creditAccountId, companyIds } = data;
const response = await httpDELETERequest(
CREDIT_ACCOUNT_API_ROUTES.REMOVE_CREDIT_ACCOUNT_USER(
companyId,
creditAccountId,
),
{
companyIds,
},
);
return response;
};

/**
* Backend request to hold/unhold/close/reopen a credit account.
* @param companyId Identifier of the company with which the credit Account is associated
* @param creditAccountId Id of the credit account we wish to add a user to
* @param status The intended status for the credit account to be updated to
* @param reason The reason why the status has been updated
* @returns Result of the update action, or error on fail
*/

export const updateCreditAccountStatus = async (data: {
companyId: number;
creditAccountId: number;
status: CreditAccountStatusType;
reason?: string;
}) => {
const { companyId, creditAccountId, status, reason } = data;

const response = await httpPUTRequest(
CREDIT_ACCOUNT_API_ROUTES.UPDATE_ACCOUNT_STATUS(companyId, creditAccountId),
{
creditAccountStatusType: status,
comment: reason,
},
);
return response;
};
19 changes: 19 additions & 0 deletions frontend/src/features/settings/apiManager/endpoints/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,22 @@ export const SUSPEND_API_ROUTES = {
HISTORY: (companyId: number) => `${SUSPEND_API_BASE}/${companyId}/suspend`,
SUSPEND: (companyId: number) => `${SUSPEND_API_BASE}/${companyId}/suspend`,
};

const CREDIT_ACCOUNT_API_BASE = `${VEHICLES_URL}/companies`;

export const CREDIT_ACCOUNT_API_ROUTES = {
CREATE_CREDIT_ACCOUNT: (companyId: number) =>
`${CREDIT_ACCOUNT_API_BASE}/${companyId}/credit-account`,
GET_CREDIT_ACCOUNT: (companyId: number) =>
`${CREDIT_ACCOUNT_API_BASE}/${companyId}/credit-account`,
GET_COMPANY: (clientNumber: string) =>
`${CREDIT_ACCOUNT_API_BASE}/companies?page=1&take=1&clientNumber=${clientNumber}`,
GET_CREDIT_ACCOUNT_USERS: (companyId: number, creditAccountId: number) =>
`${CREDIT_ACCOUNT_API_BASE}/${companyId}/credit-account/${creditAccountId}/credit-account-user?includeAccountHolder=true`,
ADD_CREDIT_ACCOUNT_USER: (companyId: number, creditAccountId: number) =>
`${CREDIT_ACCOUNT_API_BASE}/${companyId}/credit-account/${creditAccountId}/credit-account-user`,
REMOVE_CREDIT_ACCOUNT_USER: (companyId: number, creditAccountId: number) =>
`${CREDIT_ACCOUNT_API_BASE}/${companyId}/credit-account/${creditAccountId}/credit-account-user`,
UPDATE_ACCOUNT_STATUS: (companyId: number, creditAccountId: number) =>
`${CREDIT_ACCOUNT_API_BASE}/${companyId}/credit-account/${creditAccountId}/status`,
};
Loading

0 comments on commit e2a8028

Please sign in to comment.