{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "student-gradebook-table",
  "title": "Student Gradebook Table",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/student-gradebook-table/student-gradebook-table.component.ts",
      "content": "// Academic gradebook ledger with course header, weighted assignment categories, class GPA\n// and enrollment metric cards, overall percentage, letter grade badges, attendance tags, and\n// student action menus. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input } from '@angular/core'\nimport {\n  CalendarCheck,\n  ClipboardCheck,\n  Download,\n  GraduationCap,\n  LucideAngularModule,\n  Mail,\n  MoreHorizontal,\n  Pencil,\n  Plus,\n  User,\n  Users,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport {\n  UiDropdownMenuComponent,\n  UiDropdownMenuContentComponent,\n  UiDropdownMenuItemComponent,\n  UiDropdownMenuSeparatorComponent,\n  UiDropdownMenuTriggerComponent,\n} from '@/ui/dropdown-menu/dropdown-menu.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport type LetterGrade = 'A+' | 'A' | 'A-' | 'B+' | 'B' | 'B-'\n\nexport interface StudentGradeRecord {\n  id: string\n  name: string\n  studentId: string\n  homework: string\n  midterm: string\n  labProjects: string\n  finalProject: string\n  overallGrade: string\n  letterGrade: LetterGrade\n  attendanceRate: string\n  absences: number\n}\n\nconst metrics = [\n  {\n    title: 'Class GPA',\n    value: '3.62 / 4.0',\n    description: '+0.18 vs departmental target',\n    icon: GraduationCap,\n    iconClass: 'bg-success/10 text-success',\n    valueClass: 'text-success',\n  },\n  {\n    title: 'Total Enrolled Students',\n    value: '28 students',\n    description: '100% active roster',\n    icon: Users,\n    iconClass: 'bg-info/10 text-info',\n    valueClass: 'text-foreground',\n  },\n  {\n    title: 'Assignments Graded',\n    value: '12 / 14 assignments',\n    description: '2 pending final submissions',\n    icon: ClipboardCheck,\n    iconClass: 'bg-chart-1/10 text-chart-1',\n    valueClass: 'text-foreground',\n  },\n  {\n    title: 'Class Attendance Rate',\n    value: '96.2% average',\n    description: '36 lectures & labs logged',\n    icon: CalendarCheck,\n    iconClass: 'bg-warning/10 text-warning',\n    valueClass: 'text-foreground',\n  },\n]\n\nconst gradeBadgeConfig: Record<LetterGrade, { class: string }> = {\n  'A+': { class: 'bg-success/10 text-success border-success/20 font-semibold' },\n  A: { class: 'bg-success/10 text-success border-success/20 font-semibold' },\n  'A-': { class: 'bg-success/10 text-success border-success/20 font-semibold' },\n  'B+': { class: 'bg-info/10 text-info border-info/20 font-semibold' },\n  B: { class: 'bg-warning/10 text-warning border-warning/20 font-semibold' },\n  'B-': { class: 'bg-warning/10 text-warning border-warning/20 font-semibold' },\n}\n\nconst students: StudentGradeRecord[] = [\n  {\n    id: 's1',\n    name: 'Elena Rostova',\n    studentId: '#STU-84920',\n    homework: '95.0%',\n    midterm: '92.0%',\n    labProjects: '98.0%',\n    finalProject: '94.0%',\n    overallGrade: '94.5%',\n    letterGrade: 'A',\n    attendanceRate: '100%',\n    absences: 0,\n  },\n  {\n    id: 's2',\n    name: 'David Chen',\n    studentId: '#STU-84921',\n    homework: '88.0%',\n    midterm: '91.0%',\n    labProjects: '86.0%',\n    finalProject: '89.0%',\n    overallGrade: '88.6%',\n    letterGrade: 'B+',\n    attendanceRate: '96%',\n    absences: 1,\n  },\n  {\n    id: 's3',\n    name: 'Sarah Jenkins',\n    studentId: '#STU-84922',\n    homework: '98.0%',\n    midterm: '96.0%',\n    labProjects: '100.0%',\n    finalProject: '95.0%',\n    overallGrade: '97.1%',\n    letterGrade: 'A',\n    attendanceRate: '100%',\n    absences: 0,\n  },\n  {\n    id: 's4',\n    name: 'Marcus Vance',\n    studentId: '#STU-84923',\n    homework: '82.0%',\n    midterm: '79.0%',\n    labProjects: '85.0%',\n    finalProject: '80.0%',\n    overallGrade: '81.4%',\n    letterGrade: 'B',\n    attendanceRate: '92%',\n    absences: 2,\n  },\n  {\n    id: 's5',\n    name: 'Sofia Rossi',\n    studentId: '#STU-84924',\n    homework: '91.0%',\n    midterm: '88.0%',\n    labProjects: '94.0%',\n    finalProject: '90.0%',\n    overallGrade: '90.6%',\n    letterGrade: 'A-',\n    attendanceRate: '98%',\n    absences: 1,\n  },\n  {\n    id: 's6',\n    name: 'Alex Rivera',\n    studentId: '#STU-84925',\n    homework: '84.0%',\n    midterm: '86.0%',\n    labProjects: '82.0%',\n    finalProject: '85.0%',\n    overallGrade: '84.3%',\n    letterGrade: 'B',\n    attendanceRate: '94%',\n    absences: 2,\n  },\n]\n\nfunction initials(name: string): string {\n  return name\n    .split(' ')\n    .map((part) => part[0])\n    .slice(0, 2)\n    .join('')\n    .toUpperCase()\n}\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-student-gradebook-table, [ui-student-gradebook-table]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiDropdownMenuComponent,\n    UiDropdownMenuContentComponent,\n    UiDropdownMenuItemComponent,\n    UiDropdownMenuSeparatorComponent,\n    UiDropdownMenuTriggerComponent,\n    UiTableComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <div data-slot=\"student-gradebook-table\" [class]=\"rootClass\">\n      <!-- Header -->\n      <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div>\n          <div class=\"flex flex-wrap items-center gap-2.5\">\n            <h2 class=\"text-2xl font-bold tracking-tight\">CS 301: Advanced Software Systems &amp; Compilers</h2>\n            <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">CS-301-B</span>\n          </div>\n          <div class=\"text-muted-foreground mt-1.5 flex flex-wrap items-center gap-2 text-sm\">\n            <span>Fall 2026 · Section B</span>\n            <span class=\"text-foreground inline-flex items-center gap-1 font-medium\">\n              Class Average: <span class=\"tabular-nums\">88.4%</span> · Grade B+\n            </span>\n          </div>\n        </div>\n        <div class=\"flex flex-wrap items-center gap-2.5\">\n          <button ui-button aria-label=\"Download attachment\" variant=\"outline\">\n            <lucide-icon [img]=\"Download\" class=\"size-4\" />\n            Export Grades CSV\n          </button>\n          <button ui-button>\n            <lucide-icon [img]=\"Plus\" class=\"size-4\" />\n            New Grade Entry\n          </button>\n        </div>\n      </div>\n\n      <!-- 4 Class Overview Metric Cards -->\n      <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        @for (metric of metrics; track metric.title) {\n          <div ui-card class=\"shadow-xs\">\n            <div ui-card-content class=\"space-y-2 p-5\">\n              <div class=\"flex items-center justify-between\">\n                <p class=\"text-muted-foreground text-sm font-medium\">{{ metric.title }}</p>\n                <div [class]=\"metricIconClass(metric.iconClass)\">\n                  <lucide-icon [img]=\"metric.icon\" class=\"size-4\" />\n                </div>\n              </div>\n              <div>\n                <p [class]=\"metricValueClass(metric.valueClass)\">{{ metric.value }}</p>\n                <p class=\"text-muted-foreground mt-1 text-xs\">{{ metric.description }}</p>\n              </div>\n            </div>\n          </div>\n        }\n      </div>\n\n      <!-- Gradebook Table -->\n      <div class=\"bg-card overflow-x-auto rounded-lg border shadow-xs\">\n        <table ui-table>\n          <thead ui-table-header>\n            <tr ui-table-row>\n              <th ui-table-head>Student</th>\n              <th ui-table-head class=\"text-right\">Homework (25%)</th>\n              <th ui-table-head class=\"text-right\">Midterm (30%)</th>\n              <th ui-table-head class=\"text-right\">Lab Projects (25%)</th>\n              <th ui-table-head class=\"text-right\">Final Project (20%)</th>\n              <th ui-table-head class=\"text-right font-semibold\">Overall Grade</th>\n              <th ui-table-head class=\"text-center\">Letter Grade</th>\n              <th ui-table-head>Attendance</th>\n              <th ui-table-head class=\"w-12 text-right\">\n                <span class=\"sr-only\">Actions</span>\n              </th>\n            </tr>\n          </thead>\n          <tbody ui-table-body>\n            @for (student of students; track student.id) {\n              <tr ui-table-row class=\"hover:bg-muted/50\">\n                <td ui-table-cell>\n                  <div class=\"flex items-center gap-3\">\n                    <ui-avatar class=\"size-8\">\n                      <ui-avatar-fallback class=\"text-xs font-medium\">{{ initials(student.name) }}</ui-avatar-fallback>\n                    </ui-avatar>\n                    <div class=\"min-w-0\">\n                      <p class=\"truncate text-sm font-semibold\">{{ student.name }}</p>\n                      <p class=\"text-muted-foreground font-mono text-xs\">{{ student.studentId }}</p>\n                    </div>\n                  </div>\n                </td>\n                <td ui-table-cell class=\"text-right font-medium tabular-nums\">{{ student.homework }}</td>\n                <td ui-table-cell class=\"text-right font-medium tabular-nums\">{{ student.midterm }}</td>\n                <td ui-table-cell class=\"text-right font-medium tabular-nums\">{{ student.labProjects }}</td>\n                <td ui-table-cell class=\"text-right font-medium tabular-nums\">{{ student.finalProject }}</td>\n                <td ui-table-cell class=\"text-foreground text-right font-bold tabular-nums\">\n                  {{ student.overallGrade }}\n                </td>\n                <td ui-table-cell class=\"text-center\">\n                  <span ui-badge variant=\"outline\" [class]=\"gradeBadgeClass(student.letterGrade)\">\n                    {{ student.letterGrade }}\n                  </span>\n                </td>\n                <td ui-table-cell>\n                  <div class=\"inline-flex items-center gap-1.5 text-xs\">\n                    <span [class]=\"attendanceDotClass(student.absences)\" aria-hidden=\"true\"></span>\n                    <span class=\"text-foreground font-medium tabular-nums\">{{ student.attendanceRate }}</span>\n                    <span class=\"text-muted-foreground\">· {{ absenceLabel(student.absences) }}</span>\n                  </div>\n                </td>\n                <td ui-table-cell class=\"text-right\">\n                  <ui-dropdown-menu>\n                    <button\n                      ui-button\n                      ui-dropdown-menu-trigger\n                      variant=\"ghost\"\n                      size=\"icon-sm\"\n                      class=\"text-muted-foreground\"\n                    >\n                      <lucide-icon [img]=\"MoreHorizontal\" class=\"size-4\" />\n                      <span class=\"sr-only\">Open actions for {{ student.name }}</span>\n                    </button>\n                    <ui-dropdown-menu-content align=\"end\" class=\"w-48\">\n                      <ui-dropdown-menu-item>\n                        <lucide-icon [img]=\"Pencil\" class=\"size-4\" />\n                        <span>Edit Grades</span>\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-item>\n                        <lucide-icon [img]=\"User\" class=\"size-4\" />\n                        <span>View Student Profile</span>\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-separator />\n                      <ui-dropdown-menu-item>\n                        <lucide-icon [img]=\"Mail\" class=\"size-4\" />\n                        <span>Send Email Alert</span>\n                      </ui-dropdown-menu-item>\n                    </ui-dropdown-menu-content>\n                  </ui-dropdown-menu>\n                </td>\n              </tr>\n            }\n          </tbody>\n        </table>\n      </div>\n\n      <!-- Table Footer / Pagination -->\n      <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n        <p class=\"text-muted-foreground text-sm\">\n          Showing <span class=\"text-foreground font-medium\">6</span> of\n          <span class=\"text-foreground font-medium\">28</span> enrolled students\n        </p>\n        <div class=\"flex items-center gap-2\">\n          <button ui-button variant=\"outline\" size=\"sm\" disabled>Previous</button>\n          <button ui-button variant=\"outline\" size=\"sm\">Next</button>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiStudentGradebookTableComponent {\n  protected readonly Download = Download\n  protected readonly Mail = Mail\n  protected readonly MoreHorizontal = MoreHorizontal\n  protected readonly Pencil = Pencil\n  protected readonly Plus = Plus\n  protected readonly User = User\n\n  protected readonly metrics = metrics\n  protected readonly students = students\n\n  @Input('class') className?: string\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  metricIconClass(iconClass: string): string {\n    return cn('flex size-8 items-center justify-center rounded-md', iconClass)\n  }\n\n  metricValueClass(valueClass: string): string {\n    return cn('text-2xl font-bold tracking-tight tabular-nums', valueClass)\n  }\n\n  gradeBadgeClass(grade: LetterGrade): string {\n    return cn('font-semibold', gradeBadgeConfig[grade].class)\n  }\n\n  attendanceDotClass(absences: number): string {\n    return cn('inline-block size-1.5 rounded-full', absences === 0 ? 'bg-success' : 'bg-warning')\n  }\n\n  absenceLabel(absences: number): string {\n    if (absences === 0) return '0 absences'\n    return `${absences} absence${absences > 1 ? 's' : ''}`\n  }\n\n  initials(name: string): string {\n    return initials(name)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/student-gradebook-table/student-gradebook-table.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/student-gradebook-table/index.ts",
      "content": "export {\n  UiStudentGradebookTableComponent,\n  type LetterGrade,\n  type StudentGradeRecord,\n} from './student-gradebook-table.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/student-gradebook-table/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/avatar.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/dropdown-menu.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Academic gradebook ledger with course header, weighted assignment categories (homework, midterm, labs, final project), class GPA & enrollment metric cards, overall percentage, letter grade badges, attendance tags, and student action menus.",
  "categories": [
    "education",
    "app",
    "data",
    "dashboard"
  ]
}