@ubkinfotech/student-erp
v0.2.26
Published
UBK ERP enterprise-grade modular Student ERP foundation for React Native
Maintainers
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-erpRequirements
- 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-calendarsPeer 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
onOpenFilewhen 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
messageas HTML (via@native-html/render). - Lists every attachment from the comma-separated
filefield, 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.
