ARSHADUL.exe, home
Menu

/WORK / Cooperative membership and credit

Zariya

A cooperative desk where a missed day is not a payment.

Zariya is membership and credit software for a thrift and credit cooperative. Visitors can read the public site and apply for membership. Staff sign in to a dashboard that queues loans, disburses them, records installments, and closes them. The installment calendar, including holidays and missed days, is calculated on the server. The interface has to show those states without pretending each row is cash received.

Category
Cooperative membership and credit
People
  • Prospective members on the public site
  • Staff who process memberships, loans, collections, and reports
Contribution
I built this product myself, frontend and backend. That covers the public membership flow, the staff console, and the API for loans, repayments, permissions, and documents.
Status
Live

The challenge

A cooperative does not run on a single form. Someone applies, an officer reviews a loan, money is disbursed, and then collections happen on working days that skip holidays. A missed installment has to stay visible, and it must not look like a receipt.

Different officers need different powers: read a queue, disburse, relax a fine, download a notice, or edit roles. Member documents have to be viewable by the right staff without placing a storage address in the page.

The solution

The public site explains the society and accepts a contact message and a membership application. Staff sign in and work in a dashboard. Navigation shows only the items their permission keys allow. A direct visit to a forbidden child route returns them to the dashboard.

Loans move from a queue and an application form into a loan record. Officers can disburse, open repayment history, download reports they are allowed to download, and close the loan with an outcome. The React app does not build the installment calendar. It renders rows and totals the API already classified.

  • Public membership application with a success state, and a staff desk for the same records.
  • Loan queue, application detail, and a sectioned new-loan form.
  • Populate helpers that copy an address or a previous record, and that refuse to copy the applicant into the guarantor slot.
  • Disbursement, fine context after disbursement, and a close-loan flow with a credit-score summary.
  • Repayment history that labels cash, transfer, UPI, system misses, and holidays differently.
  • Organisation holidays and member holiday context, consumed by the server schedule.
  • Report screens and downloads gated by their own permission keys.
  • User, employee, and role administration.

System architecture

The frontend is a React single-page app built with Vite and JavaScript. Redux Toolkit splits state by feature: auth, loan draft, applications, memberships, users, dashboard, and one slice per report. Server data is loaded with async thunks and a hand-written fetch client. There is no React Query layer. Screens clear selected records as the officer moves, so a previous loan does not linger in the form.

Dashboard pages other than the home view are loaded lazily, with a shared skeleton. Login redirects an existing session to the dashboard. Each child route names the permission it needs. An admin route is just the user-management permission, not a separate role check.

The fetch client sends a bearer token, throws the API error message, and treats a 401 outside login as an expired session. PDFs and images are downloaded as blobs and opened with object URLs. Membership and employee documents use that path on purpose, so the page never renders a storage URL.

The Express API verifies the token, checks permission middleware, and owns schedule, balance, fine, and credit-score services. MongoDB stores memberships, loans, repayments, holidays, users, and roles. The UI depends on those services. It does not reimplement them.

System architecture

How the clients, shared code, and API relate. Read the columns from left to right.

01 Browser

  • Public siteMarketing page, contact, and membership application.
  • Staff SPALazy dashboard routes, Redux slices, fetch client.
  • DocumentsAuthenticated blob downloads. Storage URLs stay off the page.

02 API

  • ExpressJWT and permission checks on the route.
  • Domain servicesInstallment schedule, balance, fines, and credit score.

03 Records

  • MongoDBMembers, loans, repayments, holidays, users, and roles.
  • FilesDocuments are stored behind the API and streamed back to the UI.

Styling is SCSS per screen. MUI is used for controls: fields, selects, date picking, snackbars, and tooltips. It is not the layout system. The scoring and fine formulas stay on the server and are not reproduced here.

Key engineering decisions

Permission keys instead of one admin menu

Engineering challenge

Reports, downloads, disbursement, and closure are different powers. A binary admin flag either shows too much or needs a new role name for every button.

Implementation approach

A frozen catalog of keys feeds the sidebar, a route guard, and a hook that checks the current user. The API repeats the check with permission middleware. Role editing saves a selection from that catalog and can refresh the signed-in user.

Engineering rationale

A new report or download can be granted without shipping a new job title. The navigation and the route agree on the key.

Trade offs

The client catalog and the server catalog are separate copies, so a new key has to be added in both places. The dashboard home itself is available to any signed-in officer.

A loan form with sections and explicit copy rules

Engineering challenge

Officers retype addresses, nominees, and guarantors constantly. Copying the previous loan is useful. Copying the applicant into their own guarantor slot is not.

Implementation approach

The new-loan form is a sectioned Redux draft with shared validation for amounts, tenure, mobiles, and PIN codes. A populate drawer lists allowed sources. The rules state that the applicant cannot be their own nominee, guarantor, or co-applicant.

Engineering rationale

The form matches how an officer actually assembles a file, and the dangerous copy is blocked in the client rules rather than left as a warning in a wiki.

Trade offs

A flexible draft is easy to extend and easy to let drift from API validation. The shared validator is the contract the screen trusts before submit.

Repayment history that keeps holidays and misses distinct

Engineering challenge

A daily installment schedule is not a list of receipts. Holidays are skipped. Missed days are real. If the table styles every row as money in, the officer will collect against a fiction.

Implementation approach

The server builds the schedule and the miss rows. The history table branches on holiday and system-generated flags. Cash, transfer, UPI, system, and holiday rows get different labels. Long lists page with an intersection observer.

Engineering rationale

Correctness of the calendar stays where it is tested, on the server. The UI's job is to make the row type obvious.

Trade offs

A new row type from the API will look wrong until the table learns the flag. The client does not invent a second calendar to paper over that.

Documents through the API, not a public file URL

Engineering challenge

Membership files are identity documents. Binding an image to a storage URL puts that address in the page.

Implementation approach

The screen asks the API for the file, dedupes the request, and turns the blob into an object URL. The URL is revoked when the view goes away. Permission to see the file is the API's decision.

Engineering rationale

The page can show a document to an authorized officer without teaching the browser where the file lives.

Trade offs

There is more client code than an img tag. That cost is the point: the storage policy stays on the server.

Core capabilities

Membership, public and staff

A visitor applies on the public form. Staff review, edit, and open the member record, including a credit-score page.

Value. The application a person submits is the record an officer continues, not a second data entry.

Complexity. The same form container runs in both places. Documents load through the authenticated file path.

Loan application and queue

Officers work a queue and a multi-section application: amount, tenure, nominee, guarantor, and an optional co-applicant.

Value. A loan file can be assembled and reviewed before it becomes a live loan.

Complexity. Validation and populate rules sit beside the draft so repeated data entry does not copy the wrong person.

Disbursement and closure

An authorized officer records disbursement. Closure collects an outcome, a remark, next-loan eligibility, and shows a credit-score summary computed by the API.

Value. The loan has a beginning and an end that other staff can see, with the score kept as a display of a server result.

Complexity. Disburse and close are narrower permissions than read. The screen hides the action when the key is absent.

Collections and holidays

Repayment history shows what was paid, what fell on a holiday, and what the system marked as missed. Organisation holidays are maintained by staff.

Value. An officer can collect against the right day and explain a gap without opening a spreadsheet.

Complexity. Holiday rules change the server schedule. The table only classifies the rows it receives.

Reports and documents

Loan, collection, and outstanding reports are separate routes. Downloads use their own permission keys. Contracts and notices follow the same idea.

Value. Reporting is part of the job, and not every officer who can read a loan can export it.

Complexity. The file is a blob from the API. The button is present only when the key allows it.

Roles and staff accounts

User and employee records, including multipart employee data, sit next to a role editor that saves a permission selection.

Value. Access can change as the desk changes, without a deploy for each officer.

Complexity. The editor writes keys. Routes and the sidebar read those keys on the next session refresh.

Technology stack

Frontend

  • React
  • Vite
  • React Router
  • JavaScript

State and data

  • Redux Toolkit
  • Async thunks
  • fetch
  • Blob downloads

UI

  • SCSS
  • MUI controls and date pickers
  • Day.js

Backend

  • Express
  • MongoDB
  • JWT
  • Permission middleware
  • Schedule and credit services

Engineering impact

Staff can move through membership, credit, and collections in one shell. The public application is the start of that same record.

Financial rows keep their meaning. A holiday and a miss are visible states, which is what makes a collection screen trustworthy.

Permissions are granular enough for downloads and closure to be separate from read access. The route guard and the sidebar use that same catalog.

Route-level code splitting keeps later screens out of the first dashboard paint. React and MUI stay in the entry bundle so the app does not load two copies of React.

Workflows and modules

These drawings describe the desk and the loan path. They are not captures of member records or documents.

Application modules

Public

  • Marketing
  • Contact
  • Membership application

Credit

  • Queue
  • Applications
  • Loans
  • Disbursement
  • Closure

Collections

  • Repayments
  • Holidays
  • Reports

Access

  • Users
  • Employees
  • Roles
  • Documents
Primary journey
  1. 01Apply

    A prospective member submits the public application.

  2. 02Review

    Staff open the queue and complete the loan file.

  3. 03Disburse

    An officer with the key records the start of the installment schedule.

  4. 04Collect and close

    History shows payments, holidays, and misses. Closure records the outcome.

Engineering takeaways

  1. I model financial UI as states, not as a single list of money in.
  2. I gate the action with the same permission key the API will check.
  3. I would rather fetch a document as a blob than put a storage address in the page.