0%
balqalam Logo
balqalam
FeaturesCommunityPricingDocumentation
Login
  • Introduction
  • Help center
  • Common questions
  • Get Started
  • Localhost
  • Architecture
  • Structure
  • Pattern
  • Page
  • Layout
  • Content
  • Types
  • Config
  • Actions
  • Queries
  • Authorization
  • Validation
  • Form
  • Table
  • Detail
  • Card
  • Util
  • Hooks
  • List Params
  • Views
  • README.md
  • ISSUE.md
  • Technology Stack
  • Database
  • File
  • CDN Assets
  • Entry Points
  • Dashboard
  • Authentication
  • Credentials
  • OAuth
  • Flow Diagrams
  • Multi-Tenancy
  • Offline
  • Onboarding
  • Onboarding Videos
  • Add Values
  • Admission
  • Application
  • Attendance
  • Compliance
  • Profile
  • Exams
  • Exam Wizard
  • Timetable
  • Classrooms
  • Notifications
  • Conference
  • LMS (Lumos)
  • Finance
  • Fee Management
  • Invoice
  • Wallet
  • Salary
  • Payroll
  • Timesheet
  • Expenses
  • Budget
  • Receipt
  • Accounts
  • Banking
  • Reports
  • Dashboard
  • Permissions
  • Messages
  • Integration Flow
  • Provision
  • AI Document Processing
  • Document Intelligence
  • Internationalization
  • Translation
  • Translation Guide
  • Icons
  • Docs Factory
  • Inspiration
  • Listings
  • Teachers
  • Students
  • Catalog
  • Library
  • Contributing
  • Code of conduct
  • GitHub Workflow
  • Database Seeds
  • Database Safety
  • Test Accounts
  • Bulk import
  • Playwright
  • Prettier
  • Block Rebound
  • Transportation

File

PreviousNext

Centralized file operations — upload, browse, export, import, print, and document generation.

The @/components/file module consolidates every file-related concern into one place. Six sub-modules, one import surface.

Sub-modulePurpose
UploadCloud-based file upload with drag-and-drop and progress tracking
BrowserFile manager UI with grid / list views
ExportGenerate PDF, Excel, CSV, JSON from data
ImportParse CSV / Excel with a validation wizard
PrintBrowser-native print and print-to-PDF
GenerateTemplated documents — invoice, receipt, certificate, report card, ID card, transcript

Quick start

import {
  Uploader,
  ExportButton,
  Importer,
  PrintButton,
  FileBrowser,
  useUpload,
  useExport,
} from "@/components/file"
// PDF templates pull @react-pdf/renderer — import them from their own module
import { useGenerate } from "@/components/file/generate/use-generate"
 
<Uploader folder="documents" category="document" onUpload={(urls) => console.log(urls)} />
 
<ExportButton
  data={students}
  config={{ columns: STUDENT_EXPORT_COLUMNS, filename: "students" }}
  formats={["csv", "excel", "pdf"]}
/>
 
<Importer columns={STUDENT_IMPORT_COLUMNS} onImport={handleImport} />
 
<PrintButton contentRef={printRef}>Print Report</PrintButton>
 
<FileBrowser rootFolder="/school/documents" onSelect={handleSelect} />

Upload

The Uploader component renders a drag-and-drop dropzone backed by S3 signed URLs.

<Uploader
  folder="avatars"
  category="image"
  accept={{ "image/*": [".jpg", ".png", ".webp"] }}
  maxSize={5 * 1024 * 1024}
  maxFiles={1}
  onUpload={(urls) => setAvatarUrl(urls[0])}
  onError={(error) => toast.error(error)}
/>

Presets

Ready-to-use configurations live alongside the Uploader: AvatarUploader, DocumentUploader, ImageUploader, VideoUploader. Each presets accept, maxSize, and category for the common shapes. Read source for exact props.

useUpload hook

The hook exposes the same upload pipeline imperatively (no UI), useful for custom triggers.

const { upload, progress, isUploading, error } = useUpload({
  folder: "documents",
  category: "document",
})
 
await upload(file) // returns the public URL

Server actions

Two actions back the module:

ActionPurpose
getSignedUrl()Mint a presigned PUT URL for the client to upload to S3.
confirmUpload()Persist the resulting FileObject row keyed by schoolId.

Both validate auth() + getTenantContext() and write schoolId into every record.

Export

ExportButton opens a format picker (CSV, Excel, PDF, JSON) and triggers download.

<ExportButton
  data={students}
  config={{
    columns: STUDENT_EXPORT_COLUMNS,
    filename: "students",
    title: "Student Roster",
  }}
  formats={["csv", "excel", "pdf"]}
/>

For inline use without a picker, SimpleExportButton accepts a single format.

useExport hook

const { exportData, isExporting } = useExport()
 
await exportData({
  format: "pdf",
  data: students,
  config: { columns: STUDENT_EXPORT_COLUMNS, filename: "students" },
})

Columns

Per-feature column definitions live alongside the feature (e.g. students/columns-export.ts). They build on createColumn(key, label, options) helpers from @/components/file/export/columns. Common options: format, width, align, accessor.

Import

Importer renders a four-step wizard: upload → preview → map columns → validate.

<Importer
  columns={STUDENT_IMPORT_COLUMNS}
  template={{ filename: "students-template.xlsx", sheets: ["Students"] }}
  onImport={async (rows) => {
    const result = await bulkImportStudents(rows)
    toast.success(`Imported ${result.created} of ${rows.length}`)
  }}
/>

useImport hook

const { parse, validate, isImporting } = useImport()
 
const rows = await parse(file)
const issues = await validate(rows, columns)

Validation

validateRow(row, columns) checks every column's required, type, and custom validate(value, row). validateBatch(rows, columns) returns a per-row issue list and a totals summary.

Print

PrintButton opens the browser print dialog scoped to a content ref.

const printRef = useRef<HTMLDivElement>(null)
 
<PrintButton contentRef={printRef} title="Student Report">
  Print
</PrintButton>
 
<div ref={printRef}>{/* printable content */}</div>

usePrint hook

const { print } = usePrint()
print(printRef.current, { title: "Student Report" })

Generate

Pre-built templates render PDFs via @react-pdf/renderer. All return a Blob and a download trigger.

TemplateHelperUse
InvoiceuseGenerate.invoice()Fee invoices, receipts
ReceiptuseGenerate.receipt()Payment confirmation
CertificateuseGenerate.certificate()Course or achievement certificates
Report carduseGenerate.reportCard()Term and annual reports
ID carduseGenerate.idCard()Student / staff badges
TranscriptuseGenerate.transcript()Academic transcripts
import { useGenerate } from "@/components/file/generate/use-generate"
 
const { invoice } = useGenerate()
const blob = await invoice({ schoolId, items, customer, total })

generate is not re-exported from @/components/file: see What the barrel exports.

Each template accepts a typed payload and renders both a downloadable PDF and an inline preview component (InvoicePreview, etc.).

Browser

FileBrowser renders a tree-aware file manager rooted at any S3 prefix.

<FileBrowser
  rootFolder={`/${schoolId}/documents`}
  onSelect={(file) => setSelected(file)}
  view="grid"
/>

useBrowser hook

const { files, currentFolder, navigate, search } = useBrowser({
  rootFolder: `/${schoolId}/documents`,
})

Storage and limits

ConstraintDefault
ProviderAWS S3 + CloudFront CDN
Default size cap10 MB per file (per category override)
Image cap5 MB
Document cap25 MB
Video cap100 MB
Allowed typesPer-category whitelist; rejected uploads return error code

Multi-tenant safety

Every persisted file row carries schoolId. Signed URLs include a schoolId claim so cross-tenant URL forging is impossible. FileBrowser always rooted at a schoolId-scoped prefix.

// correct — schoolId scope on every query
await db.fileObject.findMany({ where: { schoolId, folder } })

i18n

UI text comes from the dictionary.file slice. Helpers (getDictionary server-side, useI18nMessages on the client) provide the full set of labels for the picker, the wizard, error toasts, and progress states.

Errors

Server actions return error codes; the client maps them via useI18nMessages(dictionary).error.

CodeMeaning
FILE_TOO_LARGEExceeds the per-category size cap
INVALID_FILE_TYPEMIME type not in the category whitelist
UPLOAD_FAILEDS3 returned non-2xx
MISSING_TENANTNo schoolId on the session
UNAUTHORIZEDauth() check failed
INVALID_FOLDERFolder path outside the schoolId-scoped tree

TypeScript

All public exports are fully typed. Common types:

import type {
  ExportConfig,
  FileObject,
  GenerateConfig,
  ImportConfig,
  PrintConfig,
  UploadConfig,
} from "@/components/file"

What the barrel exports

@/components/file is imported by client components (ExportButton, FileUploader, …), and a re-export is bundled whether or not the importer uses it. Until 2026-09-19 the barrel re-exported the whole block, and 44–49 routes — students, attendance, grades, classrooms, exams, finance, teachers — shipped @react-pdf/renderer (462 KB gzip), the Node crypto polyfill (137 KB), xlsx (135 KB) and the AWS SDK (45 KB) in their initial JavaScript to draw a table with an Export button. Removing those re-exports took 870 KB gzip off each of those routes.

So the barrel carries components, hooks, types and light helpers. These are imported from their own module:

WhatImport fromWhy it stays out of the barrel
Storage providers (getProvider, S3, R2)@/components/file/providers · …/providers/factoryAWS / R2 SDKs, server-only
Rate limiting@/components/file/rate-limitUpstash client, server-only
CDN signing (generateSignedUrl, …)@/components/file/cdnnode:crypto, server-only
Deduplication (generateFileHash, …)@/components/file/deduplicationnode:crypto, server-only
PDF templates, useGenerate@/components/file/generate/*@react-pdf/renderer
exportToExcel, exportToPdf, PDFPreview@/components/file/export/excel-generator · …/export/pdf-generatorxlsx, @react-pdf/renderer

Their types are still exported from the barrel. useExport and the spreadsheet parser load xlsx / @react-pdf/renderer with await import() when an export or a parse is actually asked for — follow the same pattern for anything heavy that serves a click rather than a render.

pnpm perf:check fails the build gate if one of these libraries re-enters a tracked route's initial JavaScript (performance guide).

Migration from the old layout

Older code imported from @/components/upload, @/components/export, @/components/import, etc. Migrate components and hooks to the barrel (heavy and server-only modules keep their own path — see above):

// before
import { ExportButton } from "@/components/export"
// after
import { ExportButton, Uploader } from "@/components/file"
import { Uploader } from "@/components/upload"

The old module paths still re-export for compatibility but are deprecated. Open issues for any direct imports still in the wild.

DatabaseCDN Assets

On This Page

Quick startUploadPresetsuseUpload hookServer actionsExportuseExport hookColumnsImportuseImport hookValidationPrintusePrint hookGenerateBrowseruseBrowser hookStorage and limitsMulti-tenant safetyi18nErrorsTypeScriptWhat the barrel exportsMigration from the old layout

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.