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/supportekranı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.
1. Veritabanı Şeması
Section titled “1. Veritabanı Şeması”Dosya: app/db/schema.ts
1.1 support_tickets
Section titled “1.1 support_tickets”| Alan | Tip | Açıklama |
|---|---|---|
id | uuid PK | Ticket kimliği. |
userId | text FK | Talebi açan kullanıcı (users.id). |
subject | text | Konu başlığı. |
category | text | Kategori: payment, technical, instructor, content, other. |
status | text | open, in_progress, resolved, closed. |
priority | text | low, medium, high — kanban kartı badge’i için. |
urlContext | text | Kullanıcının hatayı aldığı URL (örn. /learn/react-101). |
createdAt | timestamp | Oluşturulma tarihi. |
updatedAt | timestamp | Son güncelleme tarihi. |
Ek olarak:
support_tickets_status_idx—statusalanı için index.support_tickets_user_idx—userIdalanı için index.
1.2 support_messages
Section titled “1.2 support_messages”| Alan | Tip | Açıklama |
|---|---|---|
id | uuid PK | Mesaj kimliği. |
ticketId | uuid FK | İlgili ticket (support_tickets.id). |
senderId | text FK | Mesajı atan kullanıcı (users.id). |
isAdmin | boolean | Admin mi? (admin mesajlarını UI’da farklı renkle). |
message | text | Mesaj içeriği. |
attachmentUrl | text | Opsiyonel: ileride ekran görüntüsü vb. için. |
createdAt | timestamp | Gö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
2.1 Mutations
Section titled “2.1 Mutations”createSupportTicket(subject, category, message, urlContext?)
Section titled “createSupportTicket(subject, category, message, urlContext?)”-
Amaç: Öğrenci/eğitmenin yeni bir destek talebi açması.
-
Çalışma:
-
support_ticketstablosuna yeni satır eklenir (status = "open",priority = "medium"varsayılan). -
support_messagestablosuna kullanıcının ilk mesajı eklenir (isAdmin = false). -
runBackgroundile otomatik karşılama e‑postası tetiklenir:sendSupportTicketCreatedEmail(user.email,user.name,newTicket.id,subject,"https://achidemy.net/account/support",);
-
-
Yetki:
userzorunlu;sessionyoksa hata.
replySupportTicket(ticketId, message)
Section titled “replySupportTicket(ticketId, message)”-
Amaç: Kullanıcı veya adminin mevcut bilete mesaj eklemesi.
-
Çalışma:
-
support_messagesiçine yeni mesaj insert edilir (isAdminrole’a göre). -
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".
- Admin cevap verirse →
-
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:
userzorunlu;user.role === "admin"veya normal kullanıcı olabilir.
updateTicketStatus(ticketId, status)
Section titled “updateTicketStatus(ticketId, status)”-
Amaç: Admin kanban ekranından sürükle‑bırak ile durum güncellemek.
-
Çalışma:
support_tickets.statusalanı doğrudan verilen değere (open,in_progress,resolved,closed) set edilir.- 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).
-
createNotificationile 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".
2.2 Sorgular
Section titled “2.2 Sorgular”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
3.1 Amaç
Section titled “3.1 Amaç”- Öğ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.
- Net bir cevap önerisi (
- Kullanıcı cevabı yeterli bulursa ticket açmadan süreci sonlandırabiliyor; aksi halde tam ticket formu açılıyor.
3.2 İstek / Yanıt Şekli
Section titled “3.2 İstek / Yanıt Şekli”İstek body
Section titled “İstek body”{ "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}3.3 JSON Temizleme Mantığı
Section titled “3.3 JSON Temizleme Mantığı”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ınarakJSON.parseedilir.
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
4.1 Sayfa Yapısı
Section titled “4.1 Sayfa Yapısı”-
Başlık kartı
- Sol:
LifeBuoyikonlu mavi kare (Destek Merkezi). - Sağ (liste görünümünde):
"Yeni Destek Talebi"butonu (Add Card stilinde).
- Sol:
-
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,technicalvb. için i18n label), - Durum rozeti (open / in_progress / resolved / closed).
- Konu başlığı (tek satır,
- Her satırda “İncele” butonu → detay görünümünü açar.
-
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.
-
Tam ticket formu (adım 2)
- Kategori seçimi (
payment,technical,instructor,content,other). - Konu başlığı ve detaylı açıklama alanı.
createSupportTicketmutation’ını tetikleyen"Talebi Gönder"butonu.
- Kategori seçimi (
-
Detay görünümü (chat)
- Üst şerit:
Ticketikonu, 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. replySupportTicketmutation’ı kullanılır; kullanıcı tekrar yazdığında gerekirse ticket otomatik"open"olur.
- Üst şerit:
4.2 Polling (Canlı Güncelleme)
Section titled “4.2 Polling (Canlı Güncelleme)”useRevalidator+setIntervalile her 20 saniyede bir loader yeniden çalıştırılır.- Böylece kullanıcı, sayfayı yenilemeden admin’den gelen yanıtları görebilir.
5. Admin Destek Masası (admin/support)
Section titled “5. Admin Destek Masası (admin/support)”Dosya: app/routes/admin.support.tsx
Route: /admin/support
5.1 Kanban Görünümü
Section titled “5.1 Kanban Görünümü”- 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ı.
- Kart başında öncelik rozeti (
- Kartlar sürüklenip farklı statü sütunlarına bırakılabilir:
mutation UpdateTicketStatus($ticketId: ID!, $status: String!) { updateTicketStatus(ticketId: $ticketId, status: $status)}5.2 Bilet Detay Slide‑Over
Section titled “5.2 Bilet Detay Slide‑Over”- 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.
5.3 Polling ve Badge
Section titled “5.3 Polling ve Badge”- Admin sayfasında
useRevalidatorile 15 saniyede bir veriler tazelenir. - Sidebar’da
"Destek"menüsü yanında açık ticket sayısı gösterilir:- Dosya:
app/lib/db-queries.ts→getAdminNotificationCountsiçindeopenSupportTicketsalanı. - Dosya:
app/routes/admin.tsx→"Destek"linki badge olarakcounts.openSupportTicketskullanır.
- Dosya:
6. E‑posta Şablonları ve Bildirimler
Section titled “6. E‑posta Şablonları ve Bildirimler”Dosya: app/lib/email.ts
Üç ana destek e‑postası vardır:
- Talep Alındı (Auto‑Responder) —
sendSupportTicketCreatedEmail- Konu:
Talebiniz Alındı: {subject} (#XXXX) - İçerik: Ticket numarası, özet,
"Talebi Görüntüle"butonu.
- Konu:
- Talebe Yanıt Geldi —
sendSupportTicketReplyEmail- Konu:
Yeni Yanıt: {subject} (#XXXX) - İçerik: Admin mesajının kısa özeti,
"Yanıt Ver / İncele"butonu.
- Konu:
- Talep Çözüldü / Kapatıldı —
sendSupportTicketResolvedEmail- Konu:
Talebiniz Çözüldü: {subject} (#XXXX) - İçerik: Ticketın çözüldüğünü belirten metin,
"Destek Merkezine Git"butonu.
- Konu:
Bu fonksiyonlar, GraphQL mutasyonlarında runBackground(context, ...) ile non‑blocking şekilde çağrılır.
6.1 Uygulama İçi Bildirimler
Section titled “6.1 Uygulama İçi Bildirimler”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/supportekranına yönlenebileceğilink: "/account/support"ile başlık/mesaj alır. - Başlık/mesaj dili,
getNotificationLangForUserile kullanıcının ülkesine göre seçilir.
7. Özet Akış
Section titled “7. Özet Akış”| Adım | Olay | Arka Plan İşlemleri |
|---|---|---|
| 1 | Kullanıcı /account/support üzerinden AI asistana sorununu yazar | /api/support-deflect çağrılır; yeterli değilse form açılır. |
| 2 | Kullanıcı tam formu doldurup gönderir | createSupportTicket → DB insert, ilk mesaj, auto‑responder e‑posta. |
| 3 | Admin /admin/support kanban’ında bileti görür | adminTickets loader; status sütunlarına göre listeler. |
| 4 | Admin bileti sürükleyerek sütun değiştirir | updateTicketStatus → status güncellenir; resolved/closed ise in‑app notification + çözüm e‑postası. |
| 5 | Admin detay panelinden yanıt yazar | replySupportTicket → mesaj insert, status = "resolved", reply e‑postası gönderilir. |
| 6 | Kullanıcı çözülen bilete tekrar yanıt yazar | replySupportTicket → ö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.