EchoSort is a full-stack User Feedback Intelligence System built for fast triage and internal ticket handling.
The product is intentionally no-auth for demo speed, and uses workspace isolation (cookie + DB scoping) so each visitor sees an independent dataset.
- Collects feedback from
/submit. - Enriches each ticket with AI classification (sentiment, priority, category, assigned team).
- Stores enriched tickets in MongoDB via Prisma.
- Surfaces tickets in
/dashboardwith search, filters, trends, and status workflows. - Supports internal operations on each ticket (details page, assignee, status transitions, comments, activity log).
- Sends routed team notifications by email (SMTP).
- Supports AI-assisted reminder drafting before send.
/submit: end-user feedback intake./dashboard: list, filters, KPIs, trend chart, stale-ticket reminder workflow./dashboard/tickets/[id]: full ticket operations and history./dashboard/settings: team email routing, theme settings, and workspace reset.
- Next.js 16 (App Router, TypeScript strict)
- Tailwind CSS v4 + shadcn/ui + Framer Motion + Lucide
- Prisma ORM + MongoDB Atlas
- LangChain.js +
@langchain/google-genai(gemini-3-flash-preview) - Nodemailer (SMTP)
- TanStack Table + Nivo Line Chart
- Vitest + Testing Library
Feedback analysis uses LangChain structured output with Zod schema.
Extracted fields:
sentiment:positive | neutral | negativepriority:Low | Medium | High | Urgent | Criticalcategory:Bug | Feature | UI | Billing | OtherassignedTeam:Engineering | Product | Finance | Operations
Implementation references:
src/lib/ai/feedback-analyzer.tssrc/lib/schemas/feedback.ts
Behavior guarantees:
- Invalid input is rejected by Zod before persistence.
- AI failure does not block persistence.
- On AI failure, safe fallback metadata is stored with
analysisStatus=FAILED.
Ticket states:
New,InProgress,Pending,Resolved,Closed
Operational capabilities:
- Update status with reason/summary where needed.
- Assign owner (
assigneeName). - Add internal comments.
- Track activities and reminder history.
- Draft reminder email with AI, then edit and send manually.
EchoSort uses anonymous workspace IDs to isolate data without login:
- A
es_workspaceHTTP-only cookie is set automatically. FeedbackandNotificationSettingsare scoped byworkspaceId.- Dashboard queries and ticket actions always resolve current workspace first.
- A "Start New Workspace" action rotates the cookie for a fresh empty view.
Feedback submission is protected with server-side rate limiting to prevent Gemini API abuse:
- Enforced before AI/email pipeline in
createFeedbackAction. - Identity key is derived from client IP only.
- Counters are shared in MongoDB (
RateLimitBucket) for Vercel serverless consistency. - Default policy:
- 6 submissions per minute
- 40 submissions per hour
- On limit hit, action returns app-level
RATE_LIMITED+retryAfterSeconds. - If limiter storage fails, policy is fail-open (request allowed, warning logged).
Key files:
src/lib/rate-limit/client-ip.tssrc/lib/rate-limit/identity.tssrc/lib/rate-limit/submission-limiter.tssrc/app/actions/feedback-actions.ts
Primary models:
FeedbackNotificationSettingsFeedbackCommentFeedbackActivityFeedbackReminder
Schema reference:
prisma/schema.prisma
Create .env in project root and keep it private.
DATABASE_URL="mongodb+srv://<dbUser>:<urlEncodedPassword>@<cluster-host>/echosort?retryWrites=true&w=majority&appName=<clusterName>"
GOOGLE_API_KEY="<your_google_ai_studio_key>"
GEMINI_MODEL="gemini-3-flash-preview"
RATE_LIMIT_SUBMIT_PER_MINUTE="6"
RATE_LIMIT_SUBMIT_PER_HOUR="40"
RATE_LIMIT_KEY_SALT="<long_random_secret>"
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="465"
SMTP_SECURE="true"
SMTP_USER="<your_gmail_address>"
SMTP_PASS="<your_google_app_password>"
SMTP_FROM="EchoSort <your_gmail_address>"Notes:
SMTP_FROMis optional; fallback isEchoSort <SMTP_USER>.- Reporter email is used as
replyTofor team notifications. RATE_LIMIT_KEY_SALTshould be set in production for stronger IP hash privacy.- Never commit
.env.
npm install
npx prisma generate
npx prisma db push
npm run devOpen:
http://localhost:3000/submithttp://localhost:3000/dashboardhttp://localhost:3000/dashboard/settings
src/
app/
submit/
dashboard/
settings/
tickets/[id]/
actions/
components/
dashboard/
tickets/
settings/
submit/
theme/
ui/
lib/
ai/
dashboard/
email/
feedback/
rate-limit/
schemas/
settings/
tickets/
workspace/
prisma/
tests/
scripts/