Skip to content

Destek Masası ve Ticket Sistemi

Öğrenci/eğitmen destek talepleri, admin kanban paneli, yapay zeka destekli ilk temas (deflection), e-posta şablonları ve bildirim entegrasyonu.

Achidemy’de klasik e‑posta kaosu yerine, platform içi Destek Masası ve ticket tabanlı bir sistem kullanılır.
Bu sistem:

  • Öğrenci ve eğitmenlerin /account/support üzerinden bilet açmasını,
  • Adminlerin /admin/support ekranında kanban görünümünde bu biletleri yönetmesini,
  • Cloudflare AI ile ilk temas yanıtı (deflection) üretip gereksiz biletleri azaltmayı,
  • Durum değişikliklerinde e‑posta ve uygulama içi bildirim göndermeyi

sağlar.


Dosya: app/db/schema.ts

AlanTipAçıklama
iduuid PKTicket kimliği.
userIdtext FKTalebi açan kullanıcı (users.id).
subjecttextKonu başlığı.
categorytextKategori: payment, technical, instructor, content, other.
statustextopen, in_progress, resolved, closed.
prioritytextlow, medium, high — kanban kartı badge’i için.
urlContexttextKullanıcının hatayı aldığı URL (örn. /learn/react-101).
createdAttimestampOluşturulma tarihi.
updatedAttimestampSon güncelleme tarihi.

Ek olarak:

  • support_tickets_status_idxstatus alanı için index.
  • support_tickets_user_idxuserId alanı için index.
AlanTipAçıklama
iduuid PKMesaj kimliği.
ticketIduuid FKİlgili ticket (support_tickets.id).
senderIdtext FKMesajı atan kullanıcı (users.id).
isAdminbooleanAdmin mi? (admin mesajlarını UI’da farklı renkle).
messagetextMesaj içeriği.
attachmentUrltextOpsiyonel: ileride ekran görüntüsü vb. için.
createdAttimestampGönderim zamanı.

Her yeni ticket, en az bir adet ilk mesaj (kullanıcının açıklaması) ile birlikte oluşturulur.


2. GraphQL API (Ticket CRUD + Durum Yönetimi)

Section titled “2. GraphQL API (Ticket CRUD + Durum Yönetimi)”

Dosya: app/graphql/schema.ts

createSupportTicket(subject, category, message, urlContext?)

Section titled “createSupportTicket(subject, category, message, urlContext?)”
  • Amaç: Öğrenci/eğitmenin yeni bir destek talebi açması.

  • Çalışma:

    1. support_tickets tablosuna yeni satır eklenir (status = "open", priority = "medium" varsayılan).

    2. support_messages tablosuna kullanıcının ilk mesajı eklenir (isAdmin = false).

    3. runBackground ile otomatik karşılama e‑postası tetiklenir:

      sendSupportTicketCreatedEmail(
      user.email,
      user.name,
      newTicket.id,
      subject,
      "https://achidemy.net/account/support",
      );
  • Yetki: user zorunlu; session yoksa hata.

  • Amaç: Kullanıcı veya adminin mevcut bilete mesaj eklemesi.

  • Çalışma:

    1. support_messages içine yeni mesaj insert edilir (isAdmin role’a göre).

    2. Ticket’ın yeni durumu hesaplanır:

      const nextStatus = isAdmin
      ? "resolved"
      : ticketInfo.status === "resolved" || ticketInfo.status === "closed"
      ? "open"
      : "in_progress";
      • Admin cevap verirse → "resolved"
      • Kullanıcı, çözüldü/kapalı bilete tekrar yazarsa → "open"
      • Aksi halde → "in_progress".
    3. Admin cevap veriyorsa, ticket sahibine yanıt e‑postası gönderilir:

      sendSupportTicketReplyEmail(
      ticketOwner.email,
      ticketOwner.name,
      ticketId,
      ticketInfo.subject,
      message,
      "https://achidemy.net/account/support",
      );
  • Yetki: user zorunlu; user.role === "admin" veya normal kullanıcı olabilir.

  • Amaç: Admin kanban ekranından sürükle‑bırak ile durum güncellemek.

  • Çalışma:

    1. support_tickets.status alanı doğrudan verilen değere (open, in_progress, resolved, closed) set edilir.
    2. Yeni durum "resolved" veya "closed" ise:
      • Ticket ve sahibi (ticketOwner) DB’den çekilir.

      • Ticket sahibinin bildirim dili getNotificationLangForUser(db, ticketOwner.id) ile bulunur.

      • Bu dile göre başlık ve mesaj üretilir (TR/EN metinleri).

      • createNotification ile uygulama içi bildirim oluşturulur:

        createNotification(db, {
        userId: ticketOwner.id,
        type: "system",
        title,
        message,
        link: "/account/support",
        sendEmailOpt: false,
        });
      • Ardından sendSupportTicketResolvedEmail(...) ile çözüm e‑postası tetiklenir.

  • Yetki: Sadece user.role === "admin".

Projede UI odaklı kullanım için iki ana sorgu kullanılır:

  • myTickets — Öğrenci/eğitmen hesabındaki destek taleplerini döner (liste + mesajlar).
  • adminTickets(status?) — Admin panel için ticket + mesajları Kanban’a uygun formatta döner.

Bu sorguların tam şekli zamanla evrilebilir; güncel kullanım için account.support.tsx ve admin.support.tsx loader’larına bakın.


3. AI Destek Asistanı (Ticket Deflection)

Section titled “3. AI Destek Asistanı (Ticket Deflection)”

Dosya: app/routes/api.support-deflect.ts
Route: POST /api/support-deflect

  • Öğrenci formu doldurmadan önce, serbest metin alanına sorununu yazıyor.
  • Bu metin Cloudflare Workers AI (@cf/meta/llama-3.1-8b-instruct) ile analiz edilerek:
    • Net bir cevap önerisi (answer),
    • Yaklaşık bir güven skoru (confidence) döndürülüyor.
  • Kullanıcı cevabı yeterli bulursa ticket açmadan süreci sonlandırabiliyor; aksi halde tam ticket formu açılıyor.
{ "query": "Geçen hafta aldığım React kursunun faturası mailime gelmedi..." }
{
"answer": "Merhaba, faturalarınız ... menüsünden indirilebilir. Ayrıca e-posta klasörünüzde ... kontrol edin.",
"confidence": 85
}

AI modeli zaman zaman cevabı json ... içinde döndürebilir. Endpoint içinde:

  • Markdown kod blokları ( ```json, ```) temizlenir.
  • İlk { ve son } arasındaki kısım alınarak JSON.parse edilir.

Hata durumunda klasik "AI servisi şu anda kullanılamıyor" mesajı döndürülür ve UI direkt ticket formuna geçer.


4. Öğrenci/Eğitmen Arayüzü (account/support)

Section titled “4. Öğrenci/Eğitmen Arayüzü (account/support)”

Dosya: app/routes/account.support.tsx
Route: /:lang/account/support

  1. Başlık kartı

    • Sol: LifeBuoy ikonlu mavi kare (Destek Merkezi).
    • Sağ (liste görünümünde): "Yeni Destek Talebi" butonu (Add Card stilinde).
  2. Ticket listesi (tablo)

    • Sütunlar: Konu & Kategori, Durum, Son Güncelleme.
    • Satırlarda:
      • Konu başlığı (tek satır, line-clamp-1),
      • Kategori etiketi (payment, technical vb. için i18n label),
      • Durum rozeti (open / in_progress / resolved / closed).
    • Her satırda “İncele” butonu → detay görünümünü açar.
  3. AI asistan görünümü (Yeni talep > adım 1)

    • Kullanıcı önce serbest metin alanına sorununu yazar.
    • "Çözüm Bul" butonu /api/support-deflect çağrısını tetikler.
    • AI cevabı kartta gösterilir; kullanıcı:
      • Yanıt yeterli ise → listeye dönebilir,
      • Yetersiz ise → tam ticket formuna geçer.
  4. Tam ticket formu (adım 2)

    • Kategori seçimi (payment, technical, instructor, content, other).
    • Konu başlığı ve detaylı açıklama alanı.
    • createSupportTicket mutation’ını tetikleyen "Talebi Gönder" butonu.
  5. Detay görünümü (chat)

    • Üst şerit: Ticket ikonu, kısa subject, kategori etiketi, durum badge’i.
    • Mesaj alanı: Kullanıcının ve adminin mesajları konuşma balonları şeklinde.
    • Alt alan: Kullanıcının yeni mesaj yazabileceği textarea + "Gönder" butonu.
    • replySupportTicket mutation’ı kullanılır; kullanıcı tekrar yazdığında gerekirse ticket otomatik "open" olur.
  • useRevalidator + setInterval ile her 20 saniyede bir loader yeniden çalıştırılır.
  • Böylece kullanıcı, sayfayı yenilemeden admin’den gelen yanıtları görebilir.

Dosya: app/routes/admin.support.tsx
Route: /admin/support

  • Sütunlar: Yeni Talepler (open), İnceleniyor (in_progress), Çözüldü (resolved), Kapalı (closed).
  • Her kolonda:
    • Kart başında öncelik rozeti (high/medium/low).
    • Konu başlığı, kategori label’ı.
    • Kullanıcı avatarı/adı ve mesaj sayısı.
  • Kartlar sürüklenip farklı statü sütunlarına bırakılabilir:
mutation UpdateTicketStatus($ticketId: ID!, $status: String!) {
updateTicketStatus(ticketId: $ticketId, status: $status)
}
  • Sağdan açılan panelde:
    • Üst header: Ticket #, kategori etiketi, kısa konu, kullanıcı e‑postası + bağlam URL’i (varsa).
    • Ortada mesaj geçmişi (admin ve kullanıcı balonları).
    • Altta cevap yazma alanı:
      • Textarea,
      • İleride dosya ekine uygun paperclip butonu,
      • "Gönder" butonu → replySupportTicket.

Admin bu panelden cevap yazdığında:

  • Ticket durumu otomatik "resolved" olur.
  • Öğrenciye yanıt e‑postası gider.
  • Admin sayfasında useRevalidator ile 15 saniyede bir veriler tazelenir.
  • Sidebar’da "Destek" menüsü yanında açık ticket sayısı gösterilir:
    • Dosya: app/lib/db-queries.tsgetAdminNotificationCounts içinde openSupportTickets alanı.
    • Dosya: app/routes/admin.tsx"Destek" linki badge olarak counts.openSupportTickets kullanır.

Dosya: app/lib/email.ts

Üç ana destek e‑postası vardır:

  1. Talep Alındı (Auto‑Responder)sendSupportTicketCreatedEmail
    • Konu: Talebiniz Alındı: {subject} (#XXXX)
    • İçerik: Ticket numarası, özet, "Talebi Görüntüle" butonu.
  2. Talebe Yanıt GeldisendSupportTicketReplyEmail
    • Konu: Yeni Yanıt: {subject} (#XXXX)
    • İçerik: Admin mesajının kısa özeti, "Yanıt Ver / İncele" butonu.
  3. Talep Çözüldü / KapatıldısendSupportTicketResolvedEmail
    • Konu: Talebiniz Çözüldü: {subject} (#XXXX)
    • İçerik: Ticketın çözüldüğünü belirten metin, "Destek Merkezine Git" butonu.

Bu fonksiyonlar, GraphQL mutasyonlarında runBackground(context, ...) ile non‑blocking şekilde çağrılır.

Dosya: app/lib/notifications.ts, app/lib/notification-messages.ts
Detay: Bildirim Sistemi (Uygulama İçi)

Support sistemi için özellikle:

  • updateTicketStatus "resolved" veya "closed" olduğunda:
    • type: "system" bir notification oluşturulur.
    • Kullanıcı /account/support ekranına yönlenebileceği link: "/account/support" ile başlık/mesaj alır.
    • Başlık/mesaj dili, getNotificationLangForUser ile kullanıcının ülkesine göre seçilir.

AdımOlayArka Plan İşlemleri
1Kullanıcı /account/support üzerinden AI asistana sorununu yazar/api/support-deflect çağrılır; yeterli değilse form açılır.
2Kullanıcı tam formu doldurup gönderircreateSupportTicket → DB insert, ilk mesaj, auto‑responder e‑posta.
3Admin /admin/support kanban’ında bileti görüradminTickets loader; status sütunlarına göre listeler.
4Admin bileti sürükleyerek sütun değiştirirupdateTicketStatusstatus güncellenir; resolved/closed ise in‑app notification + çözüm e‑postası.
5Admin detay panelinden yanıt yazarreplySupportTicket → mesaj insert, status = "resolved", reply e‑postası gönderilir.
6Kullanıcı çözülen bilete tekrar yanıt yazarreplySupportTicket → önceki status resolved/closed ise ticket yeniden "open" olur; admin kanban’ında ilk sütuna döner.

Bu mimari sayesinde:

  • Tüm destek talepleri tek tabloda izlenir,
  • Admin ve kullanıcı tarafında chat‑benzeri bir deneyim sağlanır,
  • AI ile ilk temas yükü azaltılır,
  • E‑posta ve uygulama içi bildirimlerle kullanıcıya güçlü bir “sesim duyuluyor” hissi verilir.