Skip to content

In-app feedback for users without a GitHub account #141

Description

@carvalab

Problem

FloCafe is installed by café and retail owners who don't have GitHub
accounts. The Help menu links to GitHub Issues, which they won't open.
Real users hit real bugs, decide a GitHub account isn't worth creating
for a one-off report, and the report is lost. The fix is an in-app
feedback flow that works for every FloCafe user without any external
account.

Inspiration: bugdrop and Fider

The clearest end-user feedback widget in the open source space is
mean-weasel/bugdrop, at
v1.53.0 with the most recent release 3 days ago. MIT, ~36 stars, ranked
#6 on Product Hunt on 2026-05-09. One launcher, one modal, one sink.
Submit creates a GitHub issue via a server-side credential. The model
works because it does one thing.

For the second sink, the clearest end-user feedback board is
getfider/fider, at v0.36.1 with
the most recent release 3 days ago. MIT, 4.4k stars. Fider gives you
posts, votes, comments, statuses, and a public board out of the box.
It exposes a public REST API at POST /api/v1/posts that takes a
title and description, authenticated with a per-user API key
(Authorization: Bearer <key>).

Fider Cloud at *.fider.io is free for the first 250 feedback items
under a fair-use cap. If the board grows past that, we can revisit
self-hosting or another plan.

What the rest of the community does

Project Last commit Stars Lesson
mean-weasel/bugdrop 3 days ago 36 The model. One modal, one sink, GitHub.
getfider/fider 3 days ago 4,400 The board. Posts, votes, comments, public API.
reikjarloekl/SitePing 2 months ago 0 DOM-anchored annotations, hidden below 768px. Wrong for POS tablets.
feedbackmonk 2 months ago new 30 KB widget with a strict size cap. Inspiring rigor, AGPL-3.0 license rules it out.
usero-feedback/usero 2 months ago 3 Headless SDK with AI clustering. Heavy for our case.
contember/ucho 4 months ago 3 Solid.js widget with screenshot + annotations. Nice shape, but adds annotation tooling.
WarriorSushi/feedbacks.dev-2026 16 days ago 0 Next.js + Supabase + AI. FSL license, not MIT. Avoid.
rayketcham-lab/issue-reporter 4 months ago 0 Browser-only, token in page source. The lesson, not the model.
NoteLace desktop client active n/a "Help us improve" opens mailto: with a Preview diagnostics button. The Preview pattern is good, but mailto: assumes a mail client.

Unmaintained or stale projects are excluded: electron-elements/send-feedback
(no release since 2022), BPS-Consulting/wafir (last push 4 months ago).

The community lands on a server-mediated design every time. The projects
that put a token in the renderer consistently draw spam warnings. The
projects that keep the credential behind a server endpoint with
validation and rate-limiting are the ones people keep using.

Proposed approach

Two sinks, both opt-in for the operator. The operator picks one, or
both, in the existing Settings UI. The flow is online-only in v1: if
the user is offline, the dialog tells them so and refuses to send.

Sink A: GitHub Issues (bugdrop-style)

The Express backend on :3001 creates an issue via a fine-grained PAT
held in GITHUB_FEEDBACK_TOKEN + GITHUB_FEEDBACK_REPO env vars.
Issues go to a dedicated FloCafe Feedback repo and are labeled
user-idea / user-praise / user-issue so the maintainer can
filter.

Sink B: Fider (new)

The Express backend on :3001 posts to <fider-base>/api/v1/posts
with Authorization: Bearer <fider-api-key>. The Fider API key is
held in FIDER_FEEDBACK_URL and FIDER_FEEDBACK_API_KEY env vars.
The body shape is:

{
  "title": "[FloCafe] <kind>: <first line of message>",
  "description": "<message>\n\n---\n\n**App:** FloCafe <version>\n**OS:** <os>\n**Locale:** <locale>\n**Store hash:** <sha256(cut at 12)>\n**Last route:** <route>\n**Sent at:** <iso timestamp>"
}

Cloud Fider at feedback.fider.io works out of the box. Self-hosted
Fider works the same way once the operator points the env var at
their instance. Fider calls each submission a "post"; the title
follows the platform's own convention (a short idea, suggestion, or
bug), so we don't need to translate the category names.

UI surface

Two entry points. Both open the same FeedbackDialog, a Radix
Dialog so we get a focus trap, ESC, and role="dialog" for free.

  • Help → Send feedback (one new menu item in HelpMenu).
  • Settings → Support → Send feedback (one new card next to the
    existing "More Apps" and "About" cards).

The dialog has three category chips (Praise, Issue, Idea), a
single textarea, an optional email field, and a Send button. No
preview disclosure, no screenshot button in v1. The labels are
deliberately short so a cashier can pick one without reading.

Connectivity (online-only in v1)

The dialog checks navigator.onLine on open. If the user is
offline, the Send button is disabled and a one-line message says
"Please connect to the internet to send your feedback." When the
user comes back online, the message disappears and the button
re-enables.

On click, the renderer POSTs to /api/feedback directly through the
existing api client. There is no local queue, no SQLite table, no
drain loop. Offline users get a clear message; online users get a
direct round trip.

If the request fails:

  • Network error (offline mid-send, DNS failure, timeout) → the
    dialog shows "Please connect to the internet to send your
    feedback." with a Retry button.
  • Server error (5xx, no sink configured, sink auth failed) → the
    dialog shows "Couldn't send right now. Please try again." with a
    Retry button.
  • Validation error (4xx, missing fields) → the dialog shows the
    field-specific error inline.

Server payload

The renderer builds and sends the payload; the backend forwards it
to the configured sink and never enriches client-side fields the
client already knows. The payload shape:

{
  kind: 'praise' | 'issue' | 'idea'
  message: string            // required, max 2000 chars
  email?: string             // optional, validated
  app_version: string        // from app.getVersion()
  os: 'win' | 'mac' | 'linux'
  locale: string
  store_id_hash: string      // SHA-256 of store id, truncated to 12 hex
  last_route: string         // e.g. "/(dashboard)/settings"
}

The server adds the ISO timestamp before forwarding. No order
contents, no customer names, no payment data, no unredacted logs.

Consent

No consent gate. The existing anonymous_data_consent setting stays
for telemetry and is unrelated here. If the user clicked Send, that
is the consent. The dialog shows one line under the email field:
"Your message and basic app info are sent to the configured
destination. We do not include order data or customer names."

Why this shape

  • One local component, two sinks. The renderer code is the same
    regardless of sink. The sink choice is operator config.
  • Server-mediated. The pattern the community keeps converging on.
  • No new schema. Feedback never touches the local DB in v1.
    We can add an offline queue in v2 if real users complain.
  • Fider is the better default for end users. GitHub Issues is
    where developers live. Fider is where the customer's idea of
    "feedback" lives: posts, votes, status updates, public board.
    Using "Idea" as the category name matches Fider's own UI, so
    what the user sees in the dialog and what they see on the
    public board are the same word.
  • Free to start. The free Fider Cloud tier covers 250 items.
    How we handle the cap if we get close is a follow-up question,
    not a v1 commitment.
  • No new service to run. The Express backend on :3001 is
    already there. New route, two sink adapters.

What this is not

  • Not a replacement for GitHub issues for developers and power users.
  • Not a chat or support tool. No two-way conversation in v1.
  • Not a replacement for crash reporting. Sentry is a separate
    proposal if we ever want it.
  • Not an offline queue. That is v2 if anyone asks.
  • Not screenshot tooling. Also v2.

Sketch of the diff

  • main/services/feedback.ts — validates payload, dispatches to
    sinks. No persistence.
  • main/services/feedback-sink-github.ts — thin Octokit wrapper,
    the only file that touches the GitHub PAT.
  • main/services/feedback-sink-fider.ts — thin fetch wrapper,
    the only file that touches the Fider API key.
  • main/routes/feedback.tsPOST /api/feedback, returns 200 on
    success, 502/503 on sink failure, 503 on no sink configured.
  • frontend/src/components/feedback/FeedbackDialog.tsx — Radix
    Dialog, three category chips, textarea, email, Send, offline
    message, error states.
  • frontend/src/components/help/HelpMenu.tsx — add the entry.
  • frontend/src/components/settings/SupportSection.tsx — new card.
  • frontend/src/lib/api/feedback.ts — typed client.
  • frontend/src/lib/i18n/locales/{en,es}/feedback.json — strings.
  • tests/feedback.test.ts — validation, sink dispatch, offline
    payload, 5xx/4xx handling, no-sink → 503.

Acceptance criteria

  • A user without a GitHub account can submit Praise, Issue, or
    Idea from inside the app, online.
  • Offline users see a clear message asking them to connect to
    the internet, and the Send button is disabled.
  • Both sinks work, gated by env var. The feature is inert until
    the operator wires at least one credential.
  • Fider sink creates a post whose title includes [FloCafe] and
    the kind, so the maintainer can filter.
  • No PII: no order contents, no customer names, no unredacted
    logs, store id is hashed.
  • A11y: focus trap, ESC closes, role="dialog", visible focus
    ring.
  • npm test, npm run lint, npm run build,
    npm run build:frontend all pass.

Heads up for @khaira777 and @itsbkm

Before I touch any code on this, I want to ask which way you'd rather
go on the Fider side.

The plan above proposes Fider Cloud free (250-item cap, Fider team
runs the infra). The alternative is self-hosted Fider via Docker
Compose + Postgres, on your own infrastructure.

If self-host, two open-source tools make the deploy easy:
Dokploy (35k stars) and
Coolify (59k stars, 280+
one-click services). Either gives you a Heroku-style dashboard for
the Fider container and Postgres without hand-rolling docker-compose.

The trade is "free + someone else's infra" vs "free + you run the
thing". I'd rather hear from both of you before I pick. Do you want
this on your own infra (and if so, would you use Dokploy, Coolify,
or something else), or is the free cloud tier good enough? Is there
a reason I'm missing?

Open questions

  • One Fider board per FloCafe instance, or one shared feedback.fider.io
    board for the whole community? Default plan: shared board, posts
    filtered by store_id_hash.
  • Should the email field ever leave the dialog? Default plan: no,
    v1 is one-way. The maintainer replies via the Fider post or the
    GitHub issue.
  • Should we also add an offline queue in v2? Default plan: only if
    real users complain, and only after we measure how many POS
    installs run on truly offline networks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions