Spaces:
Sleeping
Sleeping
| 'use client'; | |
| export const ACENTE_FEEDBACK_KEY = 'ajet360.acente.feedback.v1'; | |
| export const ACENTE_FEEDBACK_EVENT = 'ajet360:acente-feedback'; | |
| export type FeedbackCategory = 'Şikayet' | 'Öneri' | 'Teşekkür' | 'Teknik Sorun' | 'Sözleşme/Komisyon'; | |
| export type FeedbackStatus = 'Gönderildi' | 'AJet İncelemede' | 'Yanıtlandı' | 'Kapatıldı'; | |
| export type FeedbackReply = { | |
| at: string; | |
| from: 'AJet' | 'Acente'; | |
| authorName: string; | |
| body: string; | |
| }; | |
| export type Feedback = { | |
| id: string; | |
| agencyCode: string; | |
| category: FeedbackCategory; | |
| subject: string; | |
| body: string; | |
| status: FeedbackStatus; | |
| createdAt: string; | |
| replies: FeedbackReply[]; | |
| }; | |
| const SEED_FEEDBACKS: Feedback[] = [ | |
| { | |
| id: 'fb-demo-001', | |
| agencyCode: 'AJ-IST-001', | |
| category: 'Sözleşme/Komisyon', | |
| subject: 'Sözleşme komisyon yapısı hakkında', | |
| body: 'Yeni dönem sözleşmesinde Q3 için komisyon dilimlerinin yeniden değerlendirilmesini talep ediyorum. Mevcut yapıda %12 komisyon hedef hacmin altında kalıyor; %14\'e çıkarılması mümkün mü?', | |
| status: 'Yanıtlandı', | |
| createdAt: '2026-04-18T09:30:00.000Z', | |
| replies: [ | |
| { | |
| at: '2026-04-19T11:00:00.000Z', | |
| from: 'AJet', | |
| authorName: 'Mehmet Yılmaz · Satış Müdürü', | |
| body: 'Talebinizi inceledik. Q3 itibarıyla %14 komisyon dilimine geçişiniz için gerekli değerlendirme süreci başlatıldı. 7 iş günü içinde yeni sözleşme taslağı tarafınıza iletilecektir.', | |
| }, | |
| { | |
| at: '2026-04-19T14:20:00.000Z', | |
| from: 'Acente', | |
| authorName: 'Istanbul Jet Travel', | |
| body: 'Hızlı dönüş için teşekkürler, taslağı bekliyoruz.', | |
| }, | |
| ], | |
| }, | |
| { | |
| id: 'fb-demo-002', | |
| agencyCode: 'AJ-IST-001', | |
| category: 'Teknik Sorun', | |
| subject: 'Crane Pax bakiye yükleme yavaş', | |
| body: 'Son 1 haftadır Crane Pax üzerinden bakiye yüklemesi 5–7 dakika sürüyor. Önceden anında yansıyordu. İncelenmesi mümkün mü?', | |
| status: 'AJet İncelemede', | |
| createdAt: '2026-04-23T08:45:00.000Z', | |
| replies: [ | |
| { | |
| at: '2026-04-23T10:15:00.000Z', | |
| from: 'AJet', | |
| authorName: 'Sistem Destek', | |
| body: 'Geri bildiriminiz alındı. Crane Pax entegrasyon ekibimizle paylaşıldı, 24 saat içinde dönüş yapılacaktır.', | |
| }, | |
| ], | |
| }, | |
| { | |
| id: 'fb-demo-003', | |
| agencyCode: 'AJ-ANK-001', | |
| category: 'Teşekkür', | |
| subject: 'Yeni eğitim için teşekkürler', | |
| body: 'Geçen hafta düzenlenen PNR v2 eğitimi çok faydalıydı, ekipçe katıldık. Bu tip eğitimlerin devamını rica ediyoruz.', | |
| status: 'Yanıtlandı', | |
| createdAt: '2026-04-15T16:00:00.000Z', | |
| replies: [ | |
| { | |
| at: '2026-04-16T09:00:00.000Z', | |
| from: 'AJet', | |
| authorName: 'Eğitim Koordinatörü', | |
| body: 'Geri dönüşünüz için teşekkür ederiz. Mayıs ayında Crane Pax kullanımı eğitimi planlıyoruz, davetiye e-postanız gelecek.', | |
| }, | |
| ], | |
| }, | |
| ]; | |
| function canUseStorage() { | |
| return typeof window !== 'undefined' && Boolean(window.localStorage); | |
| } | |
| function readAll(): Feedback[] { | |
| if (!canUseStorage()) return SEED_FEEDBACKS; | |
| const raw = window.localStorage.getItem(ACENTE_FEEDBACK_KEY); | |
| if (!raw) { | |
| return SEED_FEEDBACKS; | |
| } | |
| try { | |
| const parsed = JSON.parse(raw) as Feedback[]; | |
| if (!Array.isArray(parsed)) return SEED_FEEDBACKS; | |
| const seedIds = new Set(SEED_FEEDBACKS.map((s) => s.id)); | |
| const userOnes = parsed.filter((f) => !seedIds.has(f.id)); | |
| return [...userOnes, ...SEED_FEEDBACKS]; | |
| } catch { | |
| return SEED_FEEDBACKS; | |
| } | |
| } | |
| function writeAll(items: Feedback[]) { | |
| if (!canUseStorage()) return; | |
| const seedIds = new Set(SEED_FEEDBACKS.map((s) => s.id)); | |
| const persisted = items.filter((f) => !seedIds.has(f.id)); | |
| const seedOverrides = items.filter( | |
| (f) => seedIds.has(f.id) && hasNewReplies(f), | |
| ); | |
| window.localStorage.setItem( | |
| ACENTE_FEEDBACK_KEY, | |
| JSON.stringify([...persisted, ...seedOverrides]), | |
| ); | |
| window.dispatchEvent(new Event(ACENTE_FEEDBACK_EVENT)); | |
| } | |
| function hasNewReplies(f: Feedback): boolean { | |
| const seed = SEED_FEEDBACKS.find((s) => s.id === f.id); | |
| if (!seed) return false; | |
| return f.replies.length > seed.replies.length || f.status !== seed.status; | |
| } | |
| export function listFeedbacks(agencyCode: string): Feedback[] { | |
| return readAll() | |
| .filter((f) => f.agencyCode === agencyCode) | |
| .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); | |
| } | |
| export function getFeedbackById(id: string): Feedback | undefined { | |
| return readAll().find((f) => f.id === id); | |
| } | |
| export type CreateFeedbackInput = { | |
| agencyCode: string; | |
| category: FeedbackCategory; | |
| subject: string; | |
| body: string; | |
| }; | |
| export function createFeedback(input: CreateFeedbackInput): Feedback { | |
| const now = new Date().toISOString(); | |
| const ts = now.replace(/\D/g, '').slice(0, 14); | |
| const fb: Feedback = { | |
| id: `fb-${ts}-${Math.random().toString(36).slice(2, 6)}`, | |
| agencyCode: input.agencyCode, | |
| category: input.category, | |
| subject: input.subject, | |
| body: input.body, | |
| status: 'Gönderildi', | |
| createdAt: now, | |
| replies: [], | |
| }; | |
| writeAll([fb, ...readAll()]); | |
| return fb; | |
| } | |
| export function appendReply(id: string, reply: Omit<FeedbackReply, 'at'> & { at?: string }): Feedback | undefined { | |
| const all = readAll(); | |
| const idx = all.findIndex((f) => f.id === id); | |
| if (idx < 0) return undefined; | |
| const at = reply.at ?? new Date().toISOString(); | |
| const next = { ...all[idx]!, replies: [...all[idx]!.replies, { ...reply, at }] }; | |
| if (reply.from === 'AJet') { | |
| next.status = 'Yanıtlandı'; | |
| } | |
| const arr = [...all]; | |
| arr[idx] = next; | |
| writeAll(arr); | |
| return next; | |
| } | |
| export function transitionStatus(id: string, status: FeedbackStatus): Feedback | undefined { | |
| const all = readAll(); | |
| const idx = all.findIndex((f) => f.id === id); | |
| if (idx < 0) return undefined; | |
| const next = { ...all[idx]!, status }; | |
| const arr = [...all]; | |
| arr[idx] = next; | |
| writeAll(arr); | |
| return next; | |
| } | |
| export function simulateAjetAutoReply(id: string) { | |
| setTimeout(() => { | |
| appendReply(id, { | |
| from: 'AJet', | |
| authorName: 'AJet Acente İlişkileri', | |
| body: 'Geri bildiriminiz alındı. Konuyla ilgili 24 saat içinde dönüş yapılacaktır. Acil durumlar için temsilcinizle iletişime geçebilirsiniz.', | |
| }); | |
| }, 8000); | |
| setTimeout(() => { | |
| transitionStatus(id, 'AJet İncelemede'); | |
| }, 1500); | |
| } | |