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

@ubkinfotech/student-erp

v0.2.26

Published

UBK ERP enterprise-grade modular Student ERP foundation for React Native

Readme

@ubkinfotech/student-erp

Enterprise-grade, modular Student ERP foundation for React Native. Designed for production apps that want reusable ERP screens and API logic without coupling to the host app’s navigation or Redux setup.

  • Framework: React Native (Metro)
  • HTTP: Axios (shared client via provider)
  • Data fetching: React Query
  • Authentication: external token injection (no Bearer by default)
  • Modules: Transport, TimeTable, Notes, Syllabus, Homework, Attendance, NoticeBoard, Subject, Teacher

Install

npm i @ubkinfotech/student-erp

Requirements

  • Your app must already include React Query:
npm i @tanstack/react-query
  • For some modules, the host app must install these libraries (they are not bundled):
    • Homework upload: react-native-image-picker
    • Homework / Notes / Syllabus / NoticeBoard attachments: react-native-pdf, react-native-blob-util, react-native-image-viewing, react-native-webview
    • NoticeBoard HTML message rendering: @native-html/render
    • Attendance calendar: react-native-calendars
npm i @native-html/render react-native-pdf react-native-blob-util react-native-image-viewing react-native-webview react-native-image-picker react-native-calendars

Peer dependency versions:

| Package | Version | | --- | --- | | @tanstack/react-query | >=5 | | @expo/vector-icons | >=14 | | @native-html/render | >=1.0.3 | | react | >=18 | | react-native | >=0.72 | | react-native-blob-util | >=0.19.11 | | react-native-calendars | >=1 | | react-native-image-picker | >=7 | | react-native-image-viewing | >=0.2.2 | | react-native-pdf | >=6.7.5 | | react-native-webview | >=11 |

Provider

All modules use a shared Axios client created by ERPProvider.

Basic

import React from 'react';
import {ERPProvider} from '@ubkinfotech/student-erp';

export function AppRoot() {
  return (
    <ERPProvider baseUrl={API_BASE_URL} authToken={authToken} fileBaseUrl={FILE_BASE_URL}>
      <App />
    </ERPProvider>
  );
}

Branch-aware file provider

For Homework, Notes, and Syllabus attachments uploaded through the public image server, pass a branch-aware file base URL and schoolCode.

import React from 'react';
import {ERPProvider} from '@ubkinfotech/student-erp';

export function AppRoot({authToken, branchId}: any) {
  const staticSchoolDownloadBase = `${IMAGE_BASE_URL}/uploads/school_${branchId}/`;

  return (
    <ERPProvider
      baseUrl={API_BASE_URL}
      authToken={authToken}
      fileBaseUrl={staticSchoolDownloadBase}
      schoolCode={branchId}>
      <App />
    </ERPProvider>
  );
}

Async token callback

import React from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {ERPProvider} from '@ubkinfotech/student-erp';

export function AppRoot() {
  return (
    <ERPProvider baseUrl={API_BASE_URL} getAuthToken={() => AsyncStorage.getItem('token')}>
      <App />
    </ERPProvider>
  );
}

Module Imports

import {
  ERPProvider,
  AttendanceByDayScreen,
  AttendanceMonthlyScreen,
  HomeworkScreen,
  LibraryIssuedBooksScreen,
  NoticeBoardScreen,
  NotesScreen,
  StudentRouteDetailsScreen,
  SubjectScreen,
  SyllabusScreen,
  TeacherScreen,
  TimeTableScreen,
} from '@ubkinfotech/student-erp';

Tested Integration (Reference)

The modules are verified inside the host app using a “switcher” screen:

Branch ID

The host app should pass the school branch id to the module for branch-aware file URLs.

  • Recommended source: login response fk_createdby
  • Common fallback chain used in the host app:
    • user?.fk_createdby || user?.branch_id || user?.branchID || user?.branchId

Host URL Helper

The host app can use one shared helper, like SampleNpm.jsx, to build module file URLs from a single branch-aware base:

const branchId = 82;
const staticSchoolDownloadBase = `${IMAGE_BASE_URL}/uploads/school_${branchId}/`;

const buildStaticAttachmentUrl = (fileName: string, modulePath: string) => {
  const normalized = String(fileName || '').replace(/\\/g, '/').replace(/^\/+/, '');

  if (!normalized) {
    return '';
  }

  if (/^https?:\/\//i.test(normalized)) {
    return normalized;
  }

  if (normalized.startsWith('school_')) {
    return `${IMAGE_BASE_URL}/uploads/${normalized}`;
  }

  if (normalized.startsWith('uploads/')) {
    return `${staticSchoolDownloadBase}${normalized.replace(/^uploads\//, '')}`;
  }

  if (normalized.startsWith(`${modulePath}/`)) {
    return `${staticSchoolDownloadBase}${normalized}`;
  }

  return `${staticSchoolDownloadBase}${modulePath}/${normalized}`;
};

Examples

Transport

import React from 'react';
import {ERPProvider, StudentRouteDetailsScreen} from '@ubkinfotech/student-erp';

export function TransportExample() {
  return (
    <ERPProvider baseUrl={API_BASE_URL} authToken={authToken} fileBaseUrl={FILE_BASE_URL}>
      <StudentRouteDetailsScreen />
    </ERPProvider>
  );
}

TimeTable

import React from 'react';
import {ERPProvider, TimeTableScreen} from '@ubkinfotech/student-erp';

export function TimeTableExample({classId, sectionId, sessionId}: any) {
  return (
    <ERPProvider baseUrl={API_BASE_URL} authToken={authToken}>
      <TimeTableScreen classId={classId} sectionId={sectionId} sessionId={sessionId} />
    </ERPProvider>
  );
}

Notes (View + Download)

Notes supports inline image/PDF viewing inside the module, but the host app can still provide callbacks for opening files and downloading files. For public uploaded files, pass branchId, schoolCode, and staticSchoolDownloadBase.

import React from 'react';
import {Alert, Platform, ToastAndroid} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import ImageView from 'react-native-image-viewing';
import RNFetchBlob from 'react-native-blob-util';
import {ERPProvider, NotesScreen} from '@ubkinfotech/student-erp';

export function NotesExample() {
  const navigation = useNavigation();
  const branchId = user?.fk_createdby || user?.branch_id || user?.branchID || user?.branchId || 82;
  const staticSchoolDownloadBase = `${IMAGE_BASE_URL}/uploads/school_${branchId}/`;

  const openFile = ({url, extension, name}: any) => {
    if (extension === 'pdf') {
      navigation.navigate('FeesReport', {filepath: url, name: name || 'File'});
      return;
    }
    if (['jpg', 'jpeg', 'png', 'webp'].includes(String(extension).toLowerCase())) {
      Alert.alert('Image', 'Open image preview in your screen using ImageView');
      return;
    }
    Alert.alert('Unsupported File', 'This file type is not supported for direct viewing.');
  };

  const downloadFile = async ({url, filename}: any) => {
    if (Platform.OS === 'android') {
      ToastAndroid.showWithGravityAndOffset('Downloading Started !', ToastAndroid.LONG, ToastAndroid.BOTTOM, 25, 50);
    }
    const docPath = RNFetchBlob.fs.dirs.DocumentDir;
    await RNFetchBlob.config({
      path: `${docPath}/${filename}`,
      fileCache: true,
      addAndroidDownloads: {
        useDownloadManager: true,
        notification: true,
        title: filename,
        description: filename,
        storeInDownloads: true,
      },
    }).fetch('GET', url);
  };

  return (
    <ERPProvider
      baseUrl={API_BASE_URL}
      authToken={authToken}
      fileBaseUrl={staticSchoolDownloadBase}
      schoolCode={branchId}>
      <NotesScreen
        branchId={branchId}
        onOpenFile={({url, extension, item}) => openFile({url, extension, name: item?.title})}
        onDownloadFile={({url, filename}) => downloadFile({url, filename})}
      />
    </ERPProvider>
  );
}

Syllabus

import React from 'react';
import {Alert} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import {ERPProvider, SyllabusScreen} from '@ubkinfotech/student-erp';

export function SyllabusExample({classId}: any) {
  const navigation = useNavigation();
  const branchId = user?.fk_createdby || user?.branch_id || user?.branchID || user?.branchId || 82;
  const staticSchoolDownloadBase = `${IMAGE_BASE_URL}/uploads/school_${branchId}/`;

  return (
    <ERPProvider
      baseUrl={API_BASE_URL}
      authToken={authToken}
      fileBaseUrl={staticSchoolDownloadBase}
      schoolCode={branchId}>
      <SyllabusScreen
        classId={classId}
        branchId={branchId}
        onOpenFile={({url, extension, item}) => {
          if (extension === 'pdf') {
            navigation.navigate('FeesReport', {filepath: url, name: item?.subject_title || 'Syllabus'});
            return;
          }
          if (['jpg', 'jpeg', 'png', 'webp'].includes(String(extension).toLowerCase())) {
            Alert.alert('Image', 'Open image preview in your screen using ImageView');
          }
        }}
      />
    </ERPProvider>
  );
}

Homework (List + Upload)

Homework supports upload internally. For branch-aware attachment URLs, pass branchId, schoolCode, and staticSchoolDownloadBase.

  • Images open in the built-in image viewer.
  • PDFs open inside the homework details modal (no second modal is stacked). This avoids an Android issue where an invisible native window could block all touches after closing the PDF. The Android back button closes the PDF first, then the details modal.
  • Other file types are passed to onOpenFile when provided.
import React from 'react';
import {Alert, Platform, ToastAndroid} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import RNFetchBlob from 'react-native-blob-util';
import {ERPProvider, HomeworkScreen} from '@ubkinfotech/student-erp';

export function HomeworkExample() {
  const navigation = useNavigation();
  const branchId = user?.fk_createdby || user?.branch_id || user?.branchID || user?.branchId || 82;
  const staticSchoolDownloadBase = `${IMAGE_BASE_URL}/uploads/school_${branchId}/`;

  const downloadFile = async ({url, filename}: any) => {
    if (Platform.OS === 'android') {
      ToastAndroid.showWithGravityAndOffset('Downloading Started !', ToastAndroid.LONG, ToastAndroid.BOTTOM, 25, 50);
    } else {
      Alert.alert('Download', 'Downloading started');
    }

    const docPath = RNFetchBlob.fs.dirs.DocumentDir;
    await RNFetchBlob.config({
      path: `${docPath}/${filename}`,
      fileCache: true,
      addAndroidDownloads: {
        useDownloadManager: true,
        notification: true,
        title: filename,
        description: filename,
        storeInDownloads: true,
      },
    }).fetch('GET', url);
  };

  return (
    <ERPProvider
      baseUrl={API_BASE_URL}
      authToken={authToken}
      fileBaseUrl={staticSchoolDownloadBase}
      schoolCode={branchId}>
      <HomeworkScreen
        branchId={branchId}
        onOpenFile={({url, extension, item}) => {
          if (extension === 'pdf') {
            navigation.navigate('FeesReport', {filepath: url, name: item?.title || item?.name || 'Homework'});
            return;
          }
          if (['jpg', 'jpeg', 'png', 'webp'].includes(String(extension).toLowerCase())) {
            Alert.alert('Image', 'Open image preview in your screen using ImageView');
          }
        }}
        onDownloadFile={({url, filename}) => downloadFile({url, filename})}
      />
    </ERPProvider>
  );
}

Attendance (Calendar + Monthly)

import React from 'react';
import {ERPProvider, AttendanceByDayScreen, AttendanceMonthlyScreen} from '@ubkinfotech/student-erp';

export function AttendanceExample({studentId, sessionId}: any) {
  return (
    <ERPProvider baseUrl={API_BASE_URL} authToken={authToken}>
      <AttendanceByDayScreen studentId={studentId} sessionId={sessionId} />
      <AttendanceMonthlyScreen studentId={studentId} sessionId={sessionId} />
    </ERPProvider>
  );
}

NoticeBoard (List + Details)

By default, details opens inside a modal. If you prefer navigation, use onOpenNotice.

The details view:

  • Renders the notice message as HTML (via @native-html/render).
  • Lists every attachment from the comma-separated file field, showing a file-type badge (PDF, Image, Word, Excel, File).
  • Opens PDFs in a built-in PDF viewer and images in a built-in image viewer. Word, Excel and other files are handed to the device using Linking.openURL.

Attachment URLs are built as https://image.ubkerp.com/uploads/school_<branchId>/notice/<file>. The branch is taken from the branchId prop, then from schoolCode on ERPProvider, then from a school_<id> folder at the end of fileBaseUrl. If no branch can be found, a warning is logged and the URL has no school_<id>/ folder.

Props (NoticeBoardScreen, passed through to NoticeDetailsScreen):

| Prop | Type | Description | | --- | --- | --- | | branchId | string \| number | School branch id used for attachment URLs. | | filePathPrefix | string | Upload folder for notices. Default: notice. | | resolveFileUrl | ({fileName, notice, branchId, filePathPrefix, fileBaseUrl}) => string \| undefined | Overrides how attachment URLs are built. | | onOpenFile | ({url, extension, fileName, notice}) => void | Overrides the built-in viewers. When set, every attachment tap calls this instead. | | onOpenNotice | ({notice}) => void | Opens the notice your own way instead of using the built-in modal. |

import React from 'react';
import {ERPProvider, NoticeBoardScreen} from '@ubkinfotech/student-erp';

export function NoticeExample() {
  const branchId = user?.fk_createdby || user?.branch_id || user?.branchID || user?.branchId || 82;

  return (
    <ERPProvider baseUrl={API_BASE_URL} authToken={authToken} fileBaseUrl={FILE_BASE_URL}>
      <NoticeBoardScreen branchId={branchId} />
    </ERPProvider>
  );
}

Custom file handling:

<NoticeBoardScreen
  branchId={branchId}
  onOpenFile={({url, extension, notice}) => {
    if (extension === 'pdf') {
      navigation.navigate('FeesReport', {filepath: url, name: notice?.title || 'Notice'});
      return;
    }
    Linking.openURL(url);
  }}
/>

Subject

import React from 'react';
import {ERPProvider, SubjectScreen} from '@ubkinfotech/student-erp';

export function SubjectExample() {
  return (
    <ERPProvider baseUrl={API_BASE_URL} authToken={authToken}>
      <SubjectScreen />
    </ERPProvider>
  );
}

Teacher

Teacher requires sessionId and uses fileBaseUrl for teacher images.

import React from 'react';
import {ERPProvider, TeacherScreen} from '@ubkinfotech/student-erp';

export function TeacherExample({sessionId}: any) {
  return (
    <ERPProvider baseUrl={API_BASE_URL} authToken={authToken} fileBaseUrl={FILE_BASE_URL}>
      <TeacherScreen sessionId={sessionId} />
    </ERPProvider>
  );
}

List Card Initials

The Notes, NoticeBoard and Subject list cards show the first letter of the item's title or name in the icon box. If the title or name is empty, they fall back to N, N and S.

Notes on Authentication

The default header is:

  • Authorization: <token>

If your backend uses a different header shape, configure the provider using custom headers via headers in ERPProvider.