# Config PR Summary

> Platform structure: ConfigPRSummary (a proposed config change in plain language) and ModuleRegistry (installed modules).

Component · AI · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/ConfigPRSummary/)

Platform structure: `ConfigPRSummary` (a proposed config change in plain language) and `ModuleRegistry` (installed modules).

ConfigPRSummary: `title`, `meta`, `status` + `statusLabel`, `summary`, `items` ({kind: "project" | "team" | "agent" | "tool" | "trigger" | "file", name, detail, op: "add" | "change" | "remove"}), `actions`.
ModuleRegistry: `items` ({id, name, version, text, icon, core, enabled, adds}), `onToggle(module)`, `coreLabel`.

## Example

```tsx
import * as React from "react";
import { Button, ConfigPRSummary, ModuleRegistry } from "@twentyfirstmedia/pro";

export default function Preview() {
  return (
    <div
      className="tf-grid"
      style={
        {
          "--cols": "1fr 1fr",
          gap: 16,
          alignItems: "start",
        } as React.CSSProperties
      }
    >
      <ConfigPRSummary
        title="New team: Support for Chatteo"
        meta="Proposed by Main Brain · PR #212"
        status="signal"
        statusLabel="Needs review"
        summary="Adds a support team with two agents. Both start in drafts-only mode."
        items={[
          {
            kind: "team",
            name: "chatteo/support",
          },
          {
            kind: "agent",
            name: "Iris",
            detail: "Mailbox (read, draft) · Knowledge base",
          },
          {
            kind: "agent",
            name: "Nora",
            detail: "WhatsApp (reply within 24 h)",
          },
          {
            kind: "trigger",
            name: "New mail to hello@chatteo.de",
            op: "change",
          },
        ]}
        actions={[
          <Button key="v" variant="secondary" size="sm">
            View PR
          </Button>,
          <Button key="m" size="sm" icon="check">
            Approve
          </Button>,
        ]}
      />
      <div className="tf-panel tf-panel-flush tf-panel-list">
        <ModuleRegistry
          onToggle={() => {}}
          items={[
            {
              id: "chats",
              name: "Chats",
              version: "1.4.0",
              text: "Agents and people in one place",
              core: true,
              icon: "mail",
            },
            {
              id: "crm",
              name: "CRM",
              version: "0.9.2",
              text: "Companies, contacts, pipeline",
              enabled: true,
              icon: "users",
              adds: ["Nav", "Tool", "Jobs"],
            },
            {
              id: "brand",
              name: "Brand studio",
              version: "0.6.0",
              text: "Brand packages for customers",
              enabled: false,
              icon: "sparkle",
              adds: ["Nav", "Tool"],
            },
          ]}
        />
      </div>
    </div>
  );
}
```

## Props

### Button

| Prop | Type | Notes |
| --- | --- | --- |
| `variant?` | `"primary" \| "secondary" \| "soft" \| "ghost" \| "danger" \| "danger-solid"` |  |
| `size?` | `"md" \| "sm"` |  |
| `icon?` | `IconName` |  |
| `iconRight?` | `IconName` |  |
| `disabled?` | `boolean` |  |
| `type?` | `"button" \| "submit" \| "reset"` |  |
| `onClick?` | `(e: MouseEvent<HTMLButtonElement>) => void` |  |
| `children?` | `ReactNode` |  |
| `asChild?` | `boolean` | Render the single child element (a link, a router Link) with the button's look instead of a <button>. |
| `id?` | `string` |  |
| `name?` | `string` | Form field name and value, sent with the form when this button submits it. |
| `value?` | `string` |  |
| `form?` | `string` | The id of the form this button belongs to, when it sits outside it. |
| `title?` | `string` |  |
| `tabIndex?` | `number` |  |
| `autoFocus?` | `boolean` |  |
| `aria-label?` | `string` |  |
| `aria-labelledby?` | `string` |  |
| `aria-describedby?` | `string` |  |
| `aria-expanded?` | `boolean \| "true" \| "false"` | For disclosure and menu triggers: whether what the button controls is open. |
| `aria-controls?` | `string` |  |
| `aria-haspopup?` | `boolean \| "true" \| "false" \| "menu" \| "listbox" \| "tree" \| "grid" \| "dialog"` |  |
| `aria-pressed?` | `boolean \| "true" \| "false" \| "mixed"` | For toggle buttons. |
| `aria-current?` | `boolean \| "true" \| "false" \| "page" \| "step" \| "location" \| "date" \| "time"` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### ConfigPRSummary

| Prop | Type | Notes |
| --- | --- | --- |
| `title` | `string` | Required |
| `meta?` | `ReactNode` |  |
| `status?` | `Status` |  |
| `statusLabel?` | `string` |  |
| `summary?` | `ReactNode` |  |
| `items` | `{ kind: "project" \| "team" \| "agent" \| "tool" \| "trigger" \| "file"; name: string; detail?: ReactNode; op?: "add" \| "change" \| "remove"; }[]` | Required |
| `actions?` | `ReactNode[]` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### ModuleRegistry

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `{ id: string; name: string; version?: string; text?: ReactNode; icon?: IconName; core?: boolean; enabled?: boolean; adds?: string[]; }[]` | Required |
| `onToggle?` | `(m: NonNullable<ModuleRegistryProps["items"]>[number]) => void` |  |
| `coreLabel?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
