Vlad Ilie
BookTruck – site de prezentare, donații și campanii de strângere de fonduri pentru biblioteca mobilă
Next.js · Strângere de fonduri · ONG

BookTruck – site de prezentare, donații și campanii de strângere de fonduri pentru biblioteca mobilă

booktruck.cartilepefata.ro ↗
acum o lună
•••
9 min de citit

Context

Pe Victor Miron l-am cunoscut în decembrie 2016, la un eveniment din Timișoara despre cărți. Atunci am aflat de Asociația culturală Cărțile pe Față și de misiunea ei de a-i face pe români să citească mai mult. Am început ca voluntar la cluburile de lectură „Vocea cititorului”, am co-organizat două ediții, iar după mutarea în Cluj-Napoca am preluat, treptat, infrastructura IT a asociației.

BookTruck este biblioteca pe roți a asociației, care duce cărți copiilor din satele fără bibliotecă funcțională. Site-ul a pornit în martie 2022, pe Next.js 12, ca un site de prezentare cu formular de donații. De atunci a devenit locul prin care asociația strânge bani, primește cărți și voluntari și își vinde propriile cărți, iar în toamna lui 2026 a primit un panou de administrare, campanii de strângere de fonduri și conturi pentru donatori.

Problema

Un site de strângere de fonduri trebuie să fie, în primul rând, de încredere: o donație lunară trebuie să apară în evidențe chiar dacă procesatorul de plăți nu anunță site-ul, iar o rambursare făcută din contul de comerciant trebuie să se vadă și în panou. Asociația voia și campanii de tipul „dăruiește-ți ziua de naștere”, create de echipă sau chiar de susținători, dar fără ca orice pagină să ajungă publică nevăzută de nimeni. Pe lângă asta, vinde cărțile BookTruck, organizează în fiecare an o campanie de Crăciun și are nevoie să-și actualizeze singură partenerii, galeria, presa, punctele de donare și harta școlilor adoptate – fără să-mi scrie mie pentru fiecare logo.

Ce am construit

Sub capotă

Piesa de care sunt cel mai mulțumit este sincronizarea donațiilor cu EuPlătesc. Rulează în fiecare noapte prin Vercel Cron (sau manual, din panou) și prinde exact cazurile în care un site obișnuit pierde date: plățile lunare pe care EuPlătesc le face fără să cheme site-ul, plățile al căror callback nu a ajuns, rambursările și anulările făcute direct din contul EuPlătesc. La prima rulare a adus tot istoricul, din 2018 încoace. Poate rula de oricâte ori fără duplicate; o rulare incrementală acoperă ultimele 35 de zile și durează câteva secunde. Pentru că același cont de comerciant e folosit și de alte site-uri, importă doar tranzacțiile BookTruck, iar descrierea plății păstrează campania, deci și plățile lunare ale unei campanii ajung înapoi la ea. Integrarea folosește biblioteca EuPlătesc pentru Node.js pe care o întrețin.

Campaniile refolosesc aceleași donații și aceeași sincronizare, nu un sistem paralel. Suma strânsă nu se salvează, ci se calculează din plățile încasate până la încheierea campaniei, minus rambursările: o donație lunară contribuie cu fiecare plată cât timp campania e activă, iar plățile de după nu mai mișcă progresul afișat. Pozele și videoclipurile se încarcă direct din browser în Supabase Storage, cu URL-uri semnate, deci fișierele mari nu trec prin server, iar un job săptămânal șterge fișierele rămase fără campanie. Descrierea se scrie cu Tiptap și se curăță pe server, cu o listă scurtă de taguri permise.

Pentru licitații, plasarea unei oferte se face într-o singură tranzacție care blochează rândul licitației în Postgres. Minimul, plafonul, timpul rămas și liderul se recitesc acolo, nu se iau de la browser, pentru că două oferte egale la câteva milisecunde distanță sunt un caz real, iar un index unic pe sumă e ultima barieră. Tot acolo se alocă numărul de licitator. Un job Vercel Cron închide în fiecare minut licitațiile expirate și altul trimite reminderele, iar orice vizită pe o pagină publică închide și ea o licitație al cărei termen a trecut, deci nimeni nu poate licita după final chiar dacă o rulare e ratată. Închiderea poate rula de mai multe ori fără efecte duble. La final, câștigătorul primește e-mail, ceilalți află rezultatul, iar echipa preia adjudecarea din panou: notițe de discuție, link de plată sau plată offline, predarea obiectului și, dacă un câștigător renunță, reatribuirea către următorul clasat, la prețul ofertei lui. Linkul de plată creează o donație obișnuită prin EuPlătesc, deci banii apar în aceleași evidențe și statistici ca restul donațiilor. Membrii echipei nu pot licita, iar o ofertă aberantă se poate anula din panou, cu motiv, fără să fie ștearsă. Regulile licitațiilor sunt funcții pure, testate, folosite și de server, și de pagini, ca interfața să arate exact limitele pe care le aplică serverul.

Site-ul a trecut de la Pages Router la App Router în 2024, are o bază de date Prisma pe Supabase Postgres, react-hook-form și zod în loc de Formik și Yup, iar i18n-ul se face cu next-intl. Prima pagină e generată static și se reîmprospătează singură, ca o campanie care se încheie să dispară fără nicio editare. Autentificarea folosește Better Auth cu magic link și limite în Redis, formularele publice sunt protejate cu Cloudflare Turnstile, iar mediile de test nu sunt indexate. Paginile au adrese canonice, variante de limbă, sitemap și imagini pentru distribuire. Regulile de donații, campanii și licitații, sincronizarea, statisticile, permisiunile, consimțământul pentru cookie-uri, rutele de plată și joburile licitațiilor au teste Vitest.

Rezultat

Asociația își administrează singură conținutul, campaniile, licitațiile și comenzile și vede, într-un singur loc, donațiile, recurențele active și progresul fiecărei campanii, cu evidențe care se reconciliază singure în fiecare noapte. Susținătorii își pot porni propria strângere de fonduri, pot licita pentru obiecte donate și își pot urmări donațiile și ofertele fără să scrie cuiva. Site-ul își face treaba zilnic: oamenii donează, comandă cărți și se înscriu ca voluntari, iar BookTruck-urile pleacă spre școli.

Tehnologii

Next.js 16 (App Router), React 19, TypeScript, next-intl, Prisma 7 pe Supabase Postgres, Supabase Storage, Better Auth, Redis, Tailwind CSS 4, shadcn/ui, Headless UI, framer-motion, Tiptap, dnd-kit, Recharts, react-hook-form + zod, EuPlătesc, Stripe, Oblio, Brevo, Cloudflare Turnstile, Google Drive API, Vitest, Vercel.

Tehnologii:

  • Next.js
  • next-intl
  • Prisma
  • Supabase
  • Better Auth
  • EuPlătesc
  • Stripe
  • Oblio
  • Vitest

Partajează pe: