npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@jeeny/jeeny-react

v1.0.6

Published

React components and hooks for the Jeeny API

Readme

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>
      );
    }}
  />

| Prop | Type | Description | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | query | string (JeenyQueries) | The name of one of the Jeeny API queries. Determines which endpoint to hit. | | variables | {[variable: string]: string | number | boolean} (ListQueryInputs[typeof 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<QueryResultTypes[typeof 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.ReactElement | Renders 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>
    )}
  />

| Prop | Type | Description | | ----------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | action | string (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.ReactElement | Renders 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>
  )}
/>

| Prop | Type | Description | | ----------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | action | string (JeenyActions) | The action will be one of the Jeeny Layer mutations. | | renderChild | (props: { submit: (values: ActionInputs[action]) => 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.

| Hook | Record associations | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | useAppApi | App | | useArrivalApi | Arrival, ArrivalDetails, ArrivalRelease, ArrivalDelivery, ArrivalLineItem, ArrivalReleaseLineItem, ArrivalDeliveryLineItem | | useBidApi | BidRequest, Bid, BidRequestLineItem, BidLineItem | | useCompanyApi | Company | | useCompanyUserApi | CompanyUser | | useDepartureApi | Departure, DeparturePickList, DeparturePick, DepartureLineItem, DeparturePickListLineItem, DeparturePickLineItem | | useDeviceApi | Device | | useDynamicContainerApi | DynamicContainer | | useEventApi | Event | | useFacilityApi | Facility, FacilityDetails | | useFacilityItemApi | FacilityItem | | useInstructionApi | InstructionTemplate, InstructionExecution, InstructionSubject | | useInventoryAreaApi | StorageInventoryArea | | useInventoryRecordApi | InventoryRecord, InventoryLog | | useItemStorageInventoryAreaLocationApi | ItemStorageInventoryAreaLocation | | useItemStorageInventoryAreaRuleApi | ItemStorageInventoryAreaRule | | useItemApi | Item, ItemDetails | | useItemGroupApi | ItemGroup | | useKioskApi | Kiosk | | useKitApi | KitTemplate, KitTemplatePart, KitTemplatePartOption, KitTemplateTree, KitTemplateBomEntry | | useOperatorApi | Operator, SafeOperator | | useProductApi | Product | | useStorageInventoryApi | StorageInventory | | useStorageInventoryAreaLocationApi | StorageInventoryAreaLocation, StorageInventoryAreaLocationPayload | | useStorageInventoryAreaRuleApi | StorageInventoryAreaRule | | useSupplierApi | Supplier | | useSupplierItemApi | SupplierItem | | useTeamApi | Team |

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.

| Component | Returned property associations | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | ArrivalFormatter | arrivalNumber | | CompanyUserFormatter | ${firstName} ${lastName} | | DepartureFormatter | externalOrderId | | DeviceFormatter | name | | EmployeeFormatter | ${firstName} ${lastName} | | EventFormatter | name | | FacilityFormatter | name | | InstructionSubjectFormatter | Returns the corresponding formatter for the subject type (e.g. returns <SupplierFormatter /> if the subjectType is supplier) | | InstructionTemplateFormatter | name | | InventoryAreaFormatter | name | | ItemFormatter | name | | OperatorFormatter | ${firstName} ${lastName} | | ProductFormatter | name | | SupplierFormatter | name | | SupplierItemFormatter | supplier.name and/or item.partNumber and/or item.name | | TeamFormatter | name |

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