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 🙏

© 2024 – Pkg Stats / Ryan Hefner

tunakit

v1.0.3

Published

<img src="./TunaKIt-transparent.png" width="500">

Downloads

373

Readme

TunaKit - UI Component Library for CF

TunaKit is a collection of reusable components that can be used to build web applications.

Installation

npm install tunakit

Peer Dependencies

The following dependencies are required to be installed by the importing application:

  • "@ant-design/icons": "^4.8.0"
  • "antd": "^5.14.0"
  • "axios": "^1.2.1"
  • "react": "^18.2.0"
  • "react-dom": "^18.2.0"
  • "react-router-dom": "^6.4.3"
  • "swr": "^2.0.0"

Components

NotAvailable

Usage

import { NotAvailable } from 'tunakit';

const MyComponent = () => {
    return (
        <NotAvailable />
    );
}

Image

NotAvailable

HighlightedTag

Props

  • status (Required): The text to look for in status map.
  • statusMap (Required): An object that maps the status to the corresponding label and color.

Usage

import { HighlightedTag } from 'tunakit';

const MyComponent = () => {
    const statusMap: TagColumnData = {
        APPROVED: {
            value: 'Approved',
            label: 'APPROVED',
            color: 'green',
        },
        PENDING: {
            value: 'Pending',
            label: 'PENDING',
            color: 'orange',
        },
    }

    return (
        <HighlightedTag status="APPROVED" statusMap={statusMap} />
    );
}

Image

HighlightedTag

PageHeader

Props

  • title (Required: string): The title of the page.
  • onBack (Optional: () => void): A function called when the back button is clicked.
  • rightContent (Optional: ReactNode): The content to be displayed on the right side of the header.

Usage

import { PageHeader } from 'tunakit';

const MyComponent = () => {
    return (
        <PageHeader title="Factories" onBack={() => navigate(-1)}
                    rightContent={<Button type={'primary'}>Add Factory</Button>} />
    );
}

Image

PageHeader

FacilitySelect

Props

  • includeAll (Required: boolean): Allows "All Facilities" option should in the dropdown.
  • value (Optional: string): The value of the selected facility.
  • onChange (Optional: (value: string) => void): A function called when the selected facility changes.
  • initialSearch (Optional: string): The initial search value for the facility search.
  • loading (Optional: boolean): A flag to indicate if the facility data is loading.
  • userFacilities (Optional: UserFacility[]): The list of facilities.

Usage

import { FacilitySelect } from "tunakit";

const MyComponent = () => {
    return (
        <FacilitySelect
            includeAll={false}
            value={"Facility 1"}
            onChange={(value) => console.log(value)}
            initialSearch={"Facility 1"}
            loading={false}
            userFacilities={[
                { facility: { id: "1", name: "Facility 1" } },
                { facility: { id: "2", name: "Facility 2" } }
            ]} />
    );
};

Image

FacilitySelect

FilterComponent

Props

  • enabled (Optional: Array<'piNumber' | 'createdAt' | 'createdBy' | 'id' | 'name' | 'status'>): The list of predefined filters to enable. Allowed values: (id, name, piNumber, createdAt, createdBy, status).
  • statusOptions (Optional: Array): List of status options to enable the status filter.
  • customFilters (Optional: Array): List of filters (cf-filter-component supported format) to enable which aren't provided by default.
  • setClearAllFiltersFn (Optional: Dispatch<SetStateAction>): setState method from the useState hook. Allows to clear all the selected filters.

Usage

import { FilterComponent } from 'tunakit';

const MyComponent = () => {
    return (
        <FilterComponent
            enabled={['name', 'piNumber']}
            statusOptions={[
                { label: 'Approved', value: 'APPROVED' },
                { label: 'Pending', value: 'PENDING' },
            ]}
            customFilters={[
                {
                    label: 'Custom Filter',
                    id: 'customFilter',
                    type: 'text',
                    placeholder: 'Enter custom filter',
                },
            ]}
        />
    );
}

Image

FilterComponent

ListViewPage

Props

  • title (Required: string): The title of the page.
  • showFacilityFilter (Required: boolean): Set to true to show the facility filter.
  • filters (Optional: FilterComponentProps): The FilterComponent props.
  • dataFetcher (Required: ApiFn): The function to fetch the data which returns a SF api response and accepts SF search api params.
  • order (Required: Array): The order in which the table columns appear. Values must be api response keys. Partially providing keys will ensure order for those keys and the rest will be appended in the order they appear in the api response.
  • action (Optional: ReactElement): React Component to be rendered in the action column of the table. The component will have data of the table row in the props record.
  • excludeColumns (Optional: Set): The list of columns not to be shown in the table.
  • includeColumns (Optional: Array): The list of columns to be shown in the table. Only the provided columns will be displayed in the table. For a user name column, set type to userInfo, for a date column, set type to date and for a tag type column, set type to tag along with a tagMap. To display the row value, just provide the api response key.
  • customColumns (Optional: ColumnsType): Antd table columns type, will be added as is to the table. Suitable for custom rendering logic.
  • header (Optional: ReactElement): React Component to be rendered in the header of the table.
  • emptyText (Optional: string): The text to be displayed when there is no data.
  • setRefetch (Optional: Dispatch<SetStateAction>): setState method from the useState hook. Allows to refetch the data.
  • fixedFilters (Optional: Array): The list of fixed filters to be applied to the search api.
  • fetchUserFacilities (Optional: 'context' | 'api'): The method to fetch the user facilities. 'context' fetches the facilities from the context and 'api' fetches the facilities from the api. Default is 'context'.
  • includeAllFacilities (Optional: boolean): Set to true to include all facilities in the facility filter. Default is true.
  • fetchSize (Optional: number): The number of records to fetch in a single api call. Default is 20.
  • onBack (Optional: () => void): A function called when the back button is clicked in page header. Back button is shown only if this prop is provided.
  • setTableData (Optional: Dispatch<SetStateAction>): setState method from the useState hook. Allows to set the current visible table data.

Usage

import { ListViewPage } from 'tunakit';
import { useState } from "react";

export const EditButton = ({ record }: any) => {
    return <Button type={"primary"} onClick={() => console.log({ record })}>Edit</Button>
}

const MyComponent = () => {
    const [refetch, setRefetch] = useState(null);
    const [data, setData] = useState(null);
    const navigate = useNavigate();

    return (
        <ListViewPage
            title={"Factory Material Trace"}
            showFacilityFilter={true}
            filters={{
                enabled: ['id', 'name'],
                statusOptions: [
                    { label: 'Approved', value: 'APPROVED' },
                    { label: 'Pending', value: 'PENDING' },
                ],
                customFilters: [
                    {
                        label: 'Custom Filter',
                        id: 'customFilter',
                        type: 'text',
                        placeholder: 'Enter custom filter',
                    },
                ],
            }}
            dataFetcher={getFactoryMaterialTraceTest}
            order={['created_by']}
            action={<EditButton />}
            excludeColumns={new Set(["id", "deleted_at", "deleted_by"])}
            includeColumns={[
                "updated_by",
                { key: "created_by", type: "userInfo", title: 'Created By User' },
                { key: "created_at", type: "date" },
                { key: "updated_at", type: "datetime" },
                { key: "facility_id", type: "facility" }, // either of showFacilityFilter or fetchUserFacilities must be provided
                { key: "location_code", type: "text", title: 'Zone Code' },
                {
                    key: "step_code",
                    type: "tag",
                    tagMap: {
                        INWARD: {
                            value: "INWARD",
                            label: "INWARD",
                            color: "blue",
                        },
                        GRADING: {
                            value: "GRADING",
                            label: "GRADING",
                            color: "green",
                        },
                        ALLOCATION: {
                            value: "ALLOCATION",
                            label: "ALLOCATION",
                            color: "geekblue",
                        },
                        PACKAGING: {
                            value: "PACKAGING",
                            label: "PACKAGING",
                            color: "gold",
                        },
                        BY_PRODUCT: {
                            value: "BY_PRODUCT",
                            label: "BY PRODUCT",
                            color: "purple",
                        },
                    },
                }]}
            customColumns={[
                {
                    title: 'Custom Column',
                    dataIndex: 'customColumn',
                    render: (_, record) => <span>{record.id}</span>,
                },
            ]}
            header={<Row justify={'end'}><Button type={'primary'}>Export Data</Button></Row>}
            emptyText={"No data found"}
            setRefetch={setRefetch}
            fixedFilters={[
                { field: 'status', operator: 'eq', value: 'APPROVED' },
            ]}
            fetchUserFacilities={'api'}
            includeAllFacilities={true}
            fetchSize={10}
            onBack={() => navigate(-1)}
            setTableData={setData}
        />
    );
}

Image

ListViewPage

Box

Props

  • children (Optional: React.ReactNode): React Element to be render inside the box.
  • h (Optional: number): height of the box
  • w (Optional: number) width of the box

Usage

import { Box } from "tunakit";

const MyComponent = () => {
    return <>
        <Box h={10} />
    </>;
};

Approval Button Group

Props

  • onApprove (Optional: () => void): A function called when the approve button is clicked.
  • onReject (Optional: () => void): A function called when the reject button is clicked.
  • justify (Optional: 'start' | 'end' | 'center' | 'space-around' | 'space-between' | 'space-evenly'): The justify property of the button group. Default is 'end'.

Usage

import { ApprovalButtonGroup } from 'tunakit';

const MyComponent = () => {
    return (
        <ApprovalButtonGroup justify={'end'} onApprove={() => console.log('Approved')}
                             onReject={() => console.log('Rejected')} />
    );
}

Image

ApprovalButtonGroup.png

DataTable

A subset of ListViewPage component where the table rendering is extracted out.

Props

dataFetcher: ApiFn;
order: Array<string>;
userFacilities?: Array<UserFacility>
action?: ReactElement;
excludeColumns?: Set<string>;
includeColumns?: Array<IncludeColumnType>;
customColumns?: ColumnsType;
emptyText: string;
setRefetch?: Dispatch<SetStateAction<any>>; // setState method of useState
fixedFilters?: Array<SearchApiSearchParamsFilter>;
fetchSize?: number;
  • dataFetcher (Required: ApiFn): The function to fetch the data which returns a SF api response and accepts SF search api params.
  • order (Required: Array): The order in which the table columns appear. Values must be api response keys. Partially providing keys will ensure order for those keys and the rest will be appended in the order they appear in the api response.
  • userFacilities (Optional: Array): Array of facilities user is mapped to. Used to render facility name in the DataTable.
  • action (Optional: ReactElement): React Component to be rendered in the action column of the table. The component will have data of the table row in the props record.
  • excludeColumns (Optional: Set): The list of columns not to be shown in the table.
  • includeColumns (Optional: Array): The list of columns to be shown in the table. Only the provided columns will be displayed in the table. For a user name column, set type to userInfo, for a date column, set type to date and for a tag type column, set type to tag along with a tagMap. To display the row value, just provide the api response key.
  • customColumns (Optional: ColumnsType): Antd table columns type, will be added as is to the table. Suitable for custom rendering logic.
  • emptyText (Optional: string): The text to be displayed when there is no data.
  • setRefetch (Optional: Dispatch<SetStateAction>): setState method from the useState hook. Allows to refetch the data.
  • fixedFilters (Optional: Array): The list of fixed filters to be applied to the search api.
  • fetchSize (Optional: number): The number of records to fetch in a single api call. Default is 20.

Usage

import { DataTable } from 'tunakit';
import { useState } from "react";

export const EditButton = ({ record }: any) => {
    return <Button type={"primary"} onClick={() => console.log({ record })}>Edit</Button>
}

const MyComponent = () => {
    const [refetch, setRefetch] = useState(null);
    const [data, setData] = useState(null);
    const navigate = useNavigate();

    return (
        <DataTable
            dataFetcher={getFactoryMaterialTraceTest}
            order={['created_by']}
            userFacilities={[]} // fetched from api or from context, depends on caller
            action={<EditButton />}
            excludeColumns={new Set(["id", "deleted_at", "deleted_by"])}
            includeColumns={[
                "updated_by",
                { key: "created_by", type: "userInfo", title: 'Created By User' },
                { key: "created_at", type: "date" },
                { key: "updated_at", type: "datetime" },
                { key: "facility_id", type: "facility" }, // either of showFacilityFilter or fetchUserFacilities must be provided
                { key: "location_code", type: "text", title: 'Zone Code' },
                {
                    key: "step_code",
                    type: "tag",
                    tagMap: {
                        INWARD: {
                            value: "INWARD",
                            label: "INWARD",
                            color: "blue",
                        },
                        GRADING: {
                            value: "GRADING",
                            label: "GRADING",
                            color: "green",
                        },
                        ALLOCATION: {
                            value: "ALLOCATION",
                            label: "ALLOCATION",
                            color: "geekblue",
                        },
                        PACKAGING: {
                            value: "PACKAGING",
                            label: "PACKAGING",
                            color: "gold",
                        },
                        BY_PRODUCT: {
                            value: "BY_PRODUCT",
                            label: "BY PRODUCT",
                            color: "purple",
                        },
                    },
                }]}
            customColumns={[
                {
                    title: 'Custom Column',
                    dataIndex: 'customColumn',
                    render: (_, record) => <span>{record.id}</span>,
                },
            ]}
            emptyText={"No data found"}
            setRefetch={setRefetch}
            fixedFilters={[
                { field: 'status', operator: 'eq', value: 'APPROVED' },
            ]}
            fetchSize={10}
        />
    );
}

Image

DataTable.png

DividerHeader

Props

  • title (Required: string): The title of the header.
  • titleStyle (Optional: React.CSSProperties): The css style of the title.
  • suffix (Optional: string): The suffix of the header.
  • suffixStyle (Optional: React.CSSProperties): The css style of the suffix.
  • rightContent (Optional: { element: React.ReactNode, leftContentSpan: number, rightContentSpan: number }): The content to be displayed on the right side of the header. element is the react element to render. Use left and right content span to adjust the look of the divider line.

Usage

import { DividerHeader } from "tunakit";

const MyComponent = () => {
    return <>
        <DividerHeader title={'Remarks'} suffix={' *'} suffixStyle={{ color: 'red' }} rightContent={{
            element: <Button type={'primary'}>Submit</Button>,
            leftContentSpan: 21,
            rightContentSpan: 3
        }} />
    </>;
};

Image

Without Right Content

DividerHeaderWoRc.png

With Right Content

DividerHeaderWRc.png

UploadDocumentModal

Props

header: string;
isOpen: boolean;
onCancel: () => void;
onComplete: (data: UploadFile<any>) => void;
onError: (data: UploadFile<any>) => void;
action: string;
beforeUpload?: (file: RcFile) => Promise<boolean>;
data?: string;
acceptedFileExtensions?: Array<string>;
maxFiles?: number;
  • header (Required: string): The title of the modal.
  • isOpen (Required: boolean): The flag to show/hide the modal.
  • onCancel (Required: () => void): Callback function called when the modal is closed.
  • onComplete (Required: (data: UploadFile) => void): Callback function called when the file upload is successful with the uploaded file data.
  • onError (Required: (data: UploadFile) => void): Callback function called when the file upload fails with the file upload error data.
  • action (Required: string): The POST api endpoint on which the file must be uploaded.
  • beforeUpload (Optional: (file: RcFile) => Promise): A function that is called before the file is uploaded. If the function returns false, the file upload is cancelled. Perform any neccessary validations here.
  • data (Optional: string): File upload template which will be downloaded as a file. Passing this prop enables the Download Template button.
  • acceptedFileExtensions (Optional: Array): The list of file extensions that are allowed to be uploaded.
  • maxFiles (Optional: number): The maximum number of files that can be uploaded.

Usage

import { UploadDocumentModal } from "tunakit";
import templateCsv from "./templateCsv.csv";

const MyComponent = () => {
    const [isOpen, setIsOpen] = useState(false);

    const onError = (data: UploadFile<any>) => {
        console.log('Error Uploading file', data);
    };

    const beforeUpload = async (file: RcFile) => {
        console.log('Validating file', file);
        return true;
    }

    const onComplete = (data: UploadFile<any>) => {
        console.log('File uploaded successfully', data);
    }

    return <>
        <UploadDocumentModal
            header={'Upload Warehouse Zones'}
            data={templateCsv}
            action={'https://api.service.com/upload'}
            isOpen={isOpen}
            onError={onError}
            onCancel={() => setIsOpen(false)}
            beforeUpload={beforeUpload}
            onComplete={onComplete} />
    </>;
};

Image

UploadDocumentModal.png

RadioButtonGroup

Radio Button Group is a HOC (Higher Order Component) which provides a skeleton base for implementing custom designed radio buttons. For example a radio button where each button is a designed card. The children radio buttons must implement currentValue and setCurrentValue prop which will have the same type as RadioButtonGroup value and setValue prop respectively.

Props

  • value (Optional: string | number): The value of the selected radio button.
  • columns (Optional: number): The number of radio buttons to be displayed in a single row. Default is 2.
  • setValue (Optional: (value: string | number) => void): Callback function called when the radio button is selection is changed.

Usage

const MyComponent = () => {
    const [value, setValue] = useState('option1');

    return (
        <RadioButtonGroup value={value} setValue={setValue} columns={3}>
            <RadioButton value={'option1'}>Option 1</RadioButton>
            <RadioButton value={'option2'}>Option 2</RadioButton>
            <RadioButton value={'option3'}>Option 3</RadioButton>
        </RadioButtonGroup>
    );
}

RadioCardButton

A radio button of card type to be used with RadioButtonGroup.

Props

  • icon (Required: ReactElement): The icon to be displayed in the radio button.
  • title (Required: string): The title of the radio button.
  • subtitle (Required: string): The subtitle of the radio button.
  • value (Required: string | number): The value of the radio button.
  • currentValue (Optional: string | number): The value of the selected radio button in the RadioButtonGroup. This value is passed by the RadioButtonGroup component.
  • setCurrentValue (Optional: (value: string | number) => void): Callback function called when this radio button is selected. This function is passed by the RadioButtonGroup component.
  • ribbon (Optional: { text: string, color?: string }): The ribbon to be displayed on the radio button.
  • disabled (Optional: boolean): Enables of disables the radio button.

Usage

import { useState } from "react";

const MyComponent = () => {
    const [value, setValue] = useState('upload');

    return <>
        <RadioButtonGroup value={value} setValue={setValue}>
            <RadioCardButton
                icon={<FileAddTwoTone />}
                title={"Upload"}
                subtitle={"When you have a csv file ready"}
                value={"upload"} />
            <RadioCardButton
                icon={<ScanOutlined style={{ color: "rgb(24, 144, 255)" }} />}
                title={"Scan"}
                disabled={true}
                subtitle={"Scan and let us do the rest"}
                ribbon={{ text: "Coming Soon", color: "green" }}
                value={"scan"} />
        </RadioButtonGroup>
    </>
}

Image

RadioButtonCard.png

Common Interfaces

BaseEntity

All the backend entity types definition in UI must extend this interface type. For the ListViewPage component, the dataFetcher function must return an array of BaseEntity type.

interface BaseEntity {
    id: number;
    created_by: string;
    updated_by: string;
    created_at: string;
    updated_at: string;
}

BaseResponseStatus

Status type for all backend api response status key in SF.

interface BaseResponseStatus {
    status_code: number;
    status_message: string;
    status_type: string;
    total_count: number;
}

BaseResponseUserInfo

User info type for all backend api response user_info key in SF.

interface BaseResponseUserInfo {
    [key: string]: {
        email?: string
        name: string
        phoneNumber?: string
    };
}

BaseResponse

Base response type for all backend api response in SF where T is the entity type.

interface BaseResponse<T> {
    status: BaseResponseStatus;
    data: Array<T>;
    user_info?: BaseResponseUserInfo;
}

SearchApiSearchParamsFilter

Type for building SF search api filters param string.

interface SearchApiSearchParamsFilter {
    field: string;
    operator: string;
    value?: string;
}

SearchApiSearchParams

Type with all acceptable parameters for SF search api.

interface SearchApiSearchParams {
    fetchSize?: number;
    page?: number;
    sortBy?: string;
    includes?: string;
    filters?: Array<SearchApiSearchParamsFilter>;
}

Contexts

UserFacilityContext

Context to store the user facilities. The context provider must be wrapped around the application to provide the user facilities to the components. Ideally the context value must be saved at the render of App component on app initialisation.

Usage

  1. Wrap the AppContainer inside App component (or whichever is the parent component returned from App) with UserFacilitiesProvider.
import { UserFacilitiesProvider } from 'tunakit';

const App = () => {
    return <UserFacilitiesProvider>
        <AppContainer {...props} />
    </UserFacilitiesProvider>;
};
  1. Set the value in context on app load (inside AppContainer)
const AppContainer = () => {
  const { setUserFacilities } = useUserFacilitiesContext();
  const { userFacilities, loading: facilityLoading, error: userFacilitiesError } = useUserFacilities();

  useEffect(() => {
    if (userFacilities.length) {
      setUserFacilities(userFacilities);
    }
  }, [JSON.stringify(userFacilities)]);

  if (facilityLoading) {
    return <Spin fullscreen spinning={facilityLoading} tip={'Loading App'} />
  }

  const ApplicationError = () => {
    return <Card><Result
            status="error"
            title="Error loading WMS."
            subTitle={'Please refresh or contact admin.'}
            extra={[
              <Button type="primary" key="console" onClick={() => window.location.reload()}>
                Refresh
              </Button>,
            ]}
    /></Card>
  }

  return <>
    // core app rendering logic here, render ApplicationError if userFacilitiesError truthy
  </>
}
  1. Getting UserFacilities value anywhere in the app.
    No need to handle loading, since the value access will be sync and in constant time. The value retrieved is reactive, if context changes, part of the page rerenders where userFacilities is accessed.
const MyComponent = () => {
  const { userFacilities } = useUserFacilitiesContext();
    return <>
        {userFacilities.map(facility => <div key={facility.facility.id}>{facility.facility.name}</div>)}
    </>
}