File size: 5,171 Bytes
076c536
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<template>
  <div class="space-y-2 rounded-lg border border-[var(--border)] bg-[var(--bg)] p-3">
    <div class="flex items-center justify-between gap-3">
      <div class="min-w-0">
        <p class="text-[12px] font-semibold text-[var(--ink)]">Push notification</p>
        <p class="mt-0.5 text-[11px] text-[var(--ink3)]">{{ statusText }}</p>
      </div>
      <span
        class="shrink-0 rounded-md px-2 py-1 text-[10px] font-semibold"
        :class="badgeClass"
      >
        {{ badgeText }}
      </span>
    </div>

    <div class="flex gap-2">
      <button
        type="button"
        class="ct-btn flex-1 px-3 py-2 text-[12px]"
        :disabled="primaryDisabled"
        @click="handlePrimary"
      >
        <svg v-if="loading" class="mr-1.5 inline h-3.5 w-3.5 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M21 12a9 9 0 1 1-6.219-8.56"/>
        </svg>
        {{ primaryLabel }}
      </button>
      <button
        type="button"
        class="btn btn-primary flex-1 text-[12px]"
        :disabled="!enabled || loading || testing"
        @click="handleTest"
      >
        <svg v-if="testing" class="mr-1.5 inline h-3.5 w-3.5 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M21 12a9 9 0 1 1-6.219-8.56"/>
        </svg>
        Kiểm tra
      </button>
    </div>

    <p v-if="message" class="text-[11px]" :class="messageClass">{{ message }}</p>
  </div>
</template>

<script setup>
import { computed, onMounted, ref, watch } from 'vue'
import { useAuthStore } from '@/stores/auth.js'
import {
  getCurrentSubscription,
  isPushNotificationSupported,
  sendTestPushNotification,
  subscribeUserToPush,
  unsubscribeUserFromPush,
} from '@/services/pushNotification.js'

const emit = defineEmits(['changed'])
const auth = useAuthStore()

const loading = ref(false)
const testing = ref(false)
const supported = ref(true)
const enabled = ref(false)
const permission = ref(typeof Notification !== 'undefined' ? Notification.permission : 'default')
const message = ref('')
const messageType = ref('success')

const isAuthenticated = computed(() => auth.isAuthenticated)

const statusText = computed(() => {
  if (!supported.value) return 'Trình duyệt này chưa hỗ trợ Web Push.'
  if (!isAuthenticated.value) return 'Đăng nhập để bật thông báo.'
  if (permission.value === 'denied') return 'Bạn đã chặn quyền thông báo trong trình duyệt.'
  if (enabled.value) return 'Đã bật cho trình duyệt hiện tại.'
  return 'Chưa bật cho trình duyệt hiện tại.'
})

const badgeText = computed(() => {
  if (!supported.value) return 'Không hỗ trợ'
  if (!isAuthenticated.value) return 'Chưa đăng nhập'
  if (permission.value === 'denied') return 'Bị chặn'
  return enabled.value ? 'Đã bật' : 'Chưa bật'
})

const badgeClass = computed(() => {
  if (enabled.value) return 'bg-[#dcfce7] text-[#047857]'
  if (!supported.value || permission.value === 'denied') return 'bg-[#fee2e2] text-[#b91c1c]'
  return 'bg-[#fef3c7] text-[#92400e]'
})

const primaryLabel = computed(() => {
  if (loading.value) return 'Đang xử lý'
  return enabled.value ? 'Tắt' : 'Bật thông báo'
})

const primaryDisabled = computed(() => (
  loading.value ||
  !supported.value ||
  !isAuthenticated.value ||
  permission.value === 'denied'
))

const messageClass = computed(() => (
  messageType.value === 'error' ? 'text-[#b91c1c]' : 'text-[#047857]'
))

async function refreshState() {
  supported.value = isPushNotificationSupported()
  permission.value = typeof Notification !== 'undefined' ? Notification.permission : 'default'
  if (!supported.value || !isAuthenticated.value) {
    enabled.value = false
    return
  }
  enabled.value = !!(await getCurrentSubscription())
}

async function handlePrimary() {
  loading.value = true
  message.value = ''
  try {
    if (enabled.value) {
      await unsubscribeUserFromPush()
      enabled.value = false
      message.value = 'Đã tắt push notification.'
    } else {
      await subscribeUserToPush()
      permission.value = Notification.permission
      enabled.value = true
      message.value = 'Đã bật push notification.'
    }
    messageType.value = 'success'
    emit('changed')
  } catch (err) {
    permission.value = typeof Notification !== 'undefined' ? Notification.permission : 'default'
    messageType.value = 'error'
    message.value = err?.response?.data?.detail || err?.message || 'Không thể cập nhật push notification.'
    await refreshState()
  } finally {
    loading.value = false
  }
}

async function handleTest() {
  testing.value = true
  message.value = ''
  try {
    const data = await sendTestPushNotification()
    messageType.value = 'success'
    message.value = `Đã gửi ${data.sent || 0} thông báo kiểm tra.`
  } catch (err) {
    messageType.value = 'error'
    message.value = err?.response?.data?.detail || 'Không gửi được thông báo kiểm tra.'
  } finally {
    testing.value = false
  }
}

onMounted(refreshState)
watch(isAuthenticated, refreshState)
</script>