Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage Products Feature #1943

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions front/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import CreateTransferAgreementView from "views/Transfers/CreateTransferAgreement
import CreateShipmentView from "views/Transfers/CreateShipment/CreateShipmentView";
import ShipmentsOverviewView from "views/Transfers/ShipmentsOverview/ShipmentsOverviewView";
import ShipmentView from "views/Transfers/ShipmentView/ShipmentView";
import Products from "views/Products/ProductsView";
import ProductCreateView from "views/ProductCreate/ProductCreateView";
import QrReaderView from "views/QrReader/QrReaderView";
import NotFoundView from "views/NotFoundView/NotFoundView";
import { AuthorizeProps, useAuthorization } from "hooks/useAuthorization";
Expand Down Expand Up @@ -256,6 +258,40 @@ function App() {
/>
</Route>
</Route>
<Route path="products">
<Route
index
element={
<Protected
component={
<ErrorBoundary
fallback={
<AlertWithoutAction alertText="Could not fetch products data! Please try reloading the page." />
}
>
<Suspense fallback={<TableSkeleton />}>
<Products />
</Suspense>
</ErrorBoundary>
}
redirectPath={prevLocation}
requiredAbps={["manage_products"]}
/>
}
/>
<Route path="create">
<Route
index
element={
<Protected
component={<ProductCreateView />}
redirectPath={prevLocation}
requiredAbps={["manage_products"]}
/>
}
/>
</Route>
</Route>
</Route>
</Route>
<Route path="boxes">
Expand Down
2 changes: 1 addition & 1 deletion front/src/components/HeaderMenu/HeaderMenuContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function HeaderMenuContainer() {
external: true,
},
{
link: `${oldAppUrlWithBase}&action=products`,
link: `/bases/${baseId}/products`,
name: "Manage Products",
requiredAbps: ["manage_products"],
external: true,
Expand Down
2 changes: 2 additions & 0 deletions front/src/components/HeaderMenu/expandedMenuIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export function expandedMenuIndex() {
case "shipments":
case "agreements":
return [2]
case "products":
return [5]
default:
return undefined
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,17 @@ import {
PopoverTrigger as OrigPopoverTrigger,
Button,
} from "@chakra-ui/react";
import React, { useMemo } from "react";
import { ColumnInstance } from "react-table";
import { RiLayoutColumnFill } from "react-icons/ri";
import { BoxRow } from "./types";

const PopoverTrigger: React.FC<{ children: React.ReactNode }> = OrigPopoverTrigger;

interface IColumnSelectorProps {
availableColumns: ColumnInstance<BoxRow>[];
availableColumns: ColumnInstance<Record<string, unknown>>[];
}

function ColumnSelector({ availableColumns }: IColumnSelectorProps) {
const availableColumnOptions = useMemo(
() =>
availableColumns.filter((column) => column.id !== "shipment" && column.id !== "selection"),
[availableColumns],
);

const selectedColumnsCount = availableColumnOptions.filter(
const selectedColumnsCount = availableColumns.filter(
(column) => column.getToggleHiddenProps().checked,
).length;

Expand All @@ -46,7 +38,7 @@ function ColumnSelector({ availableColumns }: IColumnSelectorProps) {
<PopoverCloseButton />
<PopoverBody textStyle="h1">
<Flex flexWrap="wrap">
{availableColumnOptions.map((column) => (
{availableColumns.map((column) => (
<Checkbox
m={1}
py={1}
Expand Down
47 changes: 47 additions & 0 deletions front/src/components/Table/GlobalFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useState, ChangeEventHandler } from "react";
import { useAsyncDebounce } from "react-table";
import { SearchIcon } from "@chakra-ui/icons";
import {
Input,
InputGroup,
InputLeftElement,
useDisclosure,
useMediaQuery,
} from "@chakra-ui/react";

interface IProps {
globalFilter: string;
setGlobalFilter: (filterValue: string | undefined) => void;
}

export function GlobalFilter({ globalFilter, setGlobalFilter }: IProps) {
const [value, setValue] = useState<string>(globalFilter);
const { isOpen, onToggle } = useDisclosure();
const [isLargerThan768] = useMediaQuery("(min-width: 768px)");
const onChange = useAsyncDebounce((val: string) => {
setGlobalFilter(val || undefined);
}, 200);

const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const val = e.target.value;
setValue(val);
onChange(val);
};

return (
<InputGroup width="auto" variant="filled">
<InputLeftElement onClick={onToggle} cursor={isLargerThan768 ? "inherit" : "pointer"}>
<SearchIcon />
</InputLeftElement>
<Input
_focus={{ bg: "gray.200" }}
w={isLargerThan768 || isOpen ? "auto" : "0"}
pr={isLargerThan768 || isOpen ? "auto" : "0"}
borderRadius={0}
value={value || ""}
onChange={handleChange}
placeholder="Search"
/>
</InputGroup>
);
}
2 changes: 1 addition & 1 deletion front/src/components/Table/TableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface IFilteringSortingTableHeaderProps {

export function FilteringSortingTableHeader({ headerGroups }: IFilteringSortingTableHeaderProps) {
return (
<Thead position="sticky" top={0} background="white">
<Thead position="sticky" top={0} background="white" zIndex={2}>
{headerGroups.map((headerGroup: HeaderGroup, idx) => (
<Tr {...headerGroup.getHeaderGroupProps()} key={idx}>
{headerGroup.headers.map((column, idx) => (
Expand Down
3 changes: 3 additions & 0 deletions front/src/queries/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { FragmentOf, ResultOf, VariablesOf } from "gql.tada";
import { BOX_QUERY, BOX_BY_LABEL_IDENTIFIER_AND_ALL_SHIPMENTS_QUERY } from "./queries";
import { UPDATE_BOX_MUTATION } from "views/Box/BoxView";
import { BOXES_FOR_BOXESVIEW_QUERY } from "views/Boxes/BoxesView";
import { STANDARD_PRODUCTS_FOR_PRODUCTVIEW_QUERY } from "views/Products/ProductsView";
import { BOX_FIELDS_FRAGMENT, DISTRO_EVENT_FIELDS_FRAGMENT, SHIPMENT_DETAIL_FIELDS_FRAGMENT, SHIPMENT_FIELDS_FRAGMENT, TRANSFER_AGREEMENT_FIELDS_FRAGMENT } from "./fragments";

export type DistributionEventState = FragmentOf<typeof DISTRO_EVENT_FIELDS_FRAGMENT>["state"];
Expand All @@ -17,3 +18,5 @@ export type TransferAgreements = FragmentOf<typeof TRANSFER_AGREEMENT_FIELDS_FRA
export type UpdateBoxMutation = ResultOf<typeof UPDATE_BOX_MUTATION>["updateBox"];
export type BoxesForBoxesViewQuery = ResultOf<typeof BOXES_FOR_BOXESVIEW_QUERY>;
export type BoxesForBoxesViewVariables = VariablesOf<typeof BOXES_FOR_BOXESVIEW_QUERY>;
export type StandardProductsforProductsViewQuery = ResultOf<typeof STANDARD_PRODUCTS_FOR_PRODUCTVIEW_QUERY>;
export type StandardProductsforProductsViewVariables = VariablesOf<typeof STANDARD_PRODUCTS_FOR_PRODUCTVIEW_QUERY>;
13 changes: 6 additions & 7 deletions front/src/views/Boxes/components/BoxesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ import {
boxesRawDataToTableDataTransformer,
prepareBoxesForBoxesViewQueryVariables,
} from "./transformers";
import ColumnSelector from "./ColumnSelector";
import { useBaseIdParam } from "hooks/useBaseIdParam";
import { BoxesForBoxesViewVariables, BoxesForBoxesViewQuery } from "queries/types";
import ColumnSelector from "components/Table/ColumnSelector";

interface IBoxesTableProps {
tableConfig: IUseTableConfigReturnType;
Expand Down Expand Up @@ -93,12 +93,7 @@ function BoxesTable({
previousPage,
selectedFlatRows,
} = useTable(
// TODO: remove this ts-ignore again and try to fix the type error properly
// was most likely caused by setting one of the following flags in .tsconfig:
// "strictNullChecks": true
// "strictFunctionTypes": false
{
// @ts-ignore
columns,
data: tableData,
filterTypes,
Expand Down Expand Up @@ -169,7 +164,11 @@ function BoxesTable({
<ButtonGroup mb={2}>{actionButtons}</ButtonGroup>
<Spacer />
<HStack spacing={2} mb={2}>
<ColumnSelector availableColumns={allColumns} />
<ColumnSelector
availableColumns={allColumns.filter(
(column) => column.id !== "shipment" && column.id !== "selection",
)}
/>
<GlobalFilter globalFilter={globalFilter} setGlobalFilter={setGlobalFilter} />
</HStack>
</Flex>
Expand Down
6 changes: 3 additions & 3 deletions front/src/views/Boxes/components/transformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ export const boxesRawDataToTableDataTransformer = (boxesQueryResult: BoxesForBox
({
id: element.id,
labelIdentifier: element.labelIdentifier,
product: element.product!.name,
productCategory: element.product!.category.name,
gender: element.product!.gender,
product: element.product?.name,
productCategory: element.product?.category.name,
gender: element.product?.gender,
numberOfItems: element.numberOfItems,
size: element.size?.label,
state: element.state,
Expand Down
77 changes: 77 additions & 0 deletions front/src/views/ProductCreate/ProductCreateView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useNavigate } from "react-router-dom";
import { Box, Button, Center, Heading, HStack, Stack, Text, Switch } from "@chakra-ui/react";
import { MobileBreadcrumbButton } from "components/BreadcrumbNavigation";
import { useContext } from "react";
import { GlobalPreferencesContext } from "providers/GlobalPreferencesProvider";

function ProductCreateView() {
const navigate = useNavigate();
const { globalPreferences } = useContext(GlobalPreferencesContext);
const baseName = globalPreferences.selectedBase?.name;

return (
<>
<MobileBreadcrumbButton label="Back to Manage Products" linkPath=".." />
<Center>
{/* <form action=""> */}
<Box w={["100%", "100%", "60%", "40%"]}>
<Heading fontWeight="bold" mb={8} as="h1">
Enable New Product
</Heading>
<Box border="2px" mb={8}>
<HStack mb={4} borderBottom="2px" p={2}>
<Text fontWeight="bold" fontSize="md">
PRODUCT SETUP
</Text>
</HStack>
<HStack my={4} p={2}>
<Switch id="custom-product" mr={2} />
<Text fontWeight="medium" fontSize="md">
Custom Product ({baseName} Specific)
</Text>
</HStack>
</Box>
<Box border="2px" mb={8}>
<HStack mb={4} borderBottom="2px" p={2}>
<Text fontWeight="bold" fontSize="md">
FREE SHOP SETTINGS
</Text>
</HStack>
<HStack my={4} p={2}>
<Switch id="show-in-stockroom" mr={2} />
<Text fontWeight="medium" fontSize="md">
Always Show in Stockroom?
</Text>
</HStack>
</Box>
<Stack spacing={4} mt={8}>
<Button
// isLoading={isFormLoading}
type="submit"
borderRadius="0"
w="full"
variant="solid"
backgroundColor="blue.500"
color="white"
>
Enable Product
</Button>
<Button
size="md"
type="button"
borderRadius="0"
w="full"
variant="outline"
onClick={() => navigate("..")}
>
Nevermind
</Button>
</Stack>
</Box>
{/* </form> */}
</Center>
</>
);
}

export default ProductCreateView;
Loading