import { Component, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; interface Endpoint { method: 'GET' | 'POST' | 'PATCH' | 'DELETE'; path: string; description: string; auth: boolean; body?: string; response?: string; } interface ApiGroup { id: string; title: string; tag: string; tagColor: string; baseUrl: string; endpoints: Endpoint[]; } @Component({ selector: 'app-api', standalone: true, imports: [CommonModule], template: `
v1.0 REST API

API Reference

Base URL: https://api.medicode.io/v1

All endpoints require JWT authentication via Bearer token unless stated otherwise.

Authentication
Authorization: Bearer <your_token>
Obtain a token via POST /auth/login
@for (g of groups; track g.id) { @if (activeGroup() === g.id) {
{{ g.tag }}

{{ g.title }}

{{ g.baseUrl }}
@for (ep of g.endpoints; track ep.path) {
{{ ep.method }} {{ ep.path }} {{ ep.description }} @if (ep.auth) { }
@if (isOpen(g.id + ep.path)) {
@if (ep.body) {
Request Body
{{ ep.body }}
} @if (ep.response) {
Response 200 OK
{{ ep.response }}
}
}
} } }
`, styles: [` .api-page { max-width: 1400px; margin: 0 auto; font-family: 'Inter', system-ui, sans-serif; } /* ── Header ────────────────────────────────────── */ .api-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 2rem; padding: 2.5rem 2rem 2rem; background: #0f172a; flex-wrap: wrap; } .api-eyebrow { display: flex; gap: 0.5rem; margin-bottom: 0.75rem; } .version-pill, .rest-pill { font-size: 0.7rem; font-weight: 700; padding: 0.2rem 0.6rem; border-radius: 99px; letter-spacing: 0.05em; } .version-pill { background: #1e293b; color: #94a3b8; border: 1px solid #334155; } .rest-pill { background: rgba(14,165,233,0.15); color: #38bdf8; border: 1px solid rgba(14,165,233,0.3); } .api-header h1 { font-size: 1.75rem; font-weight: 800; color: #f8fafc; margin: 0 0 0.5rem; letter-spacing: -0.02em; } .base-url { background: #1e293b; color: #38bdf8; padding: 0.2rem 0.6rem; border-radius: 5px; font-size: 0.875rem; } .api-subtitle { font-size: 0.85rem; color: #64748b; margin: 0.5rem 0 0; } /* Auth card */ .auth-card { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 1.25rem 1.5rem; min-width: 280px; display: flex; flex-direction: column; gap: 0.75rem; } .auth-card-title { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.07em; } .auth-snippet { display: block; background: #0f172a; color: #38bdf8; padding: 0.6rem 0.85rem; border-radius: 7px; font-size: 0.8rem; border: 1px solid #334155; } .auth-note { font-size: 0.78rem; color: #64748b; } .auth-note strong { color: #94a3b8; } /* ── Body ──────────────────────────────────────── */ .api-body { display: grid; grid-template-columns: 220px 1fr; gap: 0; min-height: calc(100vh - 200px); } /* Sidebar */ .api-sidebar { background: #f8fafc; border-right: 1px solid #e2e8f0; padding: 1.5rem 1rem; display: flex; flex-direction: column; gap: 0.25rem; position: sticky; top: 0; align-self: start; height: 100vh; overflow-y: auto; } .sidebar-section-title { font-size: 0.68rem; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.1em; padding: 0.25rem 0.5rem; margin-bottom: 0.25rem; } .sidebar-group { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.75rem; border-radius: 7px; cursor: pointer; transition: background 0.12s; } .sidebar-group:hover { background: #e2e8f0; } .sidebar-group.active { background: #e0f2fe; } .sidebar-group.active .sidebar-label { color: #0369a1; font-weight: 600; } .sidebar-label { font-size: 0.85rem; color: #374151; } .sidebar-tag { font-size: 0.62rem; font-weight: 700; padding: 0.15rem 0.4rem; border-radius: 4px; flex-shrink: 0; } .tag-blue { background: #eff6ff; color: #1d4ed8; } .tag-green { background: #f0fdf4; color: #15803d; } .tag-purple { background: #faf5ff; color: #7c3aed; } .tag-sky { background: #f0f9ff; color: #0369a1; } .tag-orange { background: #fff7ed; color: #c2410c; } .sidebar-divider { height: 1px; background: #e2e8f0; margin: 0.75rem 0; } .sidebar-link { font-size: 0.82rem; color: #64748b; text-decoration: none; padding: 0.35rem 0.75rem; border-radius: 6px; transition: all 0.12s; } .sidebar-link:hover { background: #e2e8f0; color: #0f172a; } /* Main */ .api-main { padding: 2rem; display: flex; flex-direction: column; gap: 1rem; } .group-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem; padding-bottom: 1rem; border-bottom: 1px solid #e2e8f0; } .group-tag { font-size: 0.75rem; font-weight: 700; padding: 0.3rem 0.75rem; border-radius: 6px; } .group-header h2 { font-size: 1.25rem; font-weight: 700; color: #0f172a; margin: 0 0 0.15rem; } .group-base { font-size: 0.78rem; color: #64748b; background: #f1f5f9; padding: 0.1rem 0.4rem; border-radius: 4px; } /* Endpoint cards */ .endpoint-card { background: white; border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; cursor: pointer; transition: border-color 0.15s, box-shadow 0.15s; } .endpoint-card:hover { border-color: #0EA5E9; box-shadow: 0 0 0 3px rgba(14,165,233,0.07); } .endpoint-row { display: flex; align-items: center; gap: 0.85rem; padding: 0.85rem 1.1rem; } .method { font-size: 0.7rem; font-weight: 800; padding: 0.25rem 0.55rem; border-radius: 5px; letter-spacing: 0.05em; flex-shrink: 0; min-width: 52px; text-align: center; } .method-get { background: #f0fdf4; color: #15803d; border: 1px solid #bbf7d0; } .method-post { background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe; } .method-patch { background: #fff7ed; color: #c2410c; border: 1px solid #fed7aa; } .method-delete { background: #fff1f2; color: #be123c; border: 1px solid #fecdd3; } .ep-path { font-size: 0.88rem; color: #1e293b; font-family: 'Fira Code', 'Consolas', monospace; flex-shrink: 0; } .ep-desc { font-size: 0.82rem; color: #64748b; } .lock-icon { width: 14px; height: 14px; color: #94a3b8; margin-left: auto; flex-shrink: 0; } .chevron { width: 16px; height: 16px; color: #94a3b8; flex-shrink: 0; transition: transform 0.2s; margin-left: auto; } .chevron.rotated { transform: rotate(180deg); } .lock-icon + .chevron { margin-left: 0; } /* Expanded body */ .endpoint-body { border-top: 1px solid #f1f5f9; padding: 1.1rem; background: #fafafa; display: flex; flex-direction: column; gap: 1rem; } .code-block-wrap { display: flex; flex-direction: column; gap: 0; border-radius: 8px; overflow: hidden; border: 1px solid #e2e8f0; } .code-block-label { display: flex; align-items: center; justify-content: space-between; background: #f1f5f9; padding: 0.5rem 0.85rem; font-size: 0.75rem; font-weight: 600; color: #64748b; border-bottom: 1px solid #e2e8f0; } .status-200 { background: #f0fdf4; color: #15803d; padding: 0.1rem 0.4rem; border-radius: 4px; font-size: 0.68rem; margin-left: 0.4rem; } .copy-btn { background: none; border: 1px solid #e2e8f0; border-radius: 5px; font-size: 0.7rem; color: #64748b; padding: 0.15rem 0.5rem; cursor: pointer; transition: all 0.12s; } .copy-btn:hover { border-color: #0EA5E9; color: #0EA5E9; background: #f0f9ff; } .code-block { background: #0f172a; color: #e2e8f0; margin: 0; padding: 1rem 1.1rem; font-size: 0.8rem; font-family: 'Fira Code', 'Consolas', monospace; overflow-x: auto; line-height: 1.6; } @media (max-width: 900px) { .api-body { grid-template-columns: 1fr; } .api-sidebar { display: none; } } `], }) export class ApiReferenceComponent { activeGroup = signal('auth'); openCards = signal>(new Set()); setGroup(id: string) { this.activeGroup.set(id); } toggle(key: string) { const s = new Set(this.openCards()); s.has(key) ? s.delete(key) : s.add(key); this.openCards.set(s); } isOpen(key: string) { return this.openCards().has(key); } copy(text: string) { navigator.clipboard.writeText(text); } groups: ApiGroup[] = [ { id: 'auth', title: 'Authentication', tag: 'AUTH', tagColor: 'purple', baseUrl: '/auth', endpoints: [ { method: 'POST', path: '/auth/register', description: 'Register a new user account', auth: false, body: `{ "name": "Jane Doe", "email": "jane@clinic.com", "password": "securepass123", "role": "coder" }`, response: `{ "message": "User registered successfully", "user": { "_id": "64f3a...", "name": "Jane Doe", "email": "jane@clinic.com", "role": "coder" } }`, }, { method: 'POST', path: '/auth/login', description: 'Authenticate and receive JWT token', auth: false, body: `{ "email": "jane@clinic.com", "password": "securepass123" }`, response: `{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "_id": "64f3a...", "name": "Jane Doe", "role": "coder" } }`, }, { method: 'GET', path: '/auth/profile', description: 'Get authenticated user profile', auth: true, response: `{ "_id": "64f3a...", "name": "Jane Doe", "email": "jane@clinic.com", "role": "coder", "createdAt": "2024-01-15T10:30:00Z" }`, }, ], }, { id: 'patients', title: 'Patients', tag: 'PATIENTS', tagColor: 'blue', baseUrl: '/patients', endpoints: [ { method: 'GET', path: '/patients', description: 'List all patients (paginated)', auth: true, response: `{ "data": [ { "_id": "64f3b...", "firstName": "John", "lastName": "Smith", "dob": "1985-04-12", "mrn": "MRN-001" } ], "total": 120, "page": 1, "limit": 20 }`, }, { method: 'POST', path: '/patients', description: 'Create a new patient record', auth: true, body: `{ "firstName": "John", "lastName": "Smith", "dob": "1985-04-12", "gender": "male", "mrn": "MRN-001", "phone": "+1-305-555-0100" }`, response: `{ "_id": "64f3b...", "firstName": "John", "lastName": "Smith", "mrn": "MRN-001", "createdAt": "2024-01-15T11:00:00Z" }`, }, { method: 'GET', path: '/patients/:id', description: 'Get a single patient by ID', auth: true, response: `{ "_id": "64f3b...", "firstName": "John", "lastName": "Smith", "encounters": [ { "_id": "64f4c...", "status": "coded" } ] }`, }, { method: 'PATCH', path: '/patients/:id', description: 'Update patient information', auth: true, body: `{ "phone": "+1-305-555-0199", "address": "123 Medical Blvd, Miami FL" }`, }, { method: 'DELETE', path: '/patients/:id', description: 'Delete a patient record', auth: true, response: `{ "message": "Patient deleted successfully" }`, }, ], }, { id: 'encounters', title: 'Encounters', tag: 'ENCOUNTERS', tagColor: 'green', baseUrl: '/patients/:id/encounters', endpoints: [ { method: 'GET', path: '/patients/:id/encounters', description: 'List all encounters for a patient', auth: true, response: `[ { "_id": "64f4c...", "date": "2024-01-10", "status": "coded", "icd10Codes": ["J06.9", "Z00.00"], "cptCodes": ["99213"] } ]`, }, { method: 'POST', path: '/patients/:id/encounters', description: 'Create a new encounter', auth: true, body: `{ "date": "2024-01-10", "notes": "Routine checkup", "icd10Codes": ["Z00.00"], "cptCodes": ["99213"] }`, }, { method: 'PATCH', path: '/patients/:id/encounters/:eid', description: 'Update encounter codes or status', auth: true, body: `{ "status": "billed", "icd10Codes": ["J06.9", "Z00.00"], "cptCodes": ["99213", "36415"] }`, }, ], }, { id: 'codes', title: 'Code Search', tag: 'CODES', tagColor: 'sky', baseUrl: '/codes', endpoints: [ { method: 'GET', path: '/codes/icd10?q=diabetes', description: 'Search ICD-10-CM diagnostic codes', auth: true, response: `[ { "code": "E11.9", "description": "Type 2 diabetes mellitus without complications", "category": "Endocrine" }, { "code": "E10.9", "description": "Type 1 diabetes mellitus without complications", "category": "Endocrine" } ]`, }, { method: 'GET', path: '/codes/cpt?q=office+visit', description: 'Search CPT procedure codes', auth: true, response: `[ { "code": "99213", "description": "Office or other outpatient visit, established patient, low complexity", "category": "E&M" } ]`, }, ], }, { id: 'reports', title: 'Reports', tag: 'REPORTS', tagColor: 'orange', baseUrl: '/reports', endpoints: [ { method: 'GET', path: '/reports/encounters-by-month', description: 'Encounters grouped by month', auth: true, response: `[ { "_id": "2024-01", "count": 34 }, { "_id": "2024-02", "count": 51 } ]`, }, { method: 'GET', path: '/reports/top-diagnoses?limit=10', description: 'Most used ICD-10 codes', auth: true, response: `[ { "_id": "J06.9", "description": "Acute upper respiratory infection", "count": 42 } ]`, }, { method: 'GET', path: '/reports/top-procedures?limit=10', description: 'Most used CPT codes', auth: true, }, { method: 'GET', path: '/reports/export/pdf', description: 'Export full report as PDF', auth: true, }, { method: 'GET', path: '/reports/export/excel', description: 'Export full report as Excel', auth: true, }, ], }, ]; }