---
name: helpdesk-support
description: >
  نظام إدارة الدعم الفني وتذاكر الخدمة.
  استخدم هذا الـ skill عند: إنشاء نظام تذاكر، إدارة طلبات الدعم، تتبع المشاكل التقنية،
  إدارة SLA، قاعدة المعرفة، إدارة الأصول التقنية، أو أي مهمة تتعلق بالدعم الفني.
  يدعم التصنيف التلقائي بالذكاء الاصطناعي والتصعيد التلقائي.
---

# Help Desk & IT Support System
# نظام الدعم الفني

## Domain Model

```csharp
public class Ticket : BaseAuditableEntity
{
    public int Id { get; set; }
    public string TicketNumber { get; set; } = string.Empty;     // TKT-2024-00001
    public string Subject { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public int RequestorId { get; set; }
    public int? AssignedToId { get; set; }
    public int? AssignedTeamId { get; set; }
    public int CategoryId { get; set; }
    public int? SubCategoryId { get; set; }
    
    public TicketPriority Priority { get; set; }
    public TicketStatus Status { get; set; }
    public TicketType Type { get; set; }
    public TicketSource Source { get; set; }
    
    // SLA
    public int? SlaId { get; set; }
    public DateTime? ResponseDueAt { get; set; }
    public DateTime? ResolutionDueAt { get; set; }
    public DateTime? FirstResponseAt { get; set; }
    public DateTime? ResolvedAt { get; set; }
    public DateTime? ClosedAt { get; set; }
    public bool IsSlaBreached { get; set; }
    
    // Satisfaction
    public int? SatisfactionRating { get; set; }   // 1-5
    public string? SatisfactionComment { get; set; }

    public List<TicketComment> Comments { get; set; } = new();
    public List<TicketAttachment> Attachments { get; set; } = new();
    public List<TicketHistory> History { get; set; } = new();
}

public enum TicketPriority
{
    Critical,   // حرج - 2 ساعة
    High,       // عالي - 4 ساعات
    Medium,     // متوسط - 8 ساعات
    Low         // منخفض - 24 ساعة
}

public enum TicketType
{
    Incident,       // حادثة
    ServiceRequest, // طلب خدمة
    Problem,        // مشكلة
    Change,         // طلب تغيير
    Question        // استفسار
}

public enum TicketSource
{
    Portal,         // البوابة الإلكترونية
    Email,          // بريد إلكتروني
    Phone,          // هاتف
    WalkIn,         // حضوري
    Chat,           // محادثة
    System          // تلقائي من النظام
}

// === SLA ===
public class ServiceLevelAgreement
{
    public int Id { get; set; }
    public string NameAr { get; set; } = string.Empty;
    public TicketPriority Priority { get; set; }
    public int ResponseTimeMinutes { get; set; }
    public int ResolutionTimeMinutes { get; set; }
    public bool BusinessHoursOnly { get; set; } = true;
    public decimal EscalationThresholdPercent { get; set; } = 80; // تصعيد عند 80%
}

// === قاعدة المعرفة ===
public class KnowledgeArticle : BaseAuditableEntity
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Content { get; set; } = string.Empty;    // Markdown
    public int CategoryId { get; set; }
    public int ViewCount { get; set; }
    public int HelpfulCount { get; set; }
    public bool IsPublished { get; set; }
    public List<string> Tags { get; set; } = new();
}
```

## SLA Engine

```csharp
public class SlaEngine
{
    private static readonly TimeSpan BusinessStart = new(8, 0, 0);
    private static readonly TimeSpan BusinessEnd = new(16, 0, 0);

    public DateTime CalculateDeadline(DateTime startTime, int targetMinutes, bool businessHoursOnly)
    {
        if (!businessHoursOnly)
            return startTime.AddMinutes(targetMinutes);

        var remaining = targetMinutes;
        var current = startTime;

        while (remaining > 0)
        {
            // Skip weekends (Friday/Saturday in Saudi)
            if (current.DayOfWeek == DayOfWeek.Friday || current.DayOfWeek == DayOfWeek.Saturday)
            {
                current = current.Date.AddDays(1).Add(BusinessStart);
                continue;
            }

            var dayEnd = current.Date.Add(BusinessEnd);
            var availableMinutes = (int)(dayEnd - current).TotalMinutes;

            if (availableMinutes <= 0)
            {
                current = current.Date.AddDays(1).Add(BusinessStart);
                continue;
            }

            if (remaining <= availableMinutes)
                return current.AddMinutes(remaining);

            remaining -= availableMinutes;
            current = current.Date.AddDays(1).Add(BusinessStart);
        }

        return current;
    }
}
```

## Auto-Assignment & AI Classification

```csharp
public class TicketAutoAssigner
{
    public async Task<int?> AutoAssignAsync(Ticket ticket)
    {
        // 1. Check category-based routing rules
        var rule = await _context.RoutingRules
            .FirstOrDefaultAsync(r => r.CategoryId == ticket.CategoryId);
        
        if (rule != null)
        {
            // Round-robin within team
            var teamMembers = await _context.TeamMembers
                .Where(m => m.TeamId == rule.TeamId && m.IsAvailable)
                .OrderBy(m => m.CurrentTicketCount)
                .FirstOrDefaultAsync();
            
            return teamMembers?.EmployeeId;
        }
        return null;
    }
}
```

## SQL Schema

```sql
CREATE SCHEMA [HelpDesk];

CREATE TABLE [HelpDesk].[Tickets] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [TicketNumber] NVARCHAR(50) NOT NULL UNIQUE,
    [Subject] NVARCHAR(300) NOT NULL,
    [Description] NVARCHAR(4000) NOT NULL,
    [RequestorId] INT NOT NULL,
    [AssignedToId] INT NULL,
    [CategoryId] INT NOT NULL,
    [Priority] NVARCHAR(20) NOT NULL DEFAULT 'Medium',
    [Status] NVARCHAR(20) NOT NULL DEFAULT 'New',
    [Type] NVARCHAR(20) NOT NULL DEFAULT 'Incident',
    [Source] NVARCHAR(20) NOT NULL DEFAULT 'Portal',
    [ResponseDueAt] DATETIME2 NULL,
    [ResolutionDueAt] DATETIME2 NULL,
    [FirstResponseAt] DATETIME2 NULL,
    [ResolvedAt] DATETIME2 NULL,
    [SatisfactionRating] INT NULL,
    [IsSlaBreached] BIT NOT NULL DEFAULT 0,
    [CreatedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE()
);

CREATE TABLE [HelpDesk].[TicketComments] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [TicketId] INT NOT NULL FOREIGN KEY REFERENCES [HelpDesk].[Tickets]([Id]),
    [AuthorId] INT NOT NULL,
    [Content] NVARCHAR(4000) NOT NULL,
    [IsInternal] BIT NOT NULL DEFAULT 0,
    [CreatedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE()
);

CREATE TABLE [HelpDesk].[KnowledgeArticles] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [Title] NVARCHAR(300) NOT NULL,
    [Content] NVARCHAR(MAX) NOT NULL,
    [CategoryId] INT NOT NULL,
    [ViewCount] INT NOT NULL DEFAULT 0,
    [IsPublished] BIT NOT NULL DEFAULT 0,
    [CreatedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE()
);
```

## Dashboard Metrics

| المؤشر | الوصف |
|--------|--------|
| التذاكر المفتوحة | عدد التذاكر غير المحلولة |
| متوسط وقت الاستجابة | متوسط وقت أول رد |
| متوسط وقت الحل | متوسط وقت حل التذكرة |
| نسبة الالتزام بـ SLA | التذاكر المحلولة ضمن الوقت |
| رضا العملاء (CSAT) | متوسط تقييم الرضا |
| التذاكر حسب الفئة | توزيع التذاكر |
| تذاكر مخالفة SLA | التذاكر المتجاوزة للمدة |
| إنتاجية الفريق | تذاكر محلولة لكل فني |
