feat: free setting, replies that reach the customer, and an inbox composer - #364
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
The hero trace, the SEO description and two assertions typed the chapter price out by hand, so changing PRICING left them quoting the old figure — the description worst of all, since it is the price a search result shows somebody who has not opened the site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A chapter nobody stocks now costs what the same chapter off the shelf costs. The line stays in the quote at "free" rather than vanishing — the labour is worth naming, not hiding in a total. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A conversation's correspondent came off the From header, so the academy's work orders — sent from our sending alias with the customer in Reply-To — offered to reply to ourselves. Repairing a body now re-derives it too, which heals the threads already filed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The console could only answer mail, which is not enough when the job is sending a sheet to somebody who has never written in. A composed message opens a conversation keyed the way an inbound reply is keyed, so their answer lands in it rather than beside it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The browser suite asserts the totals the page prints, and those moved when the setting fee did. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kartikeya-27
force-pushed
the
feat/pricing-and-inbox-compose
branch
from
September 22, 2026 06:23
c933d36 to
bd8e933
Compare
pratyush618
approved these changes
Sep 22, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four changes, in the order they were asked for.
Setting a made-to-order chapter is free
PRICING.settinggoes from149to0. A chapter nobody stocks now costs exactly what the same chapter off the shelf costs, so "not in the inventory" stops being a surcharge. The quote keeps the line rather than dropping it — "we wrote this one for you and did not charge for it" is worth saying, and it renders asfreebeside the board questions and the NCERT exercise. The screenshot's ₹168 total becomes ₹19.The number stays in
pricing.tsrather than being deleted with the machinery around it: the setting fee is charged once per request and never per copy, and that rule is worth keeping intact for the day it is priced again.Chapter (₹19) and the subject/board/HOTS sets (₹199/₹499/₹699) are unchanged.
First, one place to change it
The hero trace, the SEO description and two test assertions typed the chapter price out by hand, so changing
PRICINGleft them quoting the old figure. The description is the worst of the three — it is what a search result and a link preview show somebody who has not opened the site yet. All four now read the price list, which is what made the change above a one-line edit.Replies went to our own alias instead of to the customer
A conversation's correspondent came off the
Fromheader and nothing ever looked atReply-To. The academy sends its work orders from its own sending alias with the customer inReply-To, so the console offered to reply to ByteVeda, from ByteVeda, about a sheet a student was waiting for.replyTargetOfis the rule:Reply-Towhen it is present and parses to a real address that is not the sender again, otherwiseFrom. Header names are matched case-insensitively, only the first address of several is taken, andinbound_messages.from_emailstill records who actually sent it — the row is the record of what arrived, and only the conversation's correspondent is the reply target.The thread key stays keyed on the sender, so nothing already filed is re-grouped.
This heals the threads already in the inbox. The headers are stored, and
repairBody— which backfills a message fetched after the fact — now re-derives the correspondent alongside them. It only ever narrows towards the truth: a message with noReply-Torewrites the correspondent to what it already was.Composing a new message
The console could only answer mail. That stops being enough the moment the job is sending a sheet: the work order names a customer who has never emailed this inbox, and reaching them meant another mail client and no record of it here.
A Compose button in the inbox header opens a dialog with a from-address, a recipient, a subject, a body and the same attachment picker the reply box uses. What it sends opens a conversation keyed the way an inbound reply will be keyed, so their answer lands in it rather than starting a second thread beside it.
composeMessagere-checks the chosen address against it rather than trusting the form. Somebody with only the academy's mail cannot send as ByteVeda.compose:<draft id>scope, because there is no conversation to key them to until the send happens. The draft id is minted in the browser and survives a reload with the rest of the draft, so a file uploaded before the address was decided still belongs to the message that goes out.outbound_messagespoints at it by foreign key, and a failed attempt belongs in a conversation somebody can open, with its error on it.Checks
pnpm -r typecheckclean. 229 admin tests and 32 academy tests pass, including six new ones forreplyTargetOfcovering the academy's exact shape, header capitalisation, multiple addresses and the ignore cases.next buildclean for both apps. Biome clean repo-wide.Not visually verified — both surfaces are behind a login.
Note for whoever merges second
This adds
.dialog-head,.dialog-sub,.dialog-close,.dialog-bodyand.dialog-statustoadmin.css, and scopes.dialog-card pto.dialog-card > p. #363 adds the same block for its own dialogs. Expect a conflict there; keeping either copy is correct, they are identical.🤖 Generated with Claude Code