Aleksei Makeev.
← ALL PROJECTS
02 / 03NONPROFIT · CYPRUS3 APPS · 1 DATABASE2025–26

Paws of Cyprus — three apps for one charity

A volunteer animal-welfare network needed more than a Facebook page. It got three: a public site where each animal has a costed request and every euro is accounted for, a rota app for the people who feed street colonies at night, and a back office non-developers can actually run. One database underneath, one person building it. Live at pawsofcyprus.org.

Next.js 14 & 15React 19TypeScriptSupabaseStripeLeafletMapLibre GLTipTapCloudflare R2Telegram Bot APIUpstash RedisVercel
The Paws of Cyprus home page
01

The public site — donations with a ledger anyone can check

PROBLEM

Rescue work in Cyprus runs on informal networks: a post in a group, a bank transfer, trust. Someone who wants to help cannot see what a particular animal actually needs, or what happened to the money afterwards, and volunteers spend months answering the same questions by direct message. The audience splits three ways — Greek-speaking Cypriots, English-speaking expats and a large Russian-speaking community — so one language was never going to be enough.

APPROACH

Every animal comes with costed help requests and a visible goal, so a donation goes to a named vet bill rather than into a general fund. Stripe takes the card payments, and a public balance page sets donations against withdrawals so anyone can check the arithmetic. The knowledge base covers what people actually search for at two in the morning: emergency vets by city, import rules, what to do with an animal you have just found. Every page exists in English, Greek and Russian, chosen from the browser.

The public balance page: money in and money out, with donor names masked for this screenshot
The contact page: an email address, a message form, and the phone numbers of clinics that take emergencies, tabbed by city
02

Volunteers — a duty roster for street feeding

PROBLEM

Feeding a colony every day is mostly logistics: who covers which point, who is away this week, which places have nobody down for tonight, and where it is not safe to go alone. All of it lived in one Telegram group, where a message scrolling past means an animal does not get fed. The volunteers are mostly Russian-speaking, unpaid, and looking at their phones in the dark.

APPROACH

Feeding points sit on a map with their schedule, their photos and the name of whoever is responsible. Recurring shifts get laid out automatically, an exception is recorded when someone is away, and a slot nobody has taken can be claimed; every morning a cron job looks for points with no one on them and writes to the group. Telegram is kept in the loop rather than replaced — a bare number typed into the group counts as that many bags of food pledged, and after every deploy the bot re-reads the chat history, so a restart cannot lose someone’s promise.

The rota for one feeding point: the days of the week a volunteer has taken on regularly, and every upcoming date with a button to hand it back
The volunteer sign-in screen, protected by Supabase Auth
Points

Every feeding point on a map with its schedule, its photos and the volunteer responsible for it.

Rota

Recurring shifts lay themselves out; an absence is an exception row, and an unclaimed slot can be taken by anyone.

Nightly check

A cron job finds points with nobody on them and writes to the Telegram group before the evening round.

Telegram, still

A bare number in the group counts as that many bags of food. After a deploy the bot re-reads the history, so a restart cannot lose a promise.

03

Admin — the panel the charity is run from

PROBLEM

Volunteers have no spare hours to lose to a CMS. The back office had to be usable by whoever happens to be on duty, on a laptop, in Russian — while still drawing a hard line around money. The person editing a blog post should never be one wrong click away from the withdrawals screen.

APPROACH

A rich-text editor with a drag-and-drop media manager that uploads straight to Cloudflare R2 behind signed URLs, and a map picker for feeding points. Permissions live as an array on the admin’s own row and are checked in middleware against the path being asked for, so rights belong to a person rather than to a fixed role. A “Generate draft” button in the care-guide editor fills the article in from a language model, and the volunteer either takes it or sends it back to be rewritten. Every image the draft points at is checked to see that it actually loads, so nobody reviewing an article runs into a broken link. Every article is written in all three languages from the same screen.

The admin sign-in screen, which says plainly that every action in the panel is logged: who changed what, and when
The admin dashboard: counts across the platform, a queue of requests waiting for a decision with how long each has waited, and an activity log
The knowledge base: sections on the left in the order they appear on the site, and each article showing which of the three languages it exists in and whether it is published or still a draft
3
LOCALES — EN / EL / RU, WITH DETECTION
48
TABLES ACROSS THE THREE APPS
18
ADMIN SCREENS, ALL IN RUSSIAN