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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@123usmanhaider321/ui

v1.0.6

Published

Ticketly's shared UI component library — a Vite-built React package designed to be consumed inside **Next.js** applications. Ships the full `AddEventStepper` flow (multi-step event creation/editing), Redux RTK Query APIs, utilities, and a provider for inj

Readme

@123usmanhaider321/ui

Ticketly's shared UI component library — a Vite-built React package designed to be consumed inside Next.js applications. Ships the full AddEventStepper flow (multi-step event creation/editing), Redux RTK Query APIs, utilities, and a provider for injecting auth services and environment config.


Table of Contents


Installation

From npm:

npm install @123usmanhaider321/ui

Local file link (monorepo / sibling directory):

"dependencies": {
  "@123usmanhaider321/ui": "file:../ticketly-shared-ui"
}

Peer Dependencies

These must be installed in the consuming app. They are not bundled.

npm install react react-dom @reduxjs/toolkit react-redux redux-persist framer-motion next

| Package | Required version | |---|---| | react | ^18.0.0 | | react-dom | ^18.0.0 | | @reduxjs/toolkit | ^2.0.0 | | react-redux | ^9.0.0 | | redux-persist | ^6.0.0 | | framer-motion | ^11.0.0 | | next | ^14.0.0 |


Next.js Integration

1. transpilePackages

The package is published as ESM. Add it to transpilePackages so Next.js compiles it instead of loading it raw, and alias shared singletons to prevent duplicate-React/duplicate-framer-motion errors:

// next.config.js
const path = require('path');

/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['@123usmanhaider321/ui'],
  webpack: (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      react: path.resolve('node_modules/react'),
      'react-dom': path.resolve('node_modules/react-dom'),
      'framer-motion': path.resolve('node_modules/framer-motion'),
      next: path.resolve('node_modules/next'),
    };
    return config;
  },
};

module.exports = nextConfig;

If you see TypeError: Cannot read properties of null (reading 'useContext') it means there are two copies of React. The alias above forces both the app and the library to share the same one.

2. Import styles

For Tailwind v3 consumers: Import the pre-built stylesheet once at the root of your app:

// app/layout.tsx  (App Router)
import '@123usmanhaider321/ui/dist/styles.css';

For Tailwind v4 consumers: Import the pre-built stylesheet inside a cascade layer within your global CSS file (or a scoped CSS file). This prevents the unlayered Tailwind 3 package CSS from overriding your Tailwind 4 utility classes:

/* app/globals.css */
@import "tailwindcss";
@import "@123usmanhaider321/ui/dist/styles.css" layer(components);

The CSS file contains all Tailwind utility classes used by the library. It is independent of the consuming app's Tailwind build.

3. Tailwind config

If you want the library's Tailwind classes generated by your app's build (instead of the pre-built CSS), add the compiled dist to your content array:

// tailwind.config.ts
export default {
  content: [
    './app/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    './node_modules/@123usmanhaider321/ui/dist/**/*.js',
  ],
};

4. Redux store

The library exposes two RTK Query APIs that must be registered in your store. It also reads from a state.event key — this must exist to avoid a runtime crash in EventSubmissionConfirmationModal.

// store/index.ts
'use client';

import { configureStore, createSlice } from '@reduxjs/toolkit';
import { singleEventApi, eventApi } from '@123usmanhaider321/ui';

// Required — EventSubmissionConfirmationModal selects these flags from state.event.
// Replace with your own event slice if you have one.
const eventSlice = createSlice({
  name: 'event',
  initialState: {
    addLoading: false,
    addError: null as string | null,
    addSuccess: false,
    updateLoading: false,
    updateError: null as string | null,
    updateSuccess: false,
  },
  reducers: {},
});

export const store = configureStore({
  reducer: {
    // ...your own reducers
    [singleEventApi.reducerPath]: singleEventApi.reducer,
    [eventApi.reducerPath]: eventApi.reducer,
    event: eventSlice.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({ serializableCheck: false })
      .concat(singleEventApi.middleware)
      .concat(eventApi.middleware),
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

Wrap your app with a "use client" Provider component:

// app/providers.tsx
'use client';
import { Provider } from 'react-redux';
import { store } from '@/store';

export function StoreProvider({ children }: { children: React.ReactNode }) {
  return <Provider store={store}>{children}</Provider>;
}

5. Provider setup

TicketlyUIProvider injects auth services and environment config into the library. It must sit inside a "use client" component, below the Redux <Provider>.

// app/providers.tsx
'use client';

import { Provider } from 'react-redux';
import { TicketlyUIProvider } from '@123usmanhaider321/ui';
import { store } from '@/store';
import { auth } from '@/lib/firebase'; // your app's Firebase Auth instance
import { onAuthStateChanged } from 'firebase/auth';

export function Providers({ children }: { children: React.ReactNode }) {
  const services = {
    getAuthToken: () =>
      auth.currentUser?.getIdToken() ?? Promise.resolve(null),
    onAuthStateChanged: (cb: (user: any) => void) => {
      return onAuthStateChanged(auth, cb);
    },
    getUserId: () => auth.currentUser?.uid ?? null,
    isAuthenticated: () => !!auth.currentUser,
  };

  return (
    <Provider store={store}>
      <TicketlyUIProvider
        services={services}
        config={{
          BASE_URL: process.env.NEXT_PUBLIC_API_URL!,
          FIREBASE_API_KEY: process.env.NEXT_PUBLIC_FIREBASE_API_KEY!,
          FIREBASE_AUTH_DOMAIN: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN!,
          FIREBASE_PROJECT_ID: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,
          FIREBASE_STORAGE_BUCKET: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET!,
          FIREBASE_MESSAGING_SENDER_ID: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID!,
          FIREBASE_APP_ID: process.env.NEXT_PUBLIC_FIREBASE_APP_ID!,
          GOOGLE_MAPS_API_KEY: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
          PAYFAST_MERCHANT_ID: process.env.NEXT_PUBLIC_PAYFAST_MERCHANT_ID!,
          PAYFAST_CHECKOUT_URL: process.env.NEXT_PUBLIC_PAYFAST_CHECKOUT_URL!,
          PAYFAST_API_URL: process.env.NEXT_PUBLIC_PAYFAST_API_URL!,
          HEADER_KEY: process.env.NEXT_PUBLIC_HEADER_KEY!,
          BODY_KEY: process.env.NEXT_PUBLIC_BODY_KEY!,
          VENDOR_API_KEY: process.env.NEXT_PUBLIC_VENDOR_API_KEY!,
          VENDOR_API_SECRET: process.env.NEXT_PUBLIC_VENDOR_API_SECRET!,
        }}
      >
        {children}
      </TicketlyUIProvider>
    </Provider>
  );
}

Then in your root layout:

// app/layout.tsx
import '@123usmanhaider321/ui/dist/styles.css';
import { Providers } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Sticky layout warning: Do not put overflow: hidden or overflow-x: hidden on any element that wraps these components. It breaks position: sticky used by the file upload sidebar inside the stepper. Use overflow: clip instead if you need to contain horizontal bleed.


Components

AddEventStepper

The primary export. A fully self-contained multi-step form for creating and editing events. All steps, validation, API calls, and navigation guards are internal.

// app/events/create/page.tsx
'use client';

import { AddEventStepper } from '@123usmanhaider321/ui';

export default function CreateEventPage() {
  return (
    <AddEventStepper
      organizer_id="org-123"
      onEventCreated={(eventId) => {
        console.log('Event created:', eventId);
      }}
    />
  );
}

Edit mode:

<AddEventStepper
  edit
  id="existing-event-id"
  organizer_id="org-123"
  onEventCreated={(id) => router.push(`/events/${id}`)}
/>

Restricted modes — open on a specific step group only:

// Jump straight to the questions editor
<AddEventStepper organizer_id="org-123" id="event-id" mode="questions" />

// Jump straight to the social profiles step
<AddEventStepper organizer_id="org-123" id="event-id" mode="social" />

Persisting in-progress state across page reloads:

<AddEventStepper
  organizer_id="org-123"
  SCOPED_ID_KEY="create_event_id"
  SCOPED_FORM_KEY="create_event_form"
  onEventCreated={(id) => router.push(`/events/${id}`)}
/>

Props

| Prop | Type | Default | Description | |---|---|---|---| | organizer_id | string | required | The organizer's ID — attached to all event API payloads. | | edit | boolean | false | Edit mode — loads the event specified by id. | | id | string | — | Event ID to fetch in edit mode, or to resume an in-progress creation. | | onEventCreated | (id: string) => void | — | Callback after a successful create or update. | | isClearing | boolean | false | Renders a global loading overlay while the parent is resetting state. | | SCOPED_ID_KEY | string | "" | localStorage key for persisting the in-progress event ID between page reloads. | | SCOPED_FORM_KEY | string | "" | localStorage key for persisting form state between page reloads. | | isSameQuestionTitleAllowed | boolean | — | Allow duplicate question titles in the custom form builder. | | closed_loop_id | string | — | Attaches a closed-loop ID to the event payload. | | initialStep | number | — | Force the stepper to open at a specific step number on mount. | | mode | "questions" \| "social" \| null | null | Restrict the stepper to only the questions steps or only the social profiles step. | | test | boolean | false | Dev only. Enables free click-navigation between all steps, bypassing validation and event ID guards. |


Configuration Reference

All keys are passed to TicketlyUIProvider via the config prop and stored in a singleton. Defaults are empty strings except BASE_URL.

| Key | Description | |---|---| | BASE_URL | Base URL for all API calls. Default: http://localhost:8080 | | FIREBASE_API_KEY | Firebase project API key | | FIREBASE_AUTH_DOMAIN | Firebase auth domain | | FIREBASE_PROJECT_ID | Firebase project ID | | FIREBASE_STORAGE_BUCKET | Firebase Storage bucket | | FIREBASE_MESSAGING_SENDER_ID | Firebase Cloud Messaging sender ID | | FIREBASE_APP_ID | Firebase app ID | | FIREBASE_MEASUREMENT_ID | Firebase Analytics measurement ID | | GOOGLE_MAPS_API_KEY | Google Maps API key (venue autocomplete) | | PAYFAST_MERCHANT_ID | PayFast merchant ID | | PAYFAST_CHECKOUT_URL | PayFast checkout callback URL | | PAYFAST_API_URL | PayFast API endpoint | | PAYMO_PG_PAYFAST_CHECKOUT_URL | Paymo PG PayFast callback URL | | HEADER_KEY | Request encryption header key | | BODY_KEY | Request encryption body key | | TICKETING_PORTAL_HEADER_KEY | Ticketing portal encryption header key | | TICKETING_PORTAL_BODY_KEY | Ticketing portal encryption body key | | VENDOR_API_KEY | Vendor API key | | VENDOR_API_SECRET | Vendor API secret | | INSTAGRAM_URL | Instagram URL used in social profile links |


Services Interface

interface TicketlyUIServices {
  // Returns a valid Firebase ID token for authenticated API requests
  getAuthToken: () => Promise<string | null>;

  // Subscribe to auth state changes; returns an unsubscribe function
  onAuthStateChanged: (callback: (user: any) => void) => () => void;

  // Returns the current user's UID, or null if unauthenticated
  getUserId: () => string | null;

  // Returns whether the current user is authenticated
  isAuthenticated: () => boolean;
}

Exported Utilities

import {
  cn,                       // clsx + tailwind-merge
  formatDateForApi,         // formats a Date to the API-expected string
  formatDate,               // human-readable date string
  formatDateTime,           // human-readable date + time string
  isSameDay,                // compares two dates by calendar day
  isDefaultDateRange,       // checks if a range is still the default placeholder
  toDate,                   // parses multiple input types to a Date
  extractDateAndTime,       // splits a datetime string → { date, time }
  convertDateFormat,        // converts between date format conventions
  getDateRange,             // builds a { start, end } range object
  getCurrentTierAndMember,  // resolves the active ticket tier and member info
  encrypt,                  // AES-encrypt a string using HEADER_KEY / BODY_KEY
  decrypt,                  // AES-decrypt a string
  fetchWrapper,             // typed fetch wrapper used internally by all API calls
} from '@123usmanhaider321/ui';

Redux API Hooks

Re-exported from the library for use alongside the stepper in your own components:

import {
  useAddEventMutation,
  useUpdateEventMutation,
  useGetEventDetailsByIdQuery,
  useUpdateEventSocialProfilesMutation,
  useSubmitEventForApprovalMutation,
} from '@123usmanhaider321/ui';

Development — Playground

A standalone Vite playground lets you develop and test components without a Next.js app.

npm run playground
# → http://127.0.0.1:5173

The playground mocks next/navigation, next/image, next/link, and next/dynamic via Vite aliases in playground/vite.config.ts — no Next.js install required.

Edit playground/App.tsx to swap in different components and configs.

npm run build       # build the library → dist/ (ESM + type declarations)
npm run build:css   # compile Tailwind → dist/styles.css