feat: doctor reply + diagnosis + tracking code per health request
Backend:
- HealthRequest model: TrackingCode (DR-XXXXXX), Diagnosis,
DoctorReply, RepliedAt fields
- Runtime migration: ALTER TABLE adds 4 new columns to existing DB
- POST /api/health-request: auto-generates tracking code, returns it
- PUT /api/health-requests/{id}/reply: doctor sets diagnosis + reply
- GET /api/health-request/track/{code}: public lookup by tracking code
- GET /api/health-requests?phone=: filter history by phone number
Admin panel:
- Request table shows tracking code column (gold badge)
- Detail modal (680px): tracking code header, patient info, full message
- Previous doctor reply shown in green box if exists
- Reply form: diagnosis input + textarea for doctor message
- History panel: all requests from same phone, click to switch
- 'پاسخ / مشاهده' button opens reply modal directly
Frontend:
- After form submit: shows tracking code in green box to user
(format: DR-XXXXXX, stays visible 8 seconds)
- Box auto-hides and form resets after timeout
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -171,17 +171,23 @@ public class PatientVisit
|
||||
public class HealthRequest
|
||||
{
|
||||
public int Id { get; set; }
|
||||
[MaxLength(150)] public string FullName { get; set; } = "";
|
||||
[MaxLength(20)] public string PhoneNumber { get; set; } = "";
|
||||
[MaxLength(200)] public string Email { get; set; } = "";
|
||||
public string Message { get; set; } = "";
|
||||
[MaxLength(20)] public string Category { get; set; } = "beauty"; // beauty | health
|
||||
public bool IsHandled { get; set; } = false;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
[MaxLength(20)] public string TrackingCode { get; set; } = ""; // e.g. DR-A3F7K2
|
||||
[MaxLength(150)] public string FullName { get; set; } = "";
|
||||
[MaxLength(20)] public string PhoneNumber { get; set; } = "";
|
||||
[MaxLength(200)] public string Email { get; set; } = "";
|
||||
public string Message { get; set; } = "";
|
||||
[MaxLength(20)] public string Category { get; set; } = "beauty"; // beauty | health
|
||||
public bool IsHandled { get; set; } = false;
|
||||
// Doctor response
|
||||
public string Diagnosis { get; set; } = ""; // پزشک: تشخیص
|
||||
public string DoctorReply { get; set; } = ""; // پزشک: پاسخ/توضیح
|
||||
public DateTime? RepliedAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// ─── DTOs ─────────────────────────────────────────────────────────────────────
|
||||
public record LoginRequest(string Username, string Password);
|
||||
public record DoctorReplyDto(string? Diagnosis, string? DoctorReply);
|
||||
public record ChangePasswordRequest(string CurrentPassword, string NewPassword);
|
||||
public record SettingDto(string Key, string Value);
|
||||
public record BulkSettingsDto(Dictionary<string, string> Settings);
|
||||
|
||||
@@ -791,6 +791,7 @@
|
||||
</div>
|
||||
<button type="submit" class="form-submit" id="booking-submit">ارسال و رزرو نوبت</button>
|
||||
</form>
|
||||
<div id="booking-tracking" style="display:none"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -844,15 +845,30 @@
|
||||
})
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
btn.textContent = '✓ درخواست شما ثبت شد';
|
||||
const data = await res.json();
|
||||
const code = data.trackingCode || '';
|
||||
btn.textContent = '✓ درخواست ثبت شد';
|
||||
btn.style.background = '#2D7A4F';
|
||||
// Show tracking code to user
|
||||
const trackBox = document.getElementById('booking-tracking');
|
||||
if (trackBox && code) {
|
||||
trackBox.innerHTML = `
|
||||
<div style="background:#E8F5E9;border-radius:14px;padding:1rem 1.4rem;border-right:4px solid #388E3C;margin-top:1rem">
|
||||
<p style="font-size:.85rem;color:#2D7A4F;margin-bottom:.4rem;font-weight:600">✓ درخواست شما با موفقیت ثبت شد</p>
|
||||
<p style="font-size:.82rem;color:#555;margin-bottom:.5rem">کد رهگیری شما برای پیگیری پاسخ پزشک:</p>
|
||||
<div style="font-size:1.3rem;font-weight:700;letter-spacing:.1em;color:#1a1a1a;font-family:monospace;background:#fff;display:inline-block;padding:.3rem .8rem;border-radius:8px;border:1.5px solid #a5d6a7">${code}</div>
|
||||
<p style="font-size:.75rem;color:#777;margin-top:.5rem">این کد را نزد خود نگه دارید. در اسرع وقت با شما تماس میگیریم.</p>
|
||||
</div>`;
|
||||
trackBox.style.display = 'block';
|
||||
}
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'ارسال و رزرو نوبت';
|
||||
btn.style.background = '';
|
||||
btn.disabled = false;
|
||||
e.target.reset();
|
||||
document.getElementById('booking-category').value = 'beauty';
|
||||
}, 3500);
|
||||
if (trackBox) { trackBox.innerHTML=''; trackBox.style.display='none'; }
|
||||
}, 8000);
|
||||
} catch {
|
||||
btn.textContent = 'خطا — دوباره تلاش کنید';
|
||||
btn.style.background = '#c62828';
|
||||
|
||||
+64
-13
@@ -131,17 +131,32 @@ await using (var scope = app.Services.CreateAsyncScope())
|
||||
try {
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS "HealthRequests" (
|
||||
"Id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"FullName" TEXT NOT NULL DEFAULT '',
|
||||
"PhoneNumber" TEXT NOT NULL DEFAULT '',
|
||||
"Email" TEXT NOT NULL DEFAULT '',
|
||||
"Message" TEXT NOT NULL DEFAULT '',
|
||||
"Category" TEXT NOT NULL DEFAULT 'beauty',
|
||||
"IsHandled" INTEGER NOT NULL DEFAULT 0,
|
||||
"CreatedAt" TEXT NOT NULL DEFAULT ''
|
||||
"Id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"TrackingCode" TEXT NOT NULL DEFAULT '',
|
||||
"FullName" TEXT NOT NULL DEFAULT '',
|
||||
"PhoneNumber" TEXT NOT NULL DEFAULT '',
|
||||
"Email" TEXT NOT NULL DEFAULT '',
|
||||
"Message" TEXT NOT NULL DEFAULT '',
|
||||
"Category" TEXT NOT NULL DEFAULT 'beauty',
|
||||
"IsHandled" INTEGER NOT NULL DEFAULT 0,
|
||||
"Diagnosis" TEXT NOT NULL DEFAULT '',
|
||||
"DoctorReply" TEXT NOT NULL DEFAULT '',
|
||||
"RepliedAt" TEXT,
|
||||
"CreatedAt" TEXT NOT NULL DEFAULT ''
|
||||
)
|
||||
""");
|
||||
} catch { }
|
||||
// Add new columns to existing HealthRequests table (safe migration)
|
||||
foreach (var col in new[] {
|
||||
("TrackingCode", "TEXT NOT NULL DEFAULT ''"),
|
||||
("Diagnosis", "TEXT NOT NULL DEFAULT ''"),
|
||||
("DoctorReply", "TEXT NOT NULL DEFAULT ''"),
|
||||
("RepliedAt", "TEXT") })
|
||||
{
|
||||
try { await db.Database.ExecuteSqlRawAsync(
|
||||
$"ALTER TABLE HealthRequests ADD COLUMN \"{col.Item1}\" {col.Item2}"); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
await SeedAsync(db);
|
||||
}
|
||||
@@ -703,20 +718,47 @@ patientsGroup.MapDelete("/visits/{visitId:int}", async (int visitId, AppDbContex
|
||||
// ── Health Requests (public submit / admin manage) ────────────────────────────
|
||||
app.MapPost("/api/health-request", async (HealthRequest req, AppDbContext db) =>
|
||||
{
|
||||
req.CreatedAt = DateTime.UtcNow;
|
||||
req.IsHandled = false;
|
||||
req.CreatedAt = DateTime.UtcNow;
|
||||
req.IsHandled = false;
|
||||
req.TrackingCode = "DR-" + GenerateTrackingCode();
|
||||
db.HealthRequests.Add(req);
|
||||
await db.SaveChangesAsync();
|
||||
return Results.Ok(new { message = "درخواست شما ثبت شد" });
|
||||
return Results.Ok(new { message = "درخواست شما ثبت شد", trackingCode = req.TrackingCode, id = req.Id });
|
||||
});
|
||||
|
||||
app.MapGet("/api/health-requests", async (bool? handled, AppDbContext db) =>
|
||||
// Public: look up own request by tracking code
|
||||
app.MapGet("/api/health-request/track/{code}", async (string code, AppDbContext db) =>
|
||||
{
|
||||
var r = await db.HealthRequests.FirstOrDefaultAsync(x => x.TrackingCode == code);
|
||||
if (r is null) return Results.NotFound(new { message = "کد رهگیری یافت نشد" });
|
||||
return Results.Ok(new {
|
||||
r.TrackingCode, r.FullName, r.Category, r.Message, r.IsHandled,
|
||||
r.Diagnosis, r.DoctorReply, r.RepliedAt, r.CreatedAt
|
||||
});
|
||||
});
|
||||
|
||||
app.MapGet("/api/health-requests", async (bool? handled, string? phone, AppDbContext db) =>
|
||||
{
|
||||
var q = db.HealthRequests.AsQueryable();
|
||||
if (handled.HasValue) q = q.Where(r => r.IsHandled == handled);
|
||||
if (handled.HasValue) q = q.Where(r => r.IsHandled == handled);
|
||||
if (!string.IsNullOrEmpty(phone)) q = q.Where(r => r.PhoneNumber == phone);
|
||||
return Results.Ok(await q.OrderByDescending(r => r.CreatedAt).ToListAsync());
|
||||
}).RequireAuthorization();
|
||||
|
||||
// Doctor reply: set diagnosis + reply text + mark handled
|
||||
app.MapPut("/api/health-requests/{id:int}/reply", async (int id, DoctorReplyDto dto, AppDbContext db) =>
|
||||
{
|
||||
var r = await db.HealthRequests.FindAsync(id);
|
||||
if (r is null) return Results.NotFound();
|
||||
r.Diagnosis = dto.Diagnosis ?? r.Diagnosis;
|
||||
r.DoctorReply = dto.DoctorReply ?? r.DoctorReply;
|
||||
r.IsHandled = true;
|
||||
r.RepliedAt = DateTime.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
return Results.Ok(r);
|
||||
}).RequireAuthorization();
|
||||
|
||||
// Mark handled without reply
|
||||
app.MapPut("/api/health-requests/{id:int}", async (int id, AppDbContext db) =>
|
||||
{
|
||||
var r = await db.HealthRequests.FindAsync(id);
|
||||
@@ -808,6 +850,15 @@ static string Slugify(string text)
|
||||
return text.Trim('-');
|
||||
}
|
||||
|
||||
static string GenerateTrackingCode()
|
||||
{
|
||||
const string chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||
var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
|
||||
var bytes = new byte[6];
|
||||
rng.GetBytes(bytes);
|
||||
return new string(bytes.Select(b => chars[b % chars.Length]).ToArray());
|
||||
}
|
||||
|
||||
static int EstimateReadingTime(string content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(content)) return 1;
|
||||
|
||||
@@ -722,7 +722,7 @@ tr:hover td{background:#FAFBFC}
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>نام</th><th>تلفن</th><th>دسته</th><th>پیام</th><th>تاریخ</th><th>وضعیت</th><th>عملیات</th></tr></thead>
|
||||
<thead><tr><th>کد رهگیری</th><th>نام</th><th>تلفن</th><th>دسته</th><th>پیام</th><th>تاریخ</th><th>وضعیت</th><th>عملیات</th></tr></thead>
|
||||
<tbody id="healthreqTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -861,7 +861,7 @@ tr:hover td{background:#FAFBFC}
|
||||
<!-- Visit Modal -->
|
||||
<!-- Health Request Detail Modal -->
|
||||
<div class="modal-overlay hidden" id="reqDetailModal">
|
||||
<div class="modal" style="max-width:560px">
|
||||
<div class="modal" style="max-width:680px">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">جزئیات درخواست</div>
|
||||
<button class="modal-close" onclick="closeModal('reqDetailModal')">✕</button>
|
||||
@@ -1331,45 +1331,112 @@ async function loadHealthRequests() {
|
||||
const catLabel = {beauty:'زیبایی پوست', health:'سلامت عمومی'};
|
||||
document.getElementById('healthreqTable').innerHTML = _allReqs.map(r=>`
|
||||
<tr style="${!r.isHandled?'font-weight:600':'opacity:.75'}">
|
||||
<td><span style="font-family:monospace;font-size:.78rem;background:var(--gold-pale);color:var(--gold);padding:2px 7px;border-radius:6px">${r.trackingCode||'—'}</span></td>
|
||||
<td>${r.fullName}</td>
|
||||
<td dir="ltr">${r.phoneNumber}</td>
|
||||
<td>${catLabel[r.category]||r.category}</td>
|
||||
<td style="max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mid);font-size:.85rem">${r.message||'—'}</td>
|
||||
<td>${new Date(r.createdAt).toLocaleDateString('fa-IR')}</td>
|
||||
<td><span style="background:${r.isHandled?'#E8F5E9':'#FFEBEE'};color:${r.isHandled?'#388E3C':'#C62828'};padding:2px 8px;border-radius:20px;font-size:.72rem">${r.isHandled?'بررسی شده':'جدید'}</span></td>
|
||||
<td dir="ltr" style="font-size:.85rem">${r.phoneNumber}</td>
|
||||
<td style="font-size:.82rem">${catLabel[r.category]||r.category}</td>
|
||||
<td style="max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mid);font-size:.82rem">${r.message||'—'}</td>
|
||||
<td style="font-size:.82rem">${new Date(r.createdAt).toLocaleDateString('fa-IR')}</td>
|
||||
<td><span style="background:${r.isHandled?'#E8F5E9':'#FFEBEE'};color:${r.isHandled?'#388E3C':'#C62828'};padding:2px 8px;border-radius:20px;font-size:.72rem">${r.isHandled?'پاسخ داده شده':'جدید'}</span></td>
|
||||
<td style="white-space:nowrap">
|
||||
<button class="btn btn-secondary btn-sm" onclick="viewReq(${r.id})">مشاهده</button>
|
||||
${!r.isHandled?`<button class="btn btn-secondary btn-sm" onclick="handleReq(${r.id})">✓ بررسی شد</button>`:''}
|
||||
<button class="btn btn-secondary btn-sm" onclick="viewReq(${r.id})">پاسخ / مشاهده</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="deleteReq(${r.id})">حذف</button>
|
||||
</td>
|
||||
</tr>`).join('') || '<tr><td colspan="7" style="text-align:center;color:var(--light);padding:2rem">درخواستی وجود ندارد</td></tr>';
|
||||
</tr>`).join('') || '<tr><td colspan="8" style="text-align:center;color:var(--light);padding:2rem">درخواستی وجود ندارد</td></tr>';
|
||||
}
|
||||
|
||||
function viewReq(id) {
|
||||
function lbl(text) {
|
||||
return `<span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.3rem">${text}</span>`;
|
||||
}
|
||||
|
||||
async function viewReq(id) {
|
||||
const r = _allReqs.find(x=>x.id===id); if(!r) return;
|
||||
const catLabel = {beauty:'زیبایی پوست', health:'سلامت عمومی'};
|
||||
|
||||
// Load history for same phone number
|
||||
const history = await api(`/api/health-requests?phone=${encodeURIComponent(r.phoneNumber)}`) || [];
|
||||
const histHtml = history.length > 1 ? `
|
||||
<div style="margin-top:1.5rem">
|
||||
<div style="font-size:.82rem;font-weight:600;color:var(--mid);margin-bottom:.6rem">📋 سابقه درخواستهای این شماره (${history.length} درخواست)</div>
|
||||
<div style="display:flex;flex-direction:column;gap:.5rem">
|
||||
${history.map(h=>`
|
||||
<div style="background:${h.id===r.id?'var(--gold-pale)':'var(--section-bg)'};border-radius:10px;padding:.6rem .9rem;font-size:.82rem;display:flex;align-items:center;gap:.8rem;cursor:pointer;border:1.5px solid ${h.id===r.id?'var(--gold)':'transparent'}" onclick="viewReq(${h.id})">
|
||||
<span style="color:var(--light);flex-shrink:0">${new Date(h.createdAt).toLocaleDateString('fa-IR')}</span>
|
||||
<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${(h.message||'').substring(0,60)}${h.message?.length>60?'...':''}</span>
|
||||
<span style="background:${h.isHandled?'#E8F5E9':'#FFEBEE'};color:${h.isHandled?'#388E3C':'#C62828'};padding:2px 8px;border-radius:20px;font-size:.7rem;flex-shrink:0">${h.isHandled?'پاسخ داده شده':'جدید'}</span>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
</div>` : '';
|
||||
|
||||
document.getElementById('reqDetailBody').innerHTML = `
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1.2rem">
|
||||
<div><span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.25rem">نام</span><strong>${r.fullName}</strong></div>
|
||||
<div><span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.25rem">تلفن</span><strong dir="ltr">${r.phoneNumber}</strong></div>
|
||||
<div><span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.25rem">ایمیل</span><span>${r.email||'—'}</span></div>
|
||||
<div><span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.25rem">دسته</span>
|
||||
<span style="background:${r.category==='health'?'#E3F2FD':'var(--gold-pale)'};color:${r.category==='health'?'#1565C0':'var(--gold)'};padding:3px 10px;border-radius:20px;font-size:.78rem">${catLabel[r.category]||r.category}</span>
|
||||
<!-- Header: tracking code + status -->
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1.2rem;flex-wrap:wrap;gap:.5rem">
|
||||
<div style="background:var(--gold-pale);border-radius:10px;padding:.4rem .9rem;font-size:.82rem;font-family:monospace;letter-spacing:.05em;color:var(--gold)">
|
||||
🔖 کد رهگیری: <strong>${r.trackingCode||'—'}</strong>
|
||||
</div>
|
||||
<div><span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.25rem">تاریخ ثبت</span><span>${new Date(r.createdAt).toLocaleDateString('fa-IR')}</span></div>
|
||||
<div><span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.25rem">وضعیت</span>
|
||||
<span style="background:${r.isHandled?'#E8F5E9':'#FFEBEE'};color:${r.isHandled?'#388E3C':'#C62828'};padding:3px 10px;border-radius:20px;font-size:.78rem">${r.isHandled?'بررسی شده':'جدید'}</span>
|
||||
<span style="background:${r.isHandled?'#E8F5E9':'#FFEBEE'};color:${r.isHandled?'#388E3C':'#C62828'};padding:4px 12px;border-radius:20px;font-size:.78rem">${r.isHandled?'✓ پاسخ داده شده':'⏳ در انتظار بررسی'}</span>
|
||||
</div>
|
||||
|
||||
<!-- Patient info -->
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.8rem;margin-bottom:1.2rem">
|
||||
<div>${lbl('نام')}<strong>${r.fullName}</strong></div>
|
||||
<div>${lbl('تلفن')}<strong dir="ltr">${r.phoneNumber}</strong></div>
|
||||
<div>${lbl('دسته')}
|
||||
<span style="background:${r.category==='health'?'#E3F2FD':'var(--gold-pale)'};color:${r.category==='health'?'#1565C0':'var(--gold)'};padding:2px 10px;border-radius:20px;font-size:.78rem">${catLabel[r.category]||r.category}</span>
|
||||
</div>
|
||||
<div>${lbl('تاریخ ثبت')}<span>${new Date(r.createdAt).toLocaleDateString('fa-IR')}</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Patient message -->
|
||||
<div style="margin-bottom:1.2rem">
|
||||
${lbl('پیام بیمار')}
|
||||
<div style="background:var(--section-bg);border-radius:10px;padding:.9rem 1rem;line-height:2;font-size:.88rem;white-space:pre-wrap;min-height:50px">${r.message||'—'}</div>
|
||||
</div>
|
||||
|
||||
<!-- Existing reply (if any) -->
|
||||
${r.doctorReply||r.diagnosis ? `
|
||||
<div style="background:#E8F5E9;border-radius:12px;padding:1rem;margin-bottom:1.2rem;border-right:3px solid #388E3C">
|
||||
<div style="font-size:.78rem;color:#388E3C;font-weight:600;margin-bottom:.5rem">✓ پاسخ دکتر — ${r.repliedAt?new Date(r.repliedAt).toLocaleDateString('fa-IR'):''}</div>
|
||||
${r.diagnosis?`<div style="margin-bottom:.5rem">${lbl('تشخیص')}<strong>${r.diagnosis}</strong></div>`:''}
|
||||
${r.doctorReply?`<div>${lbl('پاسخ / توضیح')}<span style="font-size:.88rem">${r.doctorReply}</span></div>`:''}
|
||||
</div>` : ''}
|
||||
|
||||
<!-- Doctor reply form -->
|
||||
<div style="border-top:1px solid var(--border);padding-top:1.2rem">
|
||||
<div style="font-size:.88rem;font-weight:600;color:var(--mid);margin-bottom:.8rem">👩⚕️ پاسخ پزشک</div>
|
||||
<div class="form-group" style="margin-bottom:.8rem">
|
||||
<label style="font-size:.8rem">تشخیص</label>
|
||||
<input id="reply-diagnosis" value="${r.diagnosis||''}" placeholder="مثال: درماتیت آتوپیک، نیاز به مشاوره حضوری ..."/>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom:1rem">
|
||||
<label style="font-size:.8rem">پاسخ / توضیح برای بیمار</label>
|
||||
<textarea id="reply-body" rows="4" placeholder="پاسخ خود را بنویسید. بیمار میتواند با کد رهگیری این پاسخ را ببیند...">${r.doctorReply||''}</textarea>
|
||||
</div>
|
||||
<div style="display:flex;gap:.6rem;flex-wrap:wrap">
|
||||
<button class="btn btn-primary" onclick="saveReply(${r.id})">💾 ذخیره پاسخ</button>
|
||||
${!r.isHandled?`<button class="btn btn-secondary" onclick="handleReq(${r.id})">✓ بررسی شد (بدون پاسخ)</button>`:''}
|
||||
<button class="btn btn-secondary" onclick="closeModal('reqDetailModal')">بستن</button>
|
||||
</div>
|
||||
</div>
|
||||
<div><span style="font-size:.75rem;color:var(--light);display:block;margin-bottom:.5rem">پیام / شرح درخواست</span>
|
||||
<div style="background:var(--section-bg);border-radius:12px;padding:1rem 1.2rem;line-height:2;font-size:.9rem;white-space:pre-wrap;min-height:60px">${r.message||'—'}</div>
|
||||
</div>
|
||||
${!r.isHandled?`<div style="margin-top:1.2rem"><button class="btn btn-primary" onclick="handleReq(${r.id});closeModal('reqDetailModal')">✓ علامتگذاری به عنوان بررسی شده</button></div>`:''}
|
||||
${histHtml}
|
||||
`;
|
||||
document.getElementById('reqDetailModal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
async function handleReq(id){await api(`/api/health-requests/${id}`,{method:'PUT'});toast('علامتگذاری شد ✓');loadHealthRequests();}
|
||||
async function saveReply(id) {
|
||||
const diagnosis = document.getElementById('reply-diagnosis').value.trim();
|
||||
const doctorReply = document.getElementById('reply-body').value.trim();
|
||||
await api(`/api/health-requests/${id}/reply`, {
|
||||
method:'PUT',
|
||||
body: JSON.stringify({diagnosis, doctorReply})
|
||||
});
|
||||
toast('پاسخ ذخیره شد ✓');
|
||||
await loadHealthRequests();
|
||||
// Refresh view with updated data
|
||||
viewReq(id);
|
||||
}
|
||||
|
||||
async function handleReq(id){await api(`/api/health-requests/${id}`,{method:'PUT'});toast('علامتگذاری شد ✓');loadHealthRequests();closeModal('reqDetailModal');}
|
||||
async function deleteReq(id){if(!confirm('حذف؟'))return;await api(`/api/health-requests/${id}`,{method:'DELETE'});toast('حذف شد','error');loadHealthRequests();}
|
||||
|
||||
// ── Comments ──────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user