1.0.6 ā€¢ Published 1 year ago

@jeeny/jeeny-react v1.0.6

Weekly downloads
-
License
ISC
Repository
github
Last release
1 year ago

The jeeny-react package provides an intuitive and typesafe way to interact with the Jeeny API. It is primarily designed to be "headless", just like the Jeeny API.

šŸ  Jeeny.com

What is Jeeny?

Jeeny is a warehouse management system and enterprise resource planning API. It is a headless system for procurement, inventory, standard operating procedures, manufacturing, and fulfillment. Without replacing your current systems you can extend, enhance, and embed in order to create the customizations your teams need.

Table of contents

What is a headless front end?

A headless front end library separates the UI from the logic. This lets the developer focus on user experience without having to think too much about how to retrieve, manipulate, and store data.

For example, the JeenyTable component returns no HTML tags. However, it has a renderTable prop where you supply your own table design. By defining the props for query, variables, and columns in the JeenyTable component, your table will automatically receive the data you want to display. Based on the value you enter for query, you will get autocomplete and typechecking functionality for the variables and columns props.

The goal is twofold - make it impossible to fail and let the API documentation fade into the background by making it part of the components. You will of course need to be using TypeScript to take full advantage of these features. The package can be used with good old JavaScript too though.

Installation

Yarn yarn add @jeeny/jeeny-react

npm npm install @jeeny/jeeny-react

Authentication

You must wrap your application in the JeenyProvider component and pass it your headless API key. This provider allows downstream components to authenticate with the Jeeny graphql server.

It includes the ApolloProvider component from the React Apollo Client. This means that the Jeeny hooks and components can take advantage of features like caching and the Apollo devtools.

You can get your free API key from the Jeeny Hub under the Headless menu.

import { JeenyProvider } from "@jeeny/jeeny-react"
<React.StrictMode>
	<JeenyProvider apiKey="YOUR_API_KEY">
		<App  />
	</JeenyProvider>
</React.StrictMode>

Tables

A headless wrapper that helps you create type-checked tables that work with the Jeeny API. The wrapper is an extension of TanStack Table v8.

The JeenyTable component is a headless component. It lets you take care of design and user experience while providing a typesafe way to easily access the Jeeny API.

The JeenyTable component gives you easy access to every list query in the Jeeny API.

The JeenyTable component has four main props. The first prop, query, indicates what GraphQL query you wish to interact with. Depending on that prop, whether it is supplierItem.getSupplierItemsByItem or items.getItems, the expectations of the variables and columns props will change. In the code example below, the variables prop expects a shape that matches QueryGetSupplierItemsByItemArgs and the columns prop will only accept id values that are in the SupplierItem record.

<JeenyTable
    query="supplierItem.getSupplierItemsByItem"
    variables={{ itemId: "6e6c677a-374b-47fc-8944-f215f56436b6" }}
    columns={[
      {
        id: "id",
      },
      {
        id: "brand",
      },
      {
        id: "brandSku",
        columnDef: {
          cell: (info) => info.getValue().toUpperCase(),
        },
      },
    ]}
    renderTable={({ table }) => {
      return (
        <table>
          <thead>
            {table.getHeaderGroups().map((headerGroup) => (
              <tr key={headerGroup.id}>
                {headerGroup.headers.map((header) => (
                  <th key={header.id}>
                    {header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.header,
                          header.getContext()
                        )}
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody>
            {table.getRowModel().rows.map((row) => (
              <tr key={row.id}>
                {row.getVisibleCells().map((cell) => (
                  <td key={cell.id}>
                    {flexRender(cell.column.columnDef.cell, cell.getContext())}
                  </td>
                ))}
              </tr>
            ))}
          </tbody>
          <tfoot>
            {table.getFooterGroups().map((footerGroup) => (
              <tr key={footerGroup.id}>
                {footerGroup.headers.map((header) => (
                  <th key={header.id}>
                    {header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.footer,
                          header.getContext()
                        )}
                  </th>
                ))}
              </tr>
            ))}
          </tfoot>
        </table>
      );
    }}
  />
PropTypeDescription
querystring (JeenyQueries)The name of one of the Jeeny API queries. Determines which endpoint to hit.
variables{variable: string: string | number | boolean} (ListQueryInputstypeof query)Any variables that might be required by the query entered into the query prop. Must be an empty object if no variables are required.
columns{id: string, columnDef: ColumnDef}[] ({ id: DeepKeys<QueryResultTypestypeof query>; columnDef: DisplayColumnDef<QueryResultTypes[typeof query, any>;}[])id must be a property of the record type that the query returns. It is the accessor that determines what value to display in the column. columnDef is a TanStack table property that provides column settings for rendering
renderTable({ table }: { table: Table\<JeenyRecord> }) => React.ReactElementRenders your table. Must follow the guidelines from TanStack. The Table object definition can be viewed here.
tanstackTableProps (optional)Omit<TableOptions\<JeenyRecord>, "columns" | "data" | "getCoreRowModel")Additional configuration options that will be passed to TanStack table. Must follow the shape from TanStack. The TableOptions definition can be viewed here.

Forms

A headless wrapper that helps you create type-checked forms that work with the Jeeny API. The wrapper is an extension of React Hook Form.

The JeenyForm component is a headless component. It lets you take care of design and user experience while providing a typesafe way to easily access the Jeeny API.

The JeenyForm component gives you easy access to every mutation in the Jeeny API. Through it's renderForm prop you receive the return values of a fully typed React Hook Form useForm hook. The typing comes from the value that you enter into the action prop. For example, a JeenyForm with action item.create will only let you register inputs for properties in the Item record.

The JeenyForm component has three main props. The first prop, action, indicates what GraphQL mutation you wish to interact with. Depending on that prop, whether it is createItem or pickLocation, the expectations of the defaultValues and renderForm props will change. In the code example below, those two props expect to see the Jeeny Type CreateItemInput.

If we were to change the action to "saveSupplier" then those two props would then expect to use the Jeeny Type SupplierInputUpdate.

  <JeenyForm
    action="item.createItem"
    defaultValues={{
      status: "active",
    }}
    reactHookFormProps={{
      mode: "onTouched",
    }}
    renderForm={({ formState: { errors }, register, submit }) => (
      <form onSubmit={submit} className="flex flex-col gap-2 w-96">
        <div className="flex flex-col">
          <label htmlFor="name">Name</label>
          <input
            type="text"
            {...register("name")}
            className="rounded border border-gray-300"
          />
          {errors.name && <span>Name is required</span>}
        </div>

        <div className="flex flex-col">
          <label htmlFor="description">Description</label>
          <textarea
            {...register("description")}
            className="rounded border border-gray-300"
          />
        </div>

        <div className="flex flex-col">
          <label htmlFor="classification">Classification</label>
          <input
            type="text"
            {...register("classification")}
            className="rounded border border-gray-300"
          />
        </div>

        <div className="flex flex-col">
          <label htmlFor="unitsOfMeasure.bom">
            Bill of materials unit of measure
          </label>
          <input
            type="text"
            className="rounded border border-gray-300"
            {...register("unitsOfMeasure.bom" as any)}
          />
        </div>
      </form>
    )}
  />
PropTypeDescription
actionstring (JeenyActions)The name of one of the Jeeny API mutations. Determines which endpoint to hit.
defaultValues{property: string: any} (a partial Jeeny record type)Values entered here will prepopulate the form. Useful for setting defaults as well as when conducting save mutations. You should prepopulate the form with the existing values of the record.
renderForm(form: JeenyFormRenderProps\<JeenyRecord> }) => React.ReactElementRenders your form and is an extension of UseFormReturn. Must follow the guidelines from React Hook Form when using the properties for rendering. The UseFormReturn object definition can be viewed here.
reactHookFormProps (optional)Omit<UseFormProps, "defaultValues">Additional configuration options that will be passed to React Hook Form. Must follow the shape from React Hook Form. The definition can be viewed here.

Actions

A headless wrapper for type-checked Jeeny mutations. Render any element and receive a submit handler to execute the desired mutation.

The JeenyAction component is a headless component. It lets you take care of design and user experience while providing a typesafe way to easily access the Jeeny Layer.

The JeenyAction component gives you easy access to every mutation in the Jeeny Layer. Through it's childRender prop you receive a fully typed submit function that you can use however you want, whether it's an onClick event or added to any other logic you require. The submit function will let you know if it's input does not match what the API expects.

The JeenyAction component takes two props. The first prop, action, indicates what GraphQL mutation you wish to interact with. Depending on that prop, whether it is createItem or pickLocation, the expectations of the second prop will change. In the source code below you can see we that what we want to render is a button. The JeenyAction component provides no styling and has no HTML elements. It's purpose is to pass a typed submit function to your UI and handle any interactions with the API.

The submit function that we passed to our button's onClick in our example will only accept the object with a shape that matches the Jeeny TypeItemInput. If we were to change the action to saveSupplier then the submit function would then expect an object with a shape that matches the Jeeny Type SupplierInputUpdate.

<JeenyAction
  action="item.createItem"
  renderChild={({ submit }) => (
    <button
      onClick={() => {
        submit({
          name: "Raspberry Punch Kombucha Extreme",
          partNumber: "RPKE",
          status: "active",
        });
      }}
    >
      My creator made me a button but I can be anything I want to be!
    </button>
  )}
/>
PropTypeDescription
actionstring (JeenyActions)The action will be one of the Jeeny Layer mutations.
renderChild(props: { submit: (values: ActionInputsaction) => Promise) => React.ReactElement;This prop expects a function that returns a valid JSX element. It provides a submit render prop that can be used by the component returned by the function. The submit function will expect the appropriate object type associated with the action prop. This ensures that you pass accurate data to the Jeeny API.

Hooks

The hooks in this package provide an easy way to get direct access to the API. The API hooks can be considered a wrapper around the Apollo Client hooks. The hooks return functions you can use to retrieve or mutate data, the loading state of the actions, and the response data. Like the other utilities in this package, they are fully typed.

Each query function is actually a wrapper around the Apollo Client useLazyQuery hook and the mutations are a wrapper around the useMutation hook. This means that the full APIs for both of those hooks are provided on each and every Jeeny hook. You can find Apollo's documentation on useLazyQuery here and their documentation on useMutation here. This will let you customize options such as fetch policy, caching, error handling, and more.

The hooks can be thought of as a self-documenting API package.

const {
  getItem: {
    query: getItem,
    data,
    loading
  }
} = useItem({
  getItem: {
    options: {
      onCompleted: (data) => dropTheBalloons();
    }
  }
})

useEffect(() => {
  getItem({variables: { id }})
}, [getItem, id])

if (isLoading) {
  return <Loader />
}

const item = data.getItem;

return <div>
  {item.name}
</div>

The following hooks are available for use. useApi is also available to access all of the below hooks at once.

HookRecord associations
useAppApiApp
useArrivalApiArrival, ArrivalDetails, ArrivalRelease, ArrivalDelivery, ArrivalLineItem, ArrivalReleaseLineItem, ArrivalDeliveryLineItem
useBidApiBidRequest, Bid, BidRequestLineItem, BidLineItem
useCompanyApiCompany
useCompanyUserApiCompanyUser
useDepartureApiDeparture, DeparturePickList, DeparturePick, DepartureLineItem, DeparturePickListLineItem, DeparturePickLineItem
useDeviceApiDevice
useDynamicContainerApiDynamicContainer
useEventApiEvent
useFacilityApiFacility, FacilityDetails
useFacilityItemApiFacilityItem
useInstructionApiInstructionTemplate, InstructionExecution, InstructionSubject
useInventoryAreaApiStorageInventoryArea
useInventoryRecordApiInventoryRecord, InventoryLog
useItemStorageInventoryAreaLocationApiItemStorageInventoryAreaLocation
useItemStorageInventoryAreaRuleApiItemStorageInventoryAreaRule
useItemApiItem, ItemDetails
useItemGroupApiItemGroup
useKioskApiKiosk
useKitApiKitTemplate, KitTemplatePart, KitTemplatePartOption, KitTemplateTree, KitTemplateBomEntry
useOperatorApiOperator, SafeOperator
useProductApiProduct
useStorageInventoryApiStorageInventory
useStorageInventoryAreaLocationApiStorageInventoryAreaLocation, StorageInventoryAreaLocationPayload
useStorageInventoryAreaRuleApiStorageInventoryAreaRule
useSupplierApiSupplier
useSupplierItemApiSupplierItem
useTeamApiTeam

Formatters

Formatters provide an easy way to get the main identifier of a record type. By passing a record's ID to the correct formatter you will receive it's name property (or the property most closely associated with name. e.g. the ArrivalFormatter will return the ArrivalNumber).

return <div>
  <span className="font-bold"><SupplierFormatter id="foo"></span>
</div>

The following formatters are available for use.

ComponentReturned property associations
ArrivalFormatterarrivalNumber
CompanyUserFormatter${firstName} ${lastName}
DepartureFormatterexternalOrderId
DeviceFormattername
EmployeeFormatter${firstName} ${lastName}
EventFormattername
FacilityFormattername
InstructionSubjectFormatterReturns the corresponding formatter for the subject type (e.g. returns <SupplierFormatter /> if the subjectType is supplier)
InstructionTemplateFormattername
InventoryAreaFormattername
ItemFormattername
OperatorFormatter${firstName} ${lastName}
ProductFormattername
SupplierFormattername
SupplierItemFormattersupplier.name and/or item.partNumber and/or item.name
TeamFormattername

JavaScript SDK

If you're not working with React you might be looking for our JavaScript/TypeScript SDK. Check it out here.

Author

šŸ‘¤ Jeeny

šŸ¤ Contributing

Contributions, issues and feature requests are welcome!Feel free to check issues page.

Show your support

Give a ā­ļø if this project helped you!

šŸ“ License

Copyright Ā© 2023 Jeeny. This project is MIT licensed.


This README was generated with ā¤ļø by readme-md-generator

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago