A lending library for the gear that makes gatherings happen: folding tables, chairs, a PA, pop-up tents, string lights, an ADA ramp. Civic Works and GROUP PROJECT keep it in New Haven. Hosts put together a request list, one of the librarians writes back, pickup happens in person at GROUP PROJECT, and after the gear comes back the host leaves a tip. That tip becomes part of the item's public biography.
- The library (home): all 13 starter items grouped by category. Each shows how many are available and the latest tip from a past host.
- Item pages: description, care notes, availability, and the biography (dated tips from past borrowers, newest first).
- Request list: add several items, adjust quantities, then send one short form. Phone is required. Pickup is a range (earliest and latest day), which starts out as a single day until the host widens it. Submitting creates one request group with one line per item.
- Confirmation: pickup details, plus the most recent tip for anything you requested.
- Librarians' desk (
#/librarian): requests grouped by submission, with Approve, Decline, Check out and Mark returned buttons for each item. Anything past its return date gets flagged. Once anything in a request comes back, the request gets one Copy reflection link button, and each returned item gets Add a tip they told us for tips a host says at the counter (posted only with their OK). The Testimonials tab is where librarians approve or hide testimonials that hosts marked OK to share. The Inventory tab syncs from a published Google Sheet CSV, with a preview before anything changes. - Reflection page (
#/reflect/<token>): one link per request. It asks one required question, "How did the gathering go?", with an "OK to share" checkbox that starts unchecked. Below that are optional tips for each returned item, collapsed until the host taps one. Tips go straight onto item biographies. Testimonials stay private to librarians unless the host checked "OK to share" and a librarian approved them. Older per-item links still open the single-item form. - What hosts say (About page): shared, approved testimonials only. The section is hidden until there's at least one.
- About: how borrowing works and who keeps the library.
With SUPABASE_URL and SUPABASE_ANON_KEY set (see .env.example), the app uses one shared database. Without them it runs on seeded preview data saved in this browser.
- Run the files in
supabase/migrations/in order, 0001 through 0013. - Authentication → Providers: turn on Email. Under URL Configuration, set Site URL to the address you use day to day. Add
https://<preview-host>/**andhttps://<published-host>/**to Redirect URLs. The librarian sign-in page shows the exact line to paste. - Librarians are rows in
librarian_allowlist. To add someone, run a new migration (or SQL) withinsert into public.librarian_allowlist (email, name) values ('them@example.com', 'Their name');. Delete the row to remove them.
Hosts' contact details can be read only by signed-in librarians on the allowlist. The public can read the library and the biographies, and can call three functions: send a request, read a reflection prompt, and leave a reflection. Magic links only work in the same browser that asked for them.
Every email goes to the host and groupprojectnhv@gmail.com, with Reply set to groupprojectnhv@gmail.com (the email service accepts only one reply-to address). Each one uses the same subject line, so Gmail keeps the whole request in one thread. Emails are sent through Community Cloud, so there are no keys to manage. The sender (library@groupproject.group) is set in the Cloud tab under the connected backend, not in code. It only works while groupproject.group stays verified in the Resend account behind it. When a host replies, it goes to the library inbox, not to the From address.
- Request made: sent automatically, but only after the database has saved the request. If the email fails, the host's confirmation page says so.
- Checkout, return thank-you, late check-in: drafts on each request card in the librarians' Emails row. Librarians edit and send them. The late email never sends by itself. After a send, the card shows when it went out.
Librarians: use Reply all so the host stays in the thread. Set up a Gmail filter that labels any email whose subject contains Lending library request: (replies starting with "Re:" match too), and agree on who checks that label each day. The default wording and the borrowing policy are in the email section of src/content/copy.ts.
At the end of the request form, hosts pick what they'd like to chip in: $10, $15, $20, another whole-dollar amount, or "Not this time". "Not this time" counts the same as any other choice and asks nothing more. A ? beside the heading explains why the library asks. The app only records the offer. The money changes hands in person at pickup (cash or Venmo), alongside any deposit. The amount shows on the confirmation page, in the request email and on the librarians' request card. "Not this time" shows only on the card. A pledge is an offer, not a bill, so nobody follows up on it. Librarians: agree where the cash goes and whose Venmo takes it, and every so often tell hosts what it paid for (about.getInvolved in the copy file is a good place).
All public wording is in src/content/copy.ts, one named entry per piece of text. Rewrite any value freely, but keep the names. A value starting with TODO shows up as a dashed note on the page until you replace it.
Every site color lives in the palette block at the top of src/index.css. There are eight named lines: --wall (background), --ink (headings, borders), --text, --text-soft, --button, --panel, --tint and --warning. Change one line and the whole site follows. If you change --wall, also change theme-color in index.html, which colors the phone's browser bar.
Keep --ink, --text and --warning dark enough to read on --wall (a contrast of 4.5:1 or more, which any online contrast checker will tell you). The GP and CW brand colors below the palette are fixed. They're used only for the stripes, category blocks and bundle bands, so changing the palette doesn't recolor them.
name, slug, category, quantity_total, status, description, care_notes, replacement_cost, deposit, block_party, indoor_gathering
block_party and indoor_gathering hold the suggested count of that item for one typical gathering of that kind (e.g. 2 tables, 4 chairs, 1 cooler). They are not how many the library owns. Leave the cell blank if the item isn't in that bundle. An item can be in both. "Start with this bundle" on the Library page puts these counts on the host's request list, and the host adjusts from there. If a sheet doesn't have one of these columns, the saved counts stay as they are.
replacement_cost and deposit are optional and take whole dollars ("$180", "180" and "180.00" all work). A blank cell clears the amount. If a sheet doesn't have one of these columns, the amounts already saved stay as they are. Both amounts are public. The replacement cost shows on the item page. The deposit shows on the item page, on the request list, on the confirmation page and on the librarians' request card. Each item has one deposit, however many someone borrows.
Category is one of: Furniture, Sound/AV, Lights/Power, Signage, Safety, Games, Other. Status is one of: active, repair, retired. The sync matches rows to items by slug, and anything missing from the sheet stays as it is. In Google Sheets: File → Share → Publish to web → CSV.
Real photos only: a phone camera against a plain wall at GROUP PROJECT. No AI-generated images of items, people, or New Haven.
Built by Kai in New Haven, with Civic Works and GROUP PROJECT. Created from a build plan made at the Relational Tech Studio (https://studio.relationaltechproject.org). Remixed from Local Supplies Sharing — https://studio.relationaltechproject.org/library?item=dcb4d804-a256-4402-b785-00dabbf9635c Item-biography pattern drawn from the Z-Space community library shelf.
- Make repo public
- Add
relational-techtopic (gh repo edit <owner>/<repo> --add-topic relational-tech, or gear icon next to "About" in the GitHub UI) - Add MIT license
- Commit
.reltech.yml
The RTP Watcher finds public repos with the relational-tech topic so other neighborhoods can find this work and remix it.
MIT. See LICENSE.