{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "maintenance-request-ticket",
  "title": "Maintenance Request Ticket",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/maintenance-request-ticket/maintenance-request-ticket.component.ts",
      "content": "// Resident maintenance request ticket: category grid, urgency radios with an\n// emergency SLA banner, unit location select, description with quick snippets,\n// entry-permission radios, photo/video dropzone, draft + submit flow, and an\n// emergency-hotline / SLA / building-operations sidebar. Port of the Vue block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport {\n  AlertCircle,\n  Bug,\n  Building2,\n  Check,\n  CheckCircle2,\n  ChevronDown,\n  Clock,\n  Droplets,\n  FileImage,\n  FileVideo,\n  Flame,\n  KeyRound,\n  LucideAngularModule,\n  PhoneCall,\n  Plus,\n  RefreshCw,\n  ShieldAlert,\n  ShieldCheck,\n  Trash2,\n  UploadCloud,\n  UserCheck,\n  Wrench,\n  Zap,\n  type LucideIconData,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiRadioGroupComponent, UiRadioGroupItemComponent } from '@/ui/radio-group/radio-group.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectGroupComponent,\n  UiSelectItemComponent,\n  UiSelectLabelComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport { UiTextareaComponent } from '@/ui/textarea/textarea.component'\n\nexport interface AttachedFile {\n  id: string\n  name: string\n  size: string\n  type: 'image' | 'video'\n  uploadedAt: string\n}\n\nexport type MaintenanceUrgency = 'routine' | 'standard' | 'emergency'\nexport type EntryPermission = 'granted' | 'call_first'\n\ninterface IssueCategory {\n  id: string\n  label: string\n  description: string\n  icon: LucideIconData\n  badgeColor: string\n}\n\nconst categories: IssueCategory[] = [\n  {\n    id: 'plumbing',\n    label: 'Plumbing & Leaks',\n    description: 'Faucets, pipes, toilets, drains',\n    icon: Droplets,\n    badgeColor: 'text-info bg-info/10 text-info',\n  },\n  {\n    id: 'electrical',\n    label: 'Electrical & Lighting',\n    description: 'Outlets, switches, fixtures, breakers',\n    icon: Zap,\n    badgeColor: 'text-warning bg-warning/10 text-warning',\n  },\n  {\n    id: 'hvac',\n    label: 'HVAC & AC Heating',\n    description: 'AC, thermostat, heater, airflow',\n    icon: Flame,\n    badgeColor: 'text-chart-2 bg-chart-2/10',\n  },\n  {\n    id: 'appliances',\n    label: 'Appliances',\n    description: 'Dishwasher, oven, disposal, fridge',\n    icon: Wrench,\n    badgeColor: 'text-chart-1 bg-chart-1/10',\n  },\n  {\n    id: 'locks',\n    label: 'Doors & Locks',\n    description: 'Keys, latches, deadbolts, windows',\n    icon: KeyRound,\n    badgeColor: 'text-success bg-success/10 text-success',\n  },\n  {\n    id: 'pest',\n    label: 'Pest Control',\n    description: 'Insects, rodents, preventative traps',\n    icon: Bug,\n    badgeColor: 'text-destructive bg-destructive/10 text-destructive',\n  },\n]\n\ninterface UrgencyOption {\n  value: MaintenanceUrgency\n  label: string\n  badge: string\n  badgeVariant: 'outline' | 'secondary' | 'destructive'\n  description: string\n}\n\nconst urgencyOptions: UrgencyOption[] = [\n  {\n    value: 'routine',\n    label: 'Low / Routine',\n    badge: '3–5 Business Days',\n    badgeVariant: 'outline',\n    description: 'Non-urgent preventative maintenance or routine hardware adjustments.',\n  },\n  {\n    value: 'standard',\n    label: 'Medium / Standard 48h',\n    badge: '24–48h SLA',\n    badgeVariant: 'secondary',\n    description: 'Standard repair with moderate impact on daily convenience or appliance usage.',\n  },\n  {\n    value: 'emergency',\n    label: 'High / Emergency 2h SLA',\n    badge: 'Emergency < 2h',\n    badgeVariant: 'destructive',\n    description: 'Immediate hazard to property or habitability. Triggers 24/7 on-call dispatch.',\n  },\n]\n\nconst locationOptions: { value: string; label: string }[] = [\n  { value: 'kitchen', label: 'Kitchen' },\n  { value: 'master_bathroom', label: 'Master Bathroom' },\n  { value: 'guest_bathroom', label: 'Guest Bathroom' },\n  { value: 'living_room', label: 'Living Room' },\n  { value: 'balcony', label: 'Balcony / Patio' },\n  { value: 'master_bedroom', label: 'Master Bedroom' },\n  { value: 'guest_bedroom', label: 'Guest Bedroom' },\n  { value: 'hallway', label: 'Hallway / Entryway' },\n  { value: 'laundry', label: 'Laundry & Utility Closet' },\n]\n\nconst quickSnippets: string[] = [\n  'Motor humming but not spinning',\n  'Water leaking under sink',\n  'Reset button tripped',\n  'Slow draining',\n]\n\nconst defaultDescription =\n  'Kitchen sink garbage disposal is jammed and leaking slightly under the cabinet when water runs. Motor makes a low humming sound.'\n\nconst defaultFiles: AttachedFile[] = [\n  {\n    id: 'f-1',\n    name: 'kitchen-sink-leak-pipe.jpg',\n    size: '2.4 MB',\n    type: 'image',\n    uploadedAt: 'Today, 10:14 AM',\n  },\n  {\n    id: 'f-2',\n    name: 'disposal-motor-hum.mp4',\n    size: '8.1 MB',\n    type: 'video',\n    uploadedAt: 'Today, 10:16 AM',\n  },\n]\n\nconst maxChars = 500\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-maintenance-request-ticket, [ui-maintenance-request-ticket]',\n  standalone: true,\n  // The block renders its own root <div>: keep the host out of layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiRadioGroupComponent,\n    UiRadioGroupItemComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectGroupComponent,\n    UiSelectItemComponent,\n    UiSelectLabelComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiSeparatorComponent,\n    UiTextareaComponent,\n  ],\n  template: `\n    <div data-slot=\"maintenance-request-ticket\" [class]=\"rootClass\">\n      <!-- Header Banner -->\n      <div ui-card class=\"border-border shadow-xs\">\n        <div ui-card-content class=\"p-4 sm:p-6\">\n          <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex flex-wrap items-center gap-2.5\">\n                <h1 class=\"text-foreground text-xl font-bold tracking-tight sm:text-2xl\">Submit Maintenance Request</h1>\n                <span ui-badge variant=\"outline\" class=\"gap-1.5 font-mono text-xs\">\n                  <span class=\"bg-success size-1.5 rounded-full\"></span>\n                  Unit 4B · Pacific Heights\n                </span>\n              </div>\n              <p class=\"text-muted-foreground text-sm\">\n                Resident: <span class=\"text-foreground font-medium\">Elena Rostova</span> · Facility maintenance &amp;\n                repair portal\n              </p>\n            </div>\n\n            <div class=\"flex flex-wrap items-center gap-2.5\">\n              <div\n                class=\"bg-muted/60 border-border inline-flex items-center gap-2 rounded-lg border px-3 py-1.5 text-xs\"\n              >\n                <span class=\"relative flex size-2\">\n                  <span class=\"bg-warning absolute inline-flex size-full rounded-full opacity-75\"></span>\n                  <span class=\"bg-warning relative inline-flex size-2 rounded-full\"></span>\n                </span>\n                <span class=\"text-foreground font-medium\">1 In Progress</span>\n                <span class=\"text-muted-foreground hidden sm:inline\">· #MNT-8821</span>\n              </div>\n              <button\n                ui-button\n                variant=\"outline\"\n                size=\"sm\"\n                class=\"gap-1.5 shadow-xs\"\n                [attr.aria-expanded]=\"showOpenRequests()\"\n                (click)=\"showOpenRequests.set(!showOpenRequests())\"\n              >\n                <span>{{ showOpenRequests() ? 'Hide Open Requests' : 'View Open Requests' }}</span>\n                <lucide-icon [img]=\"ChevronDown\" [class]=\"openRequestsChevronClass\" />\n              </button>\n            </div>\n          </div>\n\n          <!-- Collapsible Active Work Orders Panel -->\n          @if (showOpenRequests()) {\n            <div class=\"border-border bg-muted/30 mt-4 space-y-3 rounded-lg border p-4 transition-colors\">\n              <div class=\"flex items-center justify-between\">\n                <div class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Clock\" class=\"text-muted-foreground size-4\" />\n                  <span class=\"text-foreground text-xs font-semibold tracking-wider uppercase\">\n                    Currently Active Work Orders (1)\n                  </span>\n                </div>\n                <span ui-badge variant=\"secondary\" class=\"text-xs\">Technician Assigned</span>\n              </div>\n              <div\n                class=\"bg-card border-border flex flex-col justify-between gap-3 rounded-md border p-3 sm:flex-row sm:items-center\"\n              >\n                <div class=\"space-y-1\">\n                  <div class=\"flex items-center gap-2\">\n                    <span class=\"text-foreground text-sm font-semibold\"\n                      >#MNT-8821 · HVAC Filter &amp; Thermostat Inspection</span\n                    >\n                    <span ui-badge variant=\"outline\" class=\"text-xs\">Living Room</span>\n                  </div>\n                  <p class=\"text-muted-foreground text-xs\">\n                    Assigned to: <span class=\"text-foreground font-medium\">Dave Miller (Lead HVAC Specialist)</span> ·\n                    Scheduled window: Today 2:00 PM – 4:00 PM\n                  </p>\n                </div>\n                <div class=\"flex shrink-0 items-center gap-2\">\n                  <span ui-badge variant=\"info\" class=\"text-xs\">In Progress</span>\n                </div>\n              </div>\n            </div>\n          }\n        </div>\n      </div>\n\n      <!-- Main 2-Column Layout -->\n      <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-12 lg:gap-8\">\n        <!-- Left Column: Work Order Form (8 cols) -->\n        <div class=\"space-y-6 lg:col-span-8\">\n          @if (isSubmitted()) {\n            <!-- Submission Success State -->\n            <div ui-card class=\"border-success/30 bg-success/5 dark:bg-success/10 shadow-xs\">\n              <div ui-card-content class=\"space-y-4 p-6 text-center sm:p-8\">\n                <div class=\"bg-success/10 text-success mx-auto flex size-14 items-center justify-center rounded-full\">\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"size-8\" />\n                </div>\n                <div class=\"space-y-2\">\n                  <h2 class=\"text-foreground text-xl font-bold\">Maintenance Request Submitted!</h2>\n                  <p class=\"text-muted-foreground mx-auto max-w-md text-sm\">\n                    Your request <span class=\"text-foreground font-mono font-semibold\">#{{ generatedTicketId() }}</span>\n                    has been logged and dispatched to the Pacific Heights Facility Operations team.\n                  </p>\n                </div>\n                <div class=\"border-border bg-card mx-auto max-w-lg space-y-2 rounded-lg border p-4 text-left text-xs\">\n                  <div class=\"flex justify-between\">\n                    <span class=\"text-muted-foreground\">Ticket ID:</span>\n                    <span class=\"text-foreground font-mono font-medium\">#{{ generatedTicketId() }}</span>\n                  </div>\n                  <div class=\"flex justify-between\">\n                    <span class=\"text-muted-foreground\">Unit:</span>\n                    <span class=\"text-foreground font-medium\">Unit 4B (Pacific Heights)</span>\n                  </div>\n                  <div class=\"flex justify-between\">\n                    <span class=\"text-muted-foreground\">Category:</span>\n                    <span class=\"text-foreground font-medium capitalize\">{{ category() }}</span>\n                  </div>\n                  <div class=\"flex justify-between\">\n                    <span class=\"text-muted-foreground\">Urgency Tier:</span>\n                    <span class=\"text-foreground font-medium capitalize\">{{ urgency() }} SLA</span>\n                  </div>\n                  <div class=\"flex justify-between\">\n                    <span class=\"text-muted-foreground\">Entry Authorization:</span>\n                    <span class=\"text-foreground font-medium\">\n                      {{ permission() === 'granted' ? 'Permission Granted (Master Key)' : 'Call Resident First' }}\n                    </span>\n                  </div>\n                </div>\n                <div class=\"flex flex-wrap justify-center gap-3 pt-2\">\n                  <button ui-button class=\"gap-1.5 shadow-xs\" (click)=\"handleReset()\">\n                    <lucide-icon [img]=\"RefreshCw\" class=\"size-4\" />\n                    <span>Submit Another Request</span>\n                  </button>\n                </div>\n              </div>\n            </div>\n          } @else {\n            <!-- Work Order Submission Form -->\n            <div ui-card class=\"border-border shadow-xs\">\n              <div ui-card-header class=\"pb-4\">\n                <h3 ui-card-title class=\"text-lg\">Work Order Details</h3>\n                <p ui-card-description>Complete the fields below to schedule a maintenance technician visit.</p>\n              </div>\n\n              <div ui-card-content class=\"space-y-6\">\n                <!-- 1. Category Selection Grid -->\n                <div class=\"space-y-3\">\n                  <div class=\"flex items-center justify-between\">\n                    <label class=\"text-foreground text-sm font-semibold\">\n                      1. Issue Category <span class=\"text-destructive\">*</span>\n                    </label>\n                    <span class=\"text-muted-foreground text-xs\">Select one primary discipline</span>\n                  </div>\n\n                  <div class=\"grid grid-cols-2 gap-3 sm:grid-cols-3\">\n                    @for (cat of allCategories; track cat.id) {\n                      <button\n                        type=\"button\"\n                        [attr.aria-pressed]=\"category() === cat.id\"\n                        [class]=\"categoryButtonClass(cat.id)\"\n                        (click)=\"selectCategory(cat.id)\"\n                      >\n                        <div class=\"flex w-full items-center justify-between\">\n                          <div [class]=\"categoryIconClass(cat)\">\n                            <lucide-icon [img]=\"cat.icon\" class=\"size-4.5\" />\n                          </div>\n                          <div [class]=\"categoryCheckClass(cat.id)\">\n                            @if (category() === cat.id) {\n                              <lucide-icon [img]=\"Check\" class=\"size-2.5 stroke-[3]\" />\n                            }\n                          </div>\n                        </div>\n                        <div>\n                          <p class=\"text-foreground text-xs font-semibold\">{{ cat.label }}</p>\n                          <p class=\"text-muted-foreground mt-0.5 line-clamp-1 text-xs\">{{ cat.description }}</p>\n                        </div>\n                      </button>\n                    }\n                  </div>\n                </div>\n\n                <ui-separator />\n\n                <!-- 2. Urgency Level Radios -->\n                <div class=\"space-y-3\">\n                  <div class=\"flex items-center justify-between\">\n                    <label class=\"text-foreground text-sm font-semibold\">\n                      2. Urgency Priority <span class=\"text-destructive\">*</span>\n                    </label>\n                    <span class=\"text-muted-foreground text-xs\">Determines facility response window</span>\n                  </div>\n\n                  <!-- Emergency Banner Alert if High Selected -->\n                  @if (urgency() === 'emergency') {\n                    <div\n                      class=\"border-destructive/30 bg-destructive/10 text-destructive flex items-start gap-3 rounded-lg border p-3.5 text-xs\"\n                    >\n                      <lucide-icon [img]=\"AlertCircle\" class=\"mt-0.5 size-4 shrink-0\" />\n                      <div class=\"space-y-1\">\n                        <p class=\"font-semibold\">High / Emergency SLA Activated</p>\n                        <p class=\"text-destructive/90 leading-relaxed\">\n                          Emergency requests notify on-call building engineers immediately. For active gas leaks or\n                          major structural flooding, call the 24/7 hotline directly at\n                          <strong>(415) 555-0192</strong>.\n                        </p>\n                      </div>\n                    </div>\n                  }\n\n                  <ui-radio-group\n                    [value]=\"urgency()\"\n                    (valueChange)=\"setUrgency($event)\"\n                    class=\"grid gap-3 sm:grid-cols-3\"\n                  >\n                    @for (opt of urgencyOpts; track opt.value) {\n                      <div [class]=\"urgencyCardClass(opt.value)\" (click)=\"setUrgency(opt.value)\">\n                        <div class=\"flex items-start justify-between gap-2\">\n                          <div class=\"flex items-center gap-2\">\n                            <ui-radio-group-item [id]=\"'angular-urgency-' + opt.value\" [value]=\"opt.value\" />\n                            <label\n                              [for]=\"'angular-urgency-' + opt.value\"\n                              class=\"text-foreground cursor-pointer text-xs font-semibold select-none\"\n                            >\n                              {{ opt.label }}\n                            </label>\n                          </div>\n                        </div>\n                        <div>\n                          <span ui-badge [variant]=\"opt.badgeVariant\" class=\"mb-1 text-xs\">\n                            {{ opt.badge }}\n                          </span>\n                          <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                            {{ opt.description }}\n                          </p>\n                        </div>\n                      </div>\n                    }\n                  </ui-radio-group>\n                </div>\n\n                <ui-separator />\n\n                <!-- 3. Location in Unit -->\n                <div class=\"space-y-3\">\n                  <div class=\"flex items-center justify-between\">\n                    <label class=\"text-foreground text-sm font-semibold\">\n                      3. Location in Unit <span class=\"text-destructive\">*</span>\n                    </label>\n                    <span class=\"text-muted-foreground text-xs\">Specific area or room</span>\n                  </div>\n\n                  <ui-select [value]=\"location()\" (valueChange)=\"location.set($event)\">\n                    <button ui-select-trigger class=\"w-full\" aria-label=\"Select location in unit\">\n                      <ui-select-value placeholder=\"Select location in unit...\" />\n                    </button>\n                    <ui-select-content>\n                      <ui-select-group>\n                        <ui-select-label>Unit 4B Interior Areas</ui-select-label>\n                        @for (loc of locationOpts; track loc.value) {\n                          <ui-select-item [value]=\"loc.value\">{{ loc.label }}</ui-select-item>\n                        }\n                      </ui-select-group>\n                    </ui-select-content>\n                  </ui-select>\n                </div>\n\n                <ui-separator />\n\n                <!-- 4. Issue Description -->\n                <div class=\"space-y-3\">\n                  <div class=\"flex items-center justify-between\">\n                    <label for=\"angular-mnt-description\" class=\"text-foreground text-sm font-semibold\">\n                      4. Issue Description <span class=\"text-destructive\">*</span>\n                    </label>\n                    <span [class]=\"descriptionCountClass\"> {{ descriptionLength() }} / {{ charLimit }} </span>\n                  </div>\n\n                  <ui-textarea\n                    id=\"angular-mnt-description\"\n                    [value]=\"description()\"\n                    (valueChange)=\"description.set($event)\"\n                    [rows]=\"4\"\n                    placeholder=\"Please describe the maintenance issue with as much detail as possible...\"\n                    class=\"w-full text-sm\"\n                  />\n\n                  <!-- Quick Snippet Helper Chips -->\n                  <div class=\"space-y-1.5\">\n                    <p class=\"text-muted-foreground text-xs\">Quick details:</p>\n                    <div class=\"flex flex-wrap gap-1.5\">\n                      @for (chip of snippets; track chip) {\n                        <button\n                          type=\"button\"\n                          class=\"bg-muted hover:bg-muted/80 text-muted-foreground hover:text-foreground inline-flex cursor-pointer items-center gap-1 rounded-md px-2.5 py-1 text-xs transition-colors\"\n                          (click)=\"appendSnippet(chip)\"\n                        >\n                          <lucide-icon [img]=\"Plus\" class=\"size-3\" />\n                          <span>{{ chip }}</span>\n                        </button>\n                      }\n                    </div>\n                  </div>\n                </div>\n\n                <ui-separator />\n\n                <!-- 5. Permission to Enter Unit -->\n                <div class=\"space-y-3\">\n                  <div class=\"flex items-center justify-between\">\n                    <label class=\"text-foreground text-sm font-semibold\">\n                      5. Permission to Enter Unit <span class=\"text-destructive\">*</span>\n                    </label>\n                    <span class=\"text-muted-foreground text-xs\">Access protocol</span>\n                  </div>\n\n                  <ui-radio-group\n                    [value]=\"permission()\"\n                    (valueChange)=\"setPermission($event)\"\n                    class=\"grid gap-3 sm:grid-cols-2\"\n                  >\n                    <div [class]=\"permissionCardClass('granted')\" (click)=\"permission.set('granted')\">\n                      <div class=\"flex items-start gap-2.5\">\n                        <ui-radio-group-item id=\"angular-perm-granted\" value=\"granted\" class=\"mt-0.5\" />\n                        <div class=\"space-y-1\">\n                          <label\n                            for=\"angular-perm-granted\"\n                            class=\"text-foreground cursor-pointer text-xs font-semibold select-none\"\n                          >\n                            Permission Granted to Enter\n                          </label>\n                          <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                            Authorized staff may enter with master key if resident is not home. Work order sign-off\n                            notice will be left in unit.\n                          </p>\n                        </div>\n                      </div>\n                      <div class=\"text-success flex items-center gap-1.5 pl-6 text-xs font-medium\">\n                        <lucide-icon [img]=\"UserCheck\" class=\"size-3.5\" />\n                        <span>Faster dispatch window</span>\n                      </div>\n                    </div>\n\n                    <div [class]=\"permissionCardClass('call_first')\" (click)=\"permission.set('call_first')\">\n                      <div class=\"flex items-start gap-2.5\">\n                        <ui-radio-group-item id=\"angular-perm-call\" value=\"call_first\" class=\"mt-0.5\" />\n                        <div class=\"space-y-1\">\n                          <label\n                            for=\"angular-perm-call\"\n                            class=\"text-foreground cursor-pointer text-xs font-semibold select-none\"\n                          >\n                            Call Resident Before Entering\n                          </label>\n                          <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                            Technician must call <span class=\"text-foreground font-medium\">(415) 890-4412</span> 30 mins\n                            prior to arrival. Adult must be present.\n                          </p>\n                        </div>\n                      </div>\n                      <div class=\"text-muted-foreground flex items-center gap-1.5 pl-6 text-xs\">\n                        <lucide-icon [img]=\"Clock\" class=\"size-3.5\" />\n                        <span>Requires resident appointment</span>\n                      </div>\n                    </div>\n                  </ui-radio-group>\n                </div>\n\n                <ui-separator />\n\n                <!-- 6. Photo & Video Dropzone -->\n                <div class=\"space-y-3\">\n                  <div class=\"flex items-center justify-between\">\n                    <label class=\"text-foreground text-sm font-semibold\">\n                      6. Photo &amp; Video Dropzone\n                      <span class=\"text-muted-foreground text-xs font-normal\">(Optional)</span>\n                    </label>\n                    <span class=\"text-muted-foreground text-xs\">{{ attachedFiles().length }} Attached</span>\n                  </div>\n\n                  <!-- Interactive Drop Area -->\n                  <div\n                    [class]=\"dropzoneClass\"\n                    (dragover)=\"onDragOver($event)\"\n                    (dragleave)=\"onDragLeave($event)\"\n                    (drop)=\"handleDrop($event)\"\n                    (click)=\"simulateAddFile()\"\n                  >\n                    <div\n                      class=\"bg-muted text-muted-foreground mx-auto flex size-10 items-center justify-center rounded-full\"\n                    >\n                      <lucide-icon [img]=\"UploadCloud\" class=\"size-5\" />\n                    </div>\n                    <div class=\"mt-2.5 space-y-1\">\n                      <p class=\"text-foreground text-xs font-medium\">\n                        <span class=\"text-primary font-semibold hover:underline\">Click to upload</span> or drag and drop\n                        media files\n                      </p>\n                      <p class=\"text-muted-foreground text-xs\">\n                        PNG, JPG, HEIC, MP4 up to 25MB (helps technician arrive with correct parts)\n                      </p>\n                    </div>\n                  </div>\n\n                  <!-- Attached Files Previews -->\n                  @if (attachedFiles().length > 0) {\n                    <div class=\"space-y-2 pt-1\">\n                      @for (f of attachedFiles(); track f.id) {\n                        <div\n                          class=\"bg-card border-border flex items-center justify-between gap-3 rounded-lg border p-2.5 text-xs transition-colors\"\n                        >\n                          <div class=\"flex min-w-0 items-center gap-2.5\">\n                            <div\n                              class=\"bg-primary/10 text-primary flex size-8 shrink-0 items-center justify-center rounded-md\"\n                            >\n                              @if (f.type === 'video') {\n                                <lucide-icon [img]=\"FileVideo\" class=\"size-4\" />\n                              } @else {\n                                <lucide-icon [img]=\"FileImage\" class=\"size-4\" />\n                              }\n                            </div>\n                            <div class=\"min-w-0\">\n                              <p class=\"text-foreground truncate font-medium\">{{ f.name }}</p>\n                              <p class=\"text-muted-foreground text-xs\">{{ f.size }} · {{ f.uploadedAt }}</p>\n                            </div>\n                          </div>\n                          <div class=\"flex shrink-0 items-center gap-2\">\n                            <span ui-badge variant=\"outline\" class=\"border-success/30 text-success text-xs\">\n                              Attached\n                            </span>\n                            <button\n                              ui-button\n                              variant=\"ghost\"\n                              size=\"icon-sm\"\n                              class=\"text-muted-foreground hover:text-destructive size-7\"\n                              [attr.aria-label]=\"'Remove file ' + f.name\"\n                              (click)=\"removeFile(f.id)\"\n                            >\n                              <lucide-icon [img]=\"Trash2\" class=\"size-3.5\" />\n                              <span class=\"sr-only\">Remove file</span>\n                            </button>\n                          </div>\n                        </div>\n                      }\n                    </div>\n                  }\n                </div>\n              </div>\n\n              <div\n                ui-card-footer\n                class=\"border-border bg-muted/20 flex flex-col-reverse justify-between gap-3 border-t p-4 sm:flex-row sm:items-center\"\n              >\n                <div class=\"flex items-center gap-2\">\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"sm\"\n                    class=\"shadow-xs\"\n                    [disabled]=\"isSubmitting()\"\n                    (click)=\"handleSaveDraft()\"\n                  >\n                    <span>{{ draftSaved() ? 'Draft Saved!' : 'Save as Draft' }}</span>\n                  </button>\n                  @if (draftSaved()) {\n                    <span class=\"text-success flex items-center gap-1 text-xs\">\n                      <lucide-icon [img]=\"Check\" class=\"size-3\" />\n                      Saved locally\n                    </span>\n                  }\n                </div>\n\n                <button\n                  ui-button\n                  size=\"default\"\n                  class=\"w-full gap-2 shadow-xs sm:w-auto\"\n                  [disabled]=\"isSubmitting() || !description().trim()\"\n                  (click)=\"handleSubmit()\"\n                >\n                  @if (!isSubmitting()) {\n                    <lucide-icon [img]=\"Wrench\" class=\"size-4\" />\n                  } @else {\n                    <lucide-icon [img]=\"RefreshCw\" class=\"size-4 animate-spin\" />\n                  }\n                  <span>{{ isSubmitting() ? 'Dispatching Work Order...' : 'Submit Maintenance Request' }}</span>\n                </button>\n              </div>\n            </div>\n          }\n        </div>\n\n        <!-- Right Column: Emergency & SLA Sidebar (4 cols) -->\n        <div class=\"space-y-6 lg:sticky lg:top-6 lg:col-span-4\">\n          <!-- 24/7 Emergency Maintenance Hotline Card -->\n          <div ui-card class=\"border-destructive/40 bg-destructive/5 dark:bg-destructive/10 shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"text-destructive flex items-center gap-2\">\n                <lucide-icon [img]=\"ShieldAlert\" class=\"size-5 shrink-0\" />\n                <h3 ui-card-title class=\"text-destructive text-base\">24/7 Emergency Hotline</h3>\n              </div>\n              <p ui-card-description class=\"text-foreground/80 text-xs\">\n                For urgent situations threatening life safety, gas leaks, or active flooding.\n              </p>\n            </div>\n            <div ui-card-content class=\"space-y-4\">\n              <div class=\"bg-card border-destructive/30 space-y-1 rounded-lg border p-3 text-center\">\n                <p class=\"text-muted-foreground text-xs font-medium\">Immediate Dispatch Phone</p>\n                <p class=\"text-foreground font-mono text-xl font-semibold tracking-tight\">(415) 555-0192</p>\n                <p class=\"text-muted-foreground text-xs\">Facility Operations On-Call Desk</p>\n              </div>\n\n              <div class=\"space-y-2\">\n                <p class=\"text-foreground text-xs font-semibold\">What qualifies as an emergency:</p>\n                <ul class=\"text-muted-foreground space-y-1.5 text-xs\">\n                  <li class=\"flex items-start gap-2\">\n                    <span class=\"text-destructive font-bold\">•</span>\n                    <span>Active uncontrolled water leaks or burst pipes</span>\n                  </li>\n                  <li class=\"flex items-start gap-2\">\n                    <span class=\"text-destructive font-bold\">•</span>\n                    <span>Smell of natural gas or carbon monoxide alert</span>\n                  </li>\n                  <li class=\"flex items-start gap-2\">\n                    <span class=\"text-destructive font-bold\">•</span>\n                    <span>Complete electrical power loss in unit</span>\n                  </li>\n                  <li class=\"flex items-start gap-2\">\n                    <span class=\"text-destructive font-bold\">•</span>\n                    <span>Inoperable exterior lock or door security issue</span>\n                  </li>\n                  <li class=\"flex items-start gap-2\">\n                    <span class=\"text-destructive font-bold\">•</span>\n                    <span>Total loss of heating when temp is below 55°F</span>\n                  </li>\n                </ul>\n              </div>\n\n              <a ui-button href=\"tel:4155550192\" variant=\"destructive\" class=\"w-full gap-2 shadow-xs\">\n                <lucide-icon [img]=\"PhoneCall\" class=\"size-4\" />\n                <span>Call Emergency Dispatch</span>\n              </a>\n            </div>\n          </div>\n\n          <!-- Service Level Agreement (SLA) Card -->\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex items-center gap-2\">\n                <lucide-icon [img]=\"Clock\" class=\"text-primary size-4.5\" />\n                <h3 ui-card-title class=\"text-base\">Service Level Agreement (SLA)</h3>\n              </div>\n              <p ui-card-description class=\"text-xs\">Response benchmarks for Pacific Heights property maintenance.</p>\n            </div>\n            <div ui-card-content class=\"space-y-3.5\">\n              <div class=\"bg-muted/40 border-border space-y-2 rounded-lg border p-3\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2\">\n                    <span class=\"bg-destructive size-2 rounded-full\"></span>\n                    <span class=\"text-foreground text-xs font-semibold\">Emergency Priority</span>\n                  </div>\n                  <span ui-badge variant=\"destructive\" class=\"text-xs\">&lt; 2 Hours</span>\n                </div>\n                <p class=\"text-muted-foreground text-xs\">\n                  24/7/365 immediate dispatch with on-call technician response.\n                </p>\n              </div>\n\n              <div class=\"bg-muted/40 border-border space-y-2 rounded-lg border p-3\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2\">\n                    <span class=\"bg-secondary-foreground size-2 rounded-full\"></span>\n                    <span class=\"text-foreground text-xs font-semibold\">Standard Priority</span>\n                  </div>\n                  <span ui-badge variant=\"secondary\" class=\"text-xs\">24–48 Hours</span>\n                </div>\n                <p class=\"text-muted-foreground text-xs\">\n                  Standard repairs scheduled Mon–Fri during normal operational hours.\n                </p>\n              </div>\n\n              <div class=\"bg-muted/40 border-border space-y-2 rounded-lg border p-3\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2\">\n                    <span class=\"bg-muted-foreground size-2 rounded-full\"></span>\n                    <span class=\"text-foreground text-xs font-semibold\">Routine / Preventative</span>\n                  </div>\n                  <span ui-badge variant=\"outline\" class=\"text-xs\">3–5 Days</span>\n                </div>\n                <p class=\"text-muted-foreground text-xs\">\n                  Filter changes, caulking touch-ups, and scheduled inspections.\n                </p>\n              </div>\n\n              <div class=\"border-border bg-card flex items-start gap-2.5 rounded-lg border p-3 text-xs\">\n                <lucide-icon [img]=\"ShieldCheck\" class=\"text-primary mt-0.5 size-4 shrink-0\" />\n                <p class=\"text-muted-foreground leading-relaxed\">\n                  Status notifications are automatically sent via SMS and resident email as work orders progress.\n                </p>\n              </div>\n            </div>\n          </div>\n\n          <!-- Building Operations Info Card -->\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex items-center gap-2\">\n                <lucide-icon [img]=\"Building2\" class=\"text-muted-foreground size-4.5\" />\n                <h3 ui-card-title class=\"text-base\">Building Operations</h3>\n              </div>\n            </div>\n            <div ui-card-content class=\"space-y-2.5 text-xs\">\n              <div class=\"border-border/60 flex justify-between border-b py-1\">\n                <span class=\"text-muted-foreground\">Building Super:</span>\n                <span class=\"text-foreground font-medium\">Marcus Vance (Office #102)</span>\n              </div>\n              <div class=\"border-border/60 flex justify-between border-b py-1\">\n                <span class=\"text-muted-foreground\">Service Window:</span>\n                <span class=\"text-foreground font-medium\">Mon – Sat · 8:00 AM – 6:00 PM</span>\n              </div>\n              <div class=\"border-border/60 flex justify-between border-b py-1\">\n                <span class=\"text-muted-foreground\">Quiet Hours:</span>\n                <span class=\"text-foreground font-medium\">10:00 PM – 8:00 AM</span>\n              </div>\n              <div class=\"flex justify-between py-1\">\n                <span class=\"text-muted-foreground\">Resident Portal ID:</span>\n                <span class=\"text-foreground font-mono font-medium\">PH-RES-4B</span>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiMaintenanceRequestTicketComponent {\n  protected readonly AlertCircle = AlertCircle\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly ChevronDown = ChevronDown\n  protected readonly Clock = Clock\n  protected readonly FileImage = FileImage\n  protected readonly FileVideo = FileVideo\n  protected readonly PhoneCall = PhoneCall\n  protected readonly Plus = Plus\n  protected readonly RefreshCw = RefreshCw\n  protected readonly ShieldAlert = ShieldAlert\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Building2 = Building2\n  protected readonly Trash2 = Trash2\n  protected readonly UploadCloud = UploadCloud\n  protected readonly UserCheck = UserCheck\n  protected readonly Wrench = Wrench\n\n  protected readonly allCategories = categories\n  protected readonly urgencyOpts = urgencyOptions\n  protected readonly locationOpts = locationOptions\n  protected readonly snippets = quickSnippets\n  protected readonly charLimit = maxChars\n\n  @Input() set initialCategory(v: string) {\n    if (v) this.category.set(v)\n  }\n  @Input() set initialUrgency(v: MaintenanceUrgency) {\n    if (v) this.urgency.set(v)\n  }\n  @Input() set initialLocation(v: string) {\n    if (v) this.location.set(v)\n  }\n  @Input() set initialDescription(v: string) {\n    if (v !== undefined && v !== null) this.description.set(v)\n  }\n  @Input() set initialPermission(v: EntryPermission) {\n    if (v) this.permission.set(v)\n  }\n  @Input() set initialFiles(v: AttachedFile[] | undefined) {\n    this.attachedFiles.set((v ?? defaultFiles).map((f) => ({ ...f })))\n  }\n  @Input('class') className?: string\n\n  readonly category = signal<string>('appliances')\n  readonly urgency = signal<MaintenanceUrgency>('standard')\n  readonly location = signal<string>('kitchen')\n  readonly description = signal<string>(defaultDescription)\n  readonly permission = signal<EntryPermission>('granted')\n  readonly attachedFiles = signal<AttachedFile[]>(defaultFiles.map((f) => ({ ...f })))\n  readonly isDragging = signal(false)\n  readonly showOpenRequests = signal(false)\n  readonly isSubmitting = signal(false)\n  readonly isSubmitted = signal(false)\n  readonly draftSaved = signal(false)\n  readonly generatedTicketId = signal('MNT-9042')\n\n  private draftTimer: ReturnType<typeof setTimeout> | undefined\n  private submitTimer: ReturnType<typeof setTimeout> | undefined\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => {\n      clearTimeout(this.draftTimer)\n      clearTimeout(this.submitTimer)\n    })\n  }\n\n  readonly descriptionLength = computed(() => this.description().length)\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  get openRequestsChevronClass(): string {\n    return cn('size-3.5 transition-transform duration-200', this.showOpenRequests() && 'rotate-180')\n  }\n\n  get descriptionCountClass(): string {\n    return cn('text-xs', this.descriptionLength() > maxChars ? 'text-destructive font-medium' : 'text-muted-foreground')\n  }\n\n  get dropzoneClass(): string {\n    return cn(\n      'cursor-pointer rounded-lg border-2 border-dashed p-6 text-center transition-colors',\n      this.isDragging()\n        ? 'border-primary bg-primary/10'\n        : 'border-border bg-muted/20 hover:border-muted-foreground/40 hover:bg-muted/40',\n    )\n  }\n\n  categoryButtonClass(id: string): string {\n    return cn(\n      'group focus-visible:ring-ring relative flex cursor-pointer flex-col items-start gap-2.5 rounded-lg border p-3.5 text-left transition-colors outline-none select-none focus-visible:ring-2',\n      this.category() === id\n        ? 'border-primary bg-primary/5 ring-primary/30 shadow-xs ring-1'\n        : 'border-border bg-card hover:bg-muted/40 hover:border-muted-foreground/30',\n    )\n  }\n\n  categoryIconClass(cat: IssueCategory): string {\n    return cn('flex size-8 items-center justify-center rounded-md', cat.badgeColor)\n  }\n\n  categoryCheckClass(id: string): string {\n    return cn(\n      'flex size-4 items-center justify-center rounded-full border transition-colors',\n      this.category() === id\n        ? 'border-primary bg-primary text-primary-foreground'\n        : 'border-muted-foreground/30 opacity-0 group-hover:opacity-60',\n    )\n  }\n\n  urgencyCardClass(value: MaintenanceUrgency): string {\n    return cn(\n      'border-border relative flex cursor-pointer flex-col justify-between gap-3 rounded-lg border p-3.5 transition-colors',\n      this.urgency() === value\n        ? 'border-primary bg-primary/5 ring-primary/30 shadow-xs ring-1'\n        : 'bg-card hover:bg-muted/40 hover:border-muted-foreground/30',\n    )\n  }\n\n  permissionCardClass(value: EntryPermission): string {\n    return cn(\n      'border-border relative flex cursor-pointer flex-col justify-between gap-2.5 rounded-lg border p-4 transition-colors',\n      this.permission() === value\n        ? 'border-primary bg-primary/5 ring-primary/30 shadow-xs ring-1'\n        : 'bg-card hover:bg-muted/40 hover:border-muted-foreground/30',\n    )\n  }\n\n  selectCategory(id: string): void {\n    this.category.set(id)\n  }\n\n  setUrgency(value: string): void {\n    this.urgency.set(value as MaintenanceUrgency)\n  }\n\n  setPermission(value: string): void {\n    this.permission.set(value as EntryPermission)\n  }\n\n  appendSnippet(snippet: string): void {\n    const current = this.description()\n    if (!current) {\n      this.description.set(snippet)\n    } else if (!current.includes(snippet)) {\n      this.description.set(`${current.trim()} ${snippet}.`)\n    }\n  }\n\n  removeFile(id: string): void {\n    this.attachedFiles.update((prev) => prev.filter((f) => f.id !== id))\n  }\n\n  onDragOver(event: DragEvent): void {\n    event.preventDefault()\n    this.isDragging.set(true)\n  }\n\n  onDragLeave(event: DragEvent): void {\n    event.preventDefault()\n    this.isDragging.set(false)\n  }\n\n  handleDrop(event: DragEvent): void {\n    event.preventDefault()\n    this.isDragging.set(false)\n    const files = event.dataTransfer?.files\n    if (files && files.length > 0) {\n      const additions: AttachedFile[] = []\n      for (let i = 0; i < files.length; i++) {\n        const file = files[i]\n        additions.push({\n          id: `f-${Date.now()}-${i}`,\n          name: file.name,\n          size: `${(file.size / (1024 * 1024)).toFixed(1)} MB`,\n          type: file.type.startsWith('video') ? 'video' : 'image',\n          uploadedAt: 'Just now',\n        })\n      }\n      this.attachedFiles.update((prev) => [...prev, ...additions])\n    }\n  }\n\n  simulateAddFile(): void {\n    const sampleNames = ['cabinet-water-mark.jpg', 'under-sink-plumbing.jpg', 'noise-recording.mp4']\n    const files = this.attachedFiles()\n    const randomName = sampleNames[files.length % sampleNames.length]\n    this.attachedFiles.update((prev) => [\n      ...prev,\n      {\n        id: `f-${Date.now()}`,\n        name: randomName,\n        size: '3.1 MB',\n        type: randomName.endsWith('.mp4') ? 'video' : 'image',\n        uploadedAt: 'Just now',\n      },\n    ])\n  }\n\n  handleSaveDraft(): void {\n    this.draftSaved.set(true)\n    clearTimeout(this.draftTimer)\n    this.draftTimer = setTimeout(() => this.draftSaved.set(false), 3000)\n  }\n\n  handleSubmit(): void {\n    this.isSubmitting.set(true)\n    clearTimeout(this.submitTimer)\n    this.submitTimer = setTimeout(() => {\n      this.isSubmitting.set(false)\n      this.isSubmitted.set(true)\n      this.generatedTicketId.set(`MNT-${Math.floor(1000 + Math.random() * 9000)}`)\n    }, 700)\n  }\n\n  handleReset(): void {\n    clearTimeout(this.submitTimer)\n    this.isSubmitted.set(false)\n    this.category.set('appliances')\n    this.urgency.set('standard')\n    this.location.set('kitchen')\n    this.description.set(defaultDescription)\n    this.permission.set('granted')\n    this.attachedFiles.set(defaultFiles.map((f) => ({ ...f })))\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/maintenance-request-ticket/maintenance-request-ticket.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/maintenance-request-ticket/index.ts",
      "content": "export {\n  UiMaintenanceRequestTicketComponent,\n  type AttachedFile,\n  type EntryPermission,\n  type MaintenanceUrgency,\n} from './maintenance-request-ticket.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/maintenance-request-ticket/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "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/radio-group.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/textarea.json"
  ],
  "description": "Resident repair and facility work order submission portal with urgency priority and photo dropzone.",
  "categories": [
    "real-estate",
    "hospitality",
    "app",
    "forms"
  ]
}