commit changes on Slales Order

This commit is contained in:
João Mendes 2023-11-06 10:39:10 +00:00
parent 1789b83c2d
commit 3c3f9ac6e8
9 changed files with 316 additions and 137 deletions

View File

@ -14,10 +14,12 @@ import { SearchBox } from '@fluentui/react-search-preview';
import { Icon } from '@iconify/react';
import { appGlobalStateAtom } from '../atoms/appGlobalStateAtom';
import { EMessageType } from '../constants/EMessageTypes';
import { useGraphAPI } from '../hooks/useGraphAPI';
import { ICustomer } from '../models/ICustomer';
import { IMenuItem } from '../models/IMenuItem';
import { IOrder } from '../models/IOrder';
import { ShowMessage } from '../showMessage/ShowMessage';
import { CompanyInfo } from './companyInfo/CompanyInfo';
import { CustomersGrid } from './customersGrid/CustomersGrid';
import { ISalesordersProps } from './ISalesordersProps';
@ -47,6 +49,7 @@ export const SalesordersControl: React.FunctionComponent<ISalesordersProps> = (
const [searchText, setSearchText] = React.useState<string>("");
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const [customers, setCustomers] = React.useState<ICustomer[]>([]);
const [error, setError] = React.useState<Error>((undefined as unknown) as Error);
React.useEffect(() => {
setAppglobalState({ ...appglobalState, ...props });
@ -59,13 +62,28 @@ export const SalesordersControl: React.FunctionComponent<ISalesordersProps> = (
React.useEffect(() => {
(async () => {
const orders = await searchOrders(searchText);
const customers = await getCustomers(searchText);
try {
switch (selectedItem.id) {
case 1:
setOrders(await searchOrders(searchText));
break;
case 2:
setCustomers(await getCustomers(searchText));
break;
default:
break;
}
} catch (error) {
setError(error);
} finally {
setIsLoading(false);
}
/* const customers = await getCustomers(searchText);
setCustomers(customers);
setOrders(orders );
setIsLoading(false);
setOrders(orders ); */
/* setIsLoading(false); */
})();
}, [searchText]);
}, [searchText, selectedItem]);
const totalOrders = React.useMemo(() => {
return orders.length;
@ -107,9 +125,8 @@ export const SalesordersControl: React.FunctionComponent<ISalesordersProps> = (
pendingOrders={totalPeding}
processingOrders={totalProcessing}
/>
<OrdersGrid items={orders} />
<OrdersGrid items={orders} />
</>
);
case 2:
@ -130,6 +147,24 @@ export const SalesordersControl: React.FunctionComponent<ISalesordersProps> = (
}
}, [selectedItem]);
const hasError = React.useMemo(() => {
return !error ? false : true;
}, [error]);
const RenderRightContent = React.useCallback(() => {
if (hasError) {
return <ShowMessage isShow={hasError} messageType={EMessageType.ERROR} message={error.message} />;
}
if (isLoading) {
return (
<div style={{ paddingTop: 60 }}>
<Spinner />
</div>
);
}
return <>{!hasOrders ? <NoOrders /> : renderSelectedContent()}</>;
}, [ hasError, error, isLoading, hasOrders, renderSelectedContent]);
return (
<>
<main className={styles.mainContainer}>
@ -146,7 +181,7 @@ export const SalesordersControl: React.FunctionComponent<ISalesordersProps> = (
style={{ fontSize: tokens.fontSizeBase200, width: 300 }}
onChange={(ev: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData) => {
if (data.value.trim().length > 2) {
setSearchText(data.value);
setSearchText(data.value);
}
}}
dismiss={
@ -158,13 +193,14 @@ export const SalesordersControl: React.FunctionComponent<ISalesordersProps> = (
}
/>
</div>
{isLoading ? (
<RenderRightContent />
{/* {isLoading ? (
<div style={{ paddingTop: 60 }}>
<Spinner />
</div>
) : (
<>{!hasOrders ? <NoOrders /> : renderSelectedContent()}</>
)}
)} */}
</Right>
</div>
</main>

View File

@ -19,6 +19,7 @@ import {
} from '@fluentui/react-components';
import { IOrder } from '../../models/IOrder';
import { useOrdersGridStyles } from './useOrdersGridStyles';
const columns: TableColumnDefinition<IOrder>[] = [
createTableColumn<IOrder>({
@ -197,8 +198,10 @@ export const OrdersGrid: React.FunctionComponent<IOrdersGridProps> = (
setSortState(nextSortState);
}, []);
const styles = useOrdersGridStyles();
return (
<div style={{ paddingTop: 30 }}>
<div className={styles.gridContainer}>
<DataGrid
items={items}
columns={columns}

View File

@ -0,0 +1,29 @@
import {
makeStyles,
shorthands,
tokens,
} from '@fluentui/react-components';
export const useOrdersGridStyles = makeStyles({
gridContainer: {
paddingTop: "30px",
width: "100%",
height: 'calc(100vh - 440px)',
backgroundColor: tokens.colorNeutralBackground2,
overflowY: "auto",
overflowX: "hidden",
"scrollbar-color": tokens.colorNeutralBackground1,
"scrollbar-width": "thin",
"::-webkit-scrollbar-thumb": {
backgroundColor: tokens?.colorBrandStroke2,
...shorthands.borderRadius("10px"),
...shorthands.borderWidth("1px"),
},
"::-webkit-scrollbar": {
height: "10px",
width: "7px",
},
},
});

View File

@ -6,30 +6,17 @@ import {
export const useSalesordersStyles = makeStyles({
mainContainer: {
width: "100%",
height: "100vh",
height: 'calc(100vh - 200px)',
backgroundColor: tokens.colorNeutralBackground2,
overflowY: "auto",
overflowX: "hidden",
"scrollbar-color": tokens.colorNeutralBackground1,
"scrollbar-width": "thin",
"::-webkit-scrollbar-thumb": {
backgroundColor: tokens?.colorBrandStroke2,
...shorthands.borderRadius("10px"),
...shorthands.borderWidth("1px"),
},
"::-webkit-scrollbar": {
height: "10px",
width: "7px",
},
},
contentContainer: {
display: "grid",
gridTemplateColumns: "min(100%, 300px) 1fr 1fr 1fr",
gridTemplateRows: "1fr",
height: "100vh",
height: 'calc(100vh - 200px)',
},
leftContainer : {
@ -74,6 +61,6 @@ export const useSalesordersStyles = makeStyles({
justifyContent: "space-between",
rowGap: "20px",
...shorthands.padding("20px 0px"),
} ,
});

View File

@ -0,0 +1,5 @@
export enum EMessageType {
INFO = 'info',
ERROR = 'error',
SUCCESS = 'success',
}

View File

@ -23,6 +23,86 @@ export const useGraphAPI = (context: BaseComponentContext): IuseGraphAPI => {
return await context.msGraphClientFactory.getClient("3");
}, [context]);
const mappingOrders = React.useCallback((result: SearchHit[]): IOrder[] => {
const ordersList: IOrder[] = [];
for (const item of result) {
const { resource } = item;
if (resource) {
const { properties } = resource as any;
if (properties) {
const {
id,
custcode,
custname,
email,
state,
country,
orders,
orderdates,
ordertotals,
orderstatus,
} = properties as any;
for (let i = 0; i < orders.length; i++) {
const order: IOrder = {
customer: custname,
city: `${state} ${country}`,
order: orders[i],
total: ordertotals[i],
orderDate: orderdates[i],
status: orderstatus[i],
customerCode: custcode,
custmoerEmail: email,
customerState: state,
id: id,
};
ordersList.push(order);
}
}
}
}
return ordersList;
}, [ graphClient]);
const mappingCustomers = React.useCallback((result: SearchHit[]): ICustomer[] => {
const customersList: ICustomer[] = [];
for (const item of result) {
const { resource } = item;
if (resource) {
const { properties } = resource as any;
if (properties) {
const {
custcode,
custname,
email,
state,
country,
orders,
orderdates,
ordertotals,
orderstatus,
} = properties as any;
const customer: ICustomer = {
customerName: custname,
customerCode: custcode,
customerEmail: email,
customerState: state,
customerCountry: country,
lastOrder: orders[orders.length - 1],
totalOrders: orders.length,
lastOrderDate: orderdates[orderdates.length - 1],
lastOrderTotal: ordertotals[ordertotals.length - 1],
lastOrderStatus: orderstatus[orderstatus.length - 1],
};
customersList.push(customer);
}
}
}
return customersList;
}, [ graphClient]);
const searchOrders = React.useCallback(
async (searchText: string): Promise<any> => {
if (!graphClient) return undefined;
@ -45,55 +125,9 @@ export const useGraphAPI = (context: BaseComponentContext): IuseGraphAPI => {
const response = await (await graphClient)?.api(`search/query`).post(request);
const result: SearchHit[] = response?.value[0]?.hitsContainers[0]?.hits;
if (!result) return [];
const ordersList: IOrder[] = [];
for (const item of result) {
const { resource } = item;
if (resource) {
const { properties } = resource as any;
if (properties) {
const {
id,
custcode,
custname,
email,
state,
country,
orders,
orderdates,
ordertotals,
orderstatus,
} = properties as any;
if (!result) return [];
if (!custname || !state || !country || !orders || !orderdates || !ordertotals || !orderstatus) continue;
for (let i = 0; i < orders.length; i++) {
const order: IOrder = {
customer: custname,
city: `${state} ${country}`,
order: orders[i],
total: ordertotals[i],
orderDate: orderdates[i],
status: orderstatus[i],
customerCode: custcode,
custmoerEmail: email,
customerState: state,
id: id,
};
ordersList.push(order);
}
}
}
}
/* setCacheValue(searchText, ordersList); */
const ordersList = mappingOrders(result);
console.log(ordersList);
return ordersList;
} catch (error) {
@ -104,69 +138,36 @@ export const useGraphAPI = (context: BaseComponentContext): IuseGraphAPI => {
[graphClient]
);
const getCustomers = React.useCallback(async (searchText:string): Promise<ICustomer[] | []> => {
if (!graphClient) return [];
const request = {
requests: [
{
entityTypes: ["externalItem"],
contentSources: ["/external/connections/ibmdb2lob"],
query: {
queryString: `${searchText}*`,
const getCustomers = React.useCallback(
async (searchText: string): Promise<ICustomer[] | []> => {
if (!graphClient) return [];
const request = {
requests: [
{
entityTypes: ["externalItem"],
contentSources: ["/external/connections/ibmdb2lob"],
query: {
queryString: `${searchText}*`,
},
from: 0,
size: 100,
},
from: 0,
size: 100,
},
],
};
],
};
try {
const response = await (await graphClient)?.api(`search/query`).post(request);
try {
const response = await (await graphClient)?.api(`search/query`).post(request);
const result: SearchHit[] = response?.value[0]?.hitsContainers[0]?.hits;
const customersList: ICustomer[] = [];
for (const item of result) {
const { resource } = item;
if (resource) {
const { properties } = resource as any;
if (properties) {
const {
custcode,
custname,
email,
state,
country,
orders,
orderdates,
ordertotals,
orderstatus,
} = properties as any;
const customer: ICustomer = {
customerName: custname,
customerCode: custcode,
customerEmail: email,
customerState: state,
customerCountry: country,
lastOrder: orders[orders.length - 1],
totalOrders: orders.length,
lastOrderDate: orderdates[orderdates.length - 1],
lastOrderTotal: ordertotals[ordertotals.length - 1],
lastOrderStatus: orderstatus[orderstatus.length - 1],
};
customersList.push(customer);
}
}
const result: SearchHit[] = response?.value[0]?.hitsContainers[0]?.hits;
const customersList = mappingCustomers(result);
const uniqueCustomers = uniqBy(customersList, "customerCode");
return sortBy(uniqueCustomers, "customerName");
} catch (error) {
console.log("[getCustomers] error:", error);
throw new Error("Something went wrong when getting customers");
}
const uniqueCustomers = uniqBy(customersList, "customerCode");
return sortBy(uniqueCustomers, "customerName");
} catch (error) {
console.log("[getCustomers] error:", error);
throw new Error("Something went wrong when getting customers");
}
}, [graphClient]);
},
[graphClient]
);
return {
searchOrders,

View File

@ -0,0 +1,10 @@
import * as React from 'react';
import { EMessageType } from '../constants/EMessageTypes';
export interface IShowMessageProps {
isShow: boolean;
messageType: EMessageType;
message: string | React.ReactNode;
children?: React.ReactNode;
}

View File

@ -0,0 +1,73 @@
import * as React from 'react';
import {
Body1,
Body1Strong,
tokens,
} from '@fluentui/react-components';
import {
CheckmarkCircle32Regular,
Info32Regular,
} from '@fluentui/react-icons';
import { Icon } from '@iconify/react';
import { EMessageType } from '../constants/EMessageTypes';
import { IShowMessageProps } from './IShowMessageProps';
import { useShowMessageStyles } from './useShowMessageStyles';
export const ShowMessage: React.FunctionComponent<IShowMessageProps> = (
props: React.PropsWithChildren<IShowMessageProps>
) => {
const { messageType, children, message, isShow } = props;
const styles = useShowMessageStyles();
const [renderMessageIcon, setRenderMessageIcon] = React.useState<JSX.Element | null>(null);
const RenderError = React.useCallback(() => {
return (
<>
<div className={styles.errorContainer}>
<div className={styles.errorIcon}>
<Icon
icon="fluent:error-circle-24-regular"
width="32"
height="32"
color={tokens.colorStatusDangerForeground1}
/>
</div>
<Body1 style={{ width: "100%" }}>{message}</Body1>
</div>
</>
);
}, [message]);
React.useEffect(() => {
switch (messageType) {
case EMessageType.SUCCESS:
setRenderMessageIcon(<CheckmarkCircle32Regular primaryFill={tokens.colorStatusSuccessForeground1} />);
break;
case EMessageType.INFO:
setRenderMessageIcon(<Info32Regular primaryFill={tokens.colorStatusWarningForeground1} />);
break;
default:
break;
}
}, [messageType]);
if (!isShow) {
return <></>;
}
if (messageType === EMessageType.ERROR) {
return <RenderError />;
}
return (
<>
<div className={styles.root}>
{renderMessageIcon}
<Body1Strong>{messageType}</Body1Strong>
{children}
</div>
</>
);
};

View File

@ -0,0 +1,35 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
makeStyles,
shorthands,
tokens,
} from '@fluentui/react-components';
export const useShowMessageStyles = makeStyles({
root: {
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
...shorthands.padding("10px"),
...shorthands.gap("10px"),
width: '100%',
} ,
iconClass: {
width: "32px",
height: "32px",
} as any,
errorContainer: {
display: "flex",
flexDirection: "row",
justifyContent: "start",
alignItems: "center",
...shorthands.gap("10px"),
...shorthands.padding("10px"),
backgroundColor: tokens.colorStatusDangerBackground1,
} ,
errorIcon: {
width: "32px",
height: "32px",
} ,
});