import { Component, inject, signal, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { debounceTime, distinctUntilChanged, Subject, switchMap, of } from 'rxjs';
import { CodesService, CodeEntry, PagedResult } from '../../core/services/codes.service';
type Tab = 'all' | 'icd10' | 'cpt';
@Component({
selector: 'app-codes-search',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
@for (tab of tabs; track tab.id) {
}
@if (loading()) {
}
@if (!loading() && activeTab() === 'all' && query) {
@for (code of globalResults()?.icd10; track code._id) {
{{ code.code }}
{{ code.description }}
@if (code.category) {
{{ code.category }}
}
{{ code.billable ? 'Billable' : 'Non-billable' }}
}
@if (!globalResults()?.icd10?.length) {
No ICD-10 results
}
@for (code of globalResults()?.cpt; track code._id) {
{{ code.code }}
{{ code.description }}
@if (code.category) {
{{ code.category }}
}
{{ code.billable ? 'Billable' : 'Non-billable' }}
}
@if (!globalResults()?.cpt?.length) {
No CPT results
}
}
@if (!loading() && activeTab() === 'icd10') {
@for (code of icd10Results()?.data; track code._id) {
{{ code.code }}
{{ code.description }}
@if (code.category) {
{{ code.category }}
}
{{ code.billable ? 'Billable' : 'Non-billable' }}
}
@if (icd10Results()?.pages && icd10Results()!.pages > 1) {
}
}
@if (!loading() && activeTab() === 'cpt') {
@for (code of cptResults()?.data; track code._id) {
{{ code.code }}
{{ code.description }}
@if (code.category) {
{{ code.category }}
}
{{ code.billable ? 'Billable' : 'Non-billable' }}
}
@if (cptResults()?.pages && cptResults()!.pages > 1) {
}
}
@if (!loading() && !query) {
Start searching
Enter a code like J18.9 or keyword like pneumonia
}
@if (selectedCode()) {
{{ selectedCode()?.description }}
@if (selectedCode()?.category) {
Category{{ selectedCode()?.category }}
}
Billable
{{ selectedCode()?.billable ? 'Yes' : 'No' }}
Type{{ selectedCode()?.type || 'ICD-10' }}
}
`,
styles: [`
.codes-page { padding:2rem; max-width:1200px; font-family:'Inter',system-ui,sans-serif; }
.page-header { margin-bottom:1.5rem; }
.page-header h1 { font-size:1.75rem; font-weight:700; color:#0f172a; margin:0 0 0.25rem; }
.page-header p { color:#64748b; margin:0; }
.search-section { margin-bottom:1.5rem; }
.search-box { position:relative; margin-bottom:1rem; }
.search-icon { position:absolute; left:1rem; top:50%; transform:translateY(-50%); width:20px; height:20px; color:#94a3b8; }
.search-input {
width:100%; padding:0.9rem 3rem; border:1px solid #e2e8f0; border-radius:12px;
font-size:1rem; background:white; box-sizing:border-box; outline:none;
transition:border-color 0.15s, box-shadow 0.15s; box-shadow:0 1px 3px rgba(0,0,0,0.06);
}
.search-input:focus { border-color:#0EA5E9; box-shadow:0 0 0 3px rgba(14,165,233,0.15); }
.clear-btn { position:absolute; right:1rem; top:50%; transform:translateY(-50%); background:none; border:none; cursor:pointer; color:#94a3b8; font-size:1rem; }
.tabs { display:flex; gap:0.5rem; }
.tab {
padding:0.5rem 1.25rem; border:1px solid #e2e8f0; border-radius:8px; background:white;
font-size:0.875rem; font-weight:500; color:#64748b; cursor:pointer; display:flex; align-items:center; gap:0.5rem;
transition:all 0.15s;
}
.tab.active { background:#0EA5E9; color:white; border-color:#0EA5E9; }
.badge { background:rgba(255,255,255,0.3); border-radius:999px; padding:0.1rem 0.5rem; font-size:0.75rem; }
.tab:not(.active) .badge { background:#e0f2fe; color:#0369a1; }
.loading-state { display:flex; align-items:center; gap:0.75rem; padding:3rem; color:#64748b; justify-content:center; }
.spinner { width:24px; height:24px; border:2px solid #e2e8f0; border-top-color:#0EA5E9; border-radius:50%; animation:spin 0.6s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }
.results-split { display:grid; grid-template-columns:1fr 1fr; gap:1.5rem; }
.results-col { display:flex; flex-direction:column; gap:0.5rem; }
.col-header { display:flex; align-items:center; gap:0.75rem; margin-bottom:0.5rem; }
.col-count { font-size:0.8rem; color:#94a3b8; }
.col-badge { padding:0.25rem 0.75rem; border-radius:999px; font-size:0.75rem; font-weight:700; }
.col-badge.icd10 { background:#eff6ff; color:#1d4ed8; }
.col-badge.cpt { background:#f0fdf4; color:#15803d; }
.empty-col { color:#94a3b8; font-size:0.875rem; padding:1rem; text-align:center; background:white; border-radius:8px; border:1px dashed #e2e8f0; }
.results-list { display:flex; flex-direction:column; gap:0.5rem; }
.code-card {
background:white; border:1px solid #e2e8f0; border-radius:10px; padding:1rem;
display:flex; align-items:center; gap:1rem; cursor:pointer; transition:border-color 0.15s, box-shadow 0.15s;
}
.code-card:hover { border-color:#0EA5E9; box-shadow:0 0 0 3px rgba(14,165,233,0.08); }
.code-tag {
padding:0.35rem 0.75rem; border-radius:6px; font-size:0.85rem; font-weight:700;
white-space:nowrap; font-family:'JetBrains Mono',monospace;
}
.code-tag.icd10 { background:#eff6ff; color:#1d4ed8; }
.code-tag.cpt { background:#f0fdf4; color:#15803d; }
.code-body { flex:1; min-width:0; }
.code-desc { margin:0; font-size:0.9rem; color:#1e293b; font-weight:500; }
.code-cat { margin:0.2rem 0 0; font-size:0.775rem; color:#94a3b8; }
.code-badge { padding:0.2rem 0.6rem; border-radius:999px; font-size:0.7rem; font-weight:600; background:#f1f5f9; color:#64748b; white-space:nowrap; }
.code-badge.billable { background:#f0fdf4; color:#16a34a; }
.pagination { display:flex; align-items:center; justify-content:center; gap:1rem; padding:1rem; }
.pagination button { padding:0.5rem 1rem; border:1px solid #e2e8f0; border-radius:6px; background:white; cursor:pointer; font-size:0.875rem; }
.pagination button:disabled { opacity:0.4; cursor:not-allowed; }
.pagination span { color:#64748b; font-size:0.875rem; }
.empty-state { display:flex; flex-direction:column; align-items:center; padding:5rem 2rem; color:#94a3b8; }
.empty-state svg { width:80px; height:80px; margin-bottom:1.5rem; }
.empty-state h3 { font-size:1.1rem; color:#475569; margin:0 0 0.5rem; }
.empty-state p { margin:0; font-size:0.9rem; text-align:center; }
.empty-state strong { color:#475569; }
.modal-overlay { position:fixed; inset:0; background:rgba(15,23,42,0.5); display:flex; align-items:center; justify-content:center; z-index:100; }
.modal { background:white; border-radius:16px; width:100%; max-width:480px; box-shadow:0 25px 50px rgba(0,0,0,0.25); overflow:hidden; }
.modal-header { display:flex; align-items:center; justify-content:space-between; padding:1.25rem 1.5rem; border-bottom:1px solid #f1f5f9; }
.modal-close { background:none; border:none; cursor:pointer; color:#94a3b8; font-size:1.2rem; }
.modal-body { padding:1.5rem; }
.modal-body h2 { font-size:1.1rem; font-weight:600; color:#0f172a; margin:0 0 1.25rem; }
.detail-row { display:flex; justify-content:space-between; padding:0.6rem 0; border-bottom:1px solid #f8fafc; font-size:0.875rem; }
.detail-row span { color:#64748b; }
.modal-footer { padding:1rem 1.5rem; border-top:1px solid #f1f5f9; display:flex; justify-content:flex-end; }
.btn-copy { padding:0.6rem 1.5rem; background:#0EA5E9; color:white; border:none; border-radius:8px; font-weight:600; cursor:pointer; font-size:0.9rem; }
.btn-copy:hover { background:#0284c7; }
@media (max-width: 768px) { .results-split { grid-template-columns:1fr; } }
`],
})
export class CodesSearchComponent implements OnInit {
private codesService = inject(CodesService);
private search$ = new Subject