The comment thread uses one form with two hidden inputs to serve three entity types
The portal has inline comment threads on sessions, milestones, and goals. All three use the same CommentThread component. It takes four props — a comments array, an addCommentAction server action, an entityType string that is one of session, milestone, or goal, and an entityId string. The component does not branch on entity type. It does not know whether it sits below a mixing session or an accessibility audit milestone. Two hidden inputs inside the form carry the routing information. One input holds the entityType. The other holds the entityId. When the form submits through useActionState, the server action reads both values from the FormData and uses conditional spread operators to set exactly one foreign key on the comment record — sessionId, milestoneId, or goalId. The component passes strings through. The server decides where they go. The avatar next to each comment is five chained string methods inline in the JSX — split the authorName on spaces, map each word to its first character, join into a string, slice to two characters, uppercase. Sarah Chen becomes SC. James becomes JA. The circle is 32 pixels wide with a border that changes colour based on a single boolean. When isJames is true, the border and the name text use the portal accent colour. When isJames is false, both use the muted glass colour. Two visual signals from one database field. The timestamp uses Intl.DateTimeFormat with en-GB locale, day numeric, month short, hour and minute in 24-hour format. The opacity is set to fifty percent so the time recedes behind the comment body. The time element has a machine-readable ISO datetime attribute for crawlers and assistive technology. The form input is a single text field with a thousand-character maxLength, a 44-pixel minimum height for WCAG touch compliance, and a placeholder that reads Add a comment. The label is visually hidden with sr-only but present in the accessibility tree. The submit button disables during pending state and shows three dots instead of Send. On the project detail page, a single scroll can pass through five or six independent CommentThread instances — one per milestone, one per session, one per goal — each with its own form, its own hidden inputs, its own useActionState hook. They do not share state. They do not interfere. One component, two hidden inputs, three entity types, zero conditional rendering.
Comments coming soon
Sign in with TikTok to leave a comment. Coming soon.