Concept approval

Round-of-concepts review surface β€” version eyebrow, file carousel, header actions, and an optional comment thread with a POST-backed composer.

V2posted 2h ago by Marisol

Summer at the Movies β€” Concepts

Title Card
Stage Graphic
Social Tile

Title Card

MV

Here's Round 2. We leaned into the 70s marquee reference, pushed the italic coral pop on "Summer", and softened the gradient behind it. Let me know where to tighten.

8:44 PM
SR

Looks great! Adding a couple pins.

9:09 PM
SR
Marisol VegaHere's Round 2. We leaned into the 70s marquee reference…

Love the marquee β€” can we bump the coral one more notch so it pops on dark backgrounds?

9:14 PM
MV
Sarah ReyesLove the marquee β€” can we bump the coral one more notch?

Totally. I'll push it toward the brand coral and re-export the social tile too.

9:34 PM
DC

Stage graphic reads really well from the back of the room πŸ‘

stage-preview.jpg
9:49 PM
SR

Agreed. One nit: tighten the leading on the subtitle so it sits closer to the headline.

10:04 PM
MV

Done β€” leading tightened and V2.1 is uploading now. Should land in a few minutes.

10:19 PM
DC

Nice. Once Sarah signs off I'll queue it for the Friday send.

10:32 PM
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/concept-approval.json
Usage
Drop the block into a review page. Pass a `commentEndpoint` to POST comments; omit `commentsEnabled` to disable the thread.
TSImport
import { ConceptApproval } from "@/components/blocks/concept-approval";
TSExample
import { CheckIcon } from "lucide-react";
import { ConceptApproval } from "@/components/blocks/concept-approval";

export function ReviewPage() {
  return (
    <ConceptApproval
      version={2}
      versions={[1, 2]}
      title="Summer at the Movies β€” Concepts"
      files={[
        { id: "title", kind: "image", src: "/concepts/title.jpg", label: "Title Card" },
      ]}
      actions={[
        { id: "approve", label: "Approve", icon: <CheckIcon />, onClick: () => {} },
      ]}
      commentEndpoint="/api/concepts/round-2/comments"
    />
  );
}
Composition
A single ConceptApproval component with prop-driven header, carousel, and comment surfaces.
ConceptApproval                  β€” review surface for a round of concepts
β”œβ”€β”€ header
β”‚   β”œβ”€β”€ version eyebrow          β€” "Round 2 Β· posted 2h ago by Marisol"
β”‚   β”œβ”€β”€ title                    β€” heading next to the eyebrow
β”‚   └── actions[]                β€” Download / Revise / Approve buttons
β”œβ”€β”€ carousel                     β€” images or videos with arrows + dots
β”‚   └── label                    β€” caption under the active file
└── comments (commentsEnabled)    β€” rendered with the Messaging <Chat> block
    β”œβ”€β”€ thread                   β€” comments[] as ChatMessage (replies, reactions, attachments)
    └── composer                 β€” Chat composer; onSend POSTs to commentEndpoint
Preview
Full block with version eyebrow, action row, file carousel, existing thread, and composer.
V2posted 2h ago by Marisol

Summer at the Movies β€” Concepts

Title Card
Stage Graphic
Social Tile

Title Card

MV

Here's Round 2. We leaned into the 70s marquee reference, pushed the italic coral pop on "Summer", and softened the gradient behind it. Let me know where to tighten.

8:44 PM
SR

Looks great! Adding a couple pins.

9:09 PM
SR
Marisol VegaHere's Round 2. We leaned into the 70s marquee reference…

Love the marquee β€” can we bump the coral one more notch so it pops on dark backgrounds?

9:14 PM
MV
Sarah ReyesLove the marquee β€” can we bump the coral one more notch?

Totally. I'll push it toward the brand coral and re-export the social tile too.

9:34 PM
DC

Stage graphic reads really well from the back of the room πŸ‘

stage-preview.jpg
9:49 PM
SR

Agreed. One nit: tighten the leading on the subtitle so it sits closer to the headline.

10:04 PM
MV

Done β€” leading tightened and V2.1 is uploading now. Should land in a few minutes.

10:19 PM
DC

Nice. Once Sarah signs off I'll queue it for the Friday send.

10:32 PM
Comments disabled
Pass `commentsEnabled={false}` to hide the thread and composer entirely.
V2posted 2h ago by Marisol

Summer at the Movies β€” Concepts

Title Card
Stage Graphic
Social Tile

Title Card

Single file
With a single file the carousel arrows and pagination dots are hidden.
V1just posted

Logo lockup β€” first pass

Title Card

Title Card

MV

Here's Round 2. We leaned into the 70s marquee reference, pushed the italic coral pop on "Summer", and softened the gradient behind it. Let me know where to tighten.

8:44 PM
API Reference
Props for the block plus the shape of file, comment, and action entries.

ConceptApproval

PropTypeDefaultDescription
versionnumberβ€”Current version number β€” rendered in the eyebrow badge as `V{n}`.
versionsnumber[]β€”All available versions. When more than one, version-switching tabs render in the top-right of the header.
onVersionChange(version: number) => voidβ€”Called when the user picks a different version from the tabs.
titleReact.ReactNodeβ€”Heading next to the version chip.
subtitleReact.ReactNodeβ€”Inline metadata after the version ("posted 2h ago by Marisol").
filesConceptApprovalFile[]β€”Images / videos shown in the carousel.
actionsConceptApprovalAction[]β€”Buttons rendered in the header (Download / Revise / Approve).
commentsEnabledbooleantrueShow or hide the comment thread + composer.
commentsChatMessage[]β€”The feedback thread β€” the Messaging block's `ChatMessage[]` shape (inline attachments / reactions / replyTo). Rendered with `<Chat>`.
commentEndpointstringβ€”POST URL for new comments. Receives `{ body, version, fileId }`.
onSubmitComment(c) => void | Promise<void>β€”Called after a successful submit, with `{ body, version }`.
currentUserIdstring"you"Id of the viewer β€” own messages align right; drives reaction state.
currentUserNamestring"You"Display name stamped on messages you send.
placeholderstring"Leave feedback for your Squad…"Composer placeholder.
classNamestringβ€”Extra classes merged onto the outer section.

ConceptApprovalFile

PropTypeDefaultDescription
idstringβ€”Stable id for keying and comment payloads.
kind"image" | "video""image"Render an <img> or a controlled <video>.
srcstringβ€”Asset URL.
posterstringβ€”Poster image for videos.
labelstringβ€”Caption shown under the active file in the carousel.
altstringβ€”Image alt text. Falls back to the label.

ConceptApprovalComment

PropTypeDefaultDescription
idstringβ€”Stable id.
senderIdstringβ€”Matched against `currentUserId` β€” own messages align right.
senderNamestringβ€”Author name β€” initials and avatar color are derived from this.
senderAvatarstring | nullβ€”Optional photo URL (falls back to initials).
textstringβ€”Message body.
timestampDate | string | numberβ€”When the message was posted β€” auto-formatted into a time.
attachmentsChatAttachment[]β€”`{ id, name, url?, size?, type? }` β€” images preview inline.
reactionsChatReaction[]β€”`{ emoji, users: string[] }` β€” count = users.length.
replyToChatReplyRefβ€”`{ id, senderName, text }` β€” quoted parent message.

ConceptApprovalAction

PropTypeDefaultDescription
idstringβ€”Stable key for the action.
labelstringβ€”Button label.
iconReact.ReactNodeβ€”Optional lucide icon.
onClick() => voidβ€”Click handler.
variantButtonProps["variant"]"outline"Forwarded to the squad-ui Button.
sizeButtonProps["size"]"default"Forwarded to the squad-ui Button.