@kompasid/lit-web-components 0.8.7 → 0.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/empty-state-notification.png +0 -0
- package/demo/index.html +24 -7
- package/dist/src/components/kompasid-grace-period/KompasGracePeriod.d.ts +1 -0
- package/dist/src/components/kompasid-grace-period/KompasGracePeriod.js +20 -6
- package/dist/src/components/kompasid-grace-period/KompasGracePeriod.js.map +1 -1
- package/dist/src/components/kompasid-header-notification/KompasHeaderNotification.d.ts +35 -0
- package/dist/src/components/kompasid-header-notification/KompasHeaderNotification.js +482 -0
- package/dist/src/components/kompasid-header-notification/KompasHeaderNotification.js.map +1 -0
- package/dist/src/components/kompasid-header-notification/types.d.ts +35 -0
- package/dist/src/components/kompasid-header-notification/types.js +2 -0
- package/dist/src/components/kompasid-header-notification/types.js.map +1 -0
- package/dist/src/components/kompasid-header-notification/utils.d.ts +21 -0
- package/dist/src/components/kompasid-header-notification/utils.js +43 -0
- package/dist/src/components/kompasid-header-notification/utils.js.map +1 -0
- package/dist/src/components/kompasid-paywall-body/KompasPaywallBody.js +2 -1
- package/dist/src/components/kompasid-paywall-body/KompasPaywallBody.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/components/kompasid-grace-period/KompasGracePeriod.ts +24 -6
- package/src/components/kompasid-header-notification/KompasHeaderNotification.ts +548 -0
- package/src/components/kompasid-header-notification/types.ts +39 -0
- package/src/components/kompasid-header-notification/utils.ts +86 -0
- package/src/components/kompasid-paywall-body/KompasPaywallBody.ts +2 -1
- package/src/index.ts +1 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit'
|
|
2
|
+
import { format, isToday, formatDistanceStrict, subDays } from 'date-fns'
|
|
3
|
+
import { id } from 'date-fns/locale/id'
|
|
4
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
|
5
|
+
import { unsafeSVG } from 'lit/directives/unsafe-svg.js'
|
|
6
|
+
import { getFontAwesomeIcon } from '../../utils/fontawesome-setup.js'
|
|
7
|
+
import { TWStyles } from '../../../tailwind/tailwind.js'
|
|
8
|
+
import {
|
|
9
|
+
UserNotification,
|
|
10
|
+
NotificationList,
|
|
11
|
+
ApiResponse,
|
|
12
|
+
ApiRubrikResponse,
|
|
13
|
+
} from './types.js'
|
|
14
|
+
import { customFetch } from './utils.js'
|
|
15
|
+
|
|
16
|
+
@customElement('kompasid-header-notification')
|
|
17
|
+
export class KompasHeaderNotification extends LitElement {
|
|
18
|
+
static styles = [
|
|
19
|
+
css`
|
|
20
|
+
:host {
|
|
21
|
+
font-family: 'PT Sans', sans-serif;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.header-account--notification-indicator {
|
|
25
|
+
position: absolute;
|
|
26
|
+
top: 0;
|
|
27
|
+
height: 0.5rem;
|
|
28
|
+
width: 0.5rem;
|
|
29
|
+
background-color: #ff7a00;
|
|
30
|
+
border-radius: 50%;
|
|
31
|
+
right: -0.403rem;
|
|
32
|
+
top: -0.281rem;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.header-notificaion-dropdown {
|
|
36
|
+
width: 20.5rem;
|
|
37
|
+
background-color: #ffffff;
|
|
38
|
+
font-family: 'PT Sans', sans-serif;
|
|
39
|
+
font-size: 0.875rem;
|
|
40
|
+
position: absolute;
|
|
41
|
+
right: -7.5vmax;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.w-14 {
|
|
45
|
+
width: 3.25rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.h-\\[300px\\] {
|
|
49
|
+
height: 300px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.h-14 {
|
|
53
|
+
height: 3.25rem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.bg-green-100 {
|
|
57
|
+
--tw-bg-opacity: 1;
|
|
58
|
+
background-color: rgb(238 252 210 / var(--tw-bg-opacity));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.overflow-y-scroll {
|
|
62
|
+
overflow-y: scroll;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.link-active {
|
|
66
|
+
color: #0468cb;
|
|
67
|
+
border-color: #0468cb;
|
|
68
|
+
border-bottom-width: 3px;
|
|
69
|
+
background-color: transparent !important;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.animate-spin {
|
|
73
|
+
animation: spin 2s linear infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@keyframes spin {
|
|
77
|
+
from {
|
|
78
|
+
transform: rotate(0deg);
|
|
79
|
+
}
|
|
80
|
+
to {
|
|
81
|
+
transform: rotate(360deg);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.object-cover {
|
|
86
|
+
object-fit: cover;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.z-index-max {
|
|
90
|
+
z-index: 99999;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Custom Scrollbar for header */
|
|
94
|
+
/* width */
|
|
95
|
+
::-webkit-scrollbar {
|
|
96
|
+
width: 4px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Track */
|
|
100
|
+
::-webkit-scrollbar-track {
|
|
101
|
+
background: transparent;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Handle */
|
|
105
|
+
::-webkit-scrollbar-thumb {
|
|
106
|
+
background: #0356a8;
|
|
107
|
+
border-radius: 100px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Handle on hover */
|
|
111
|
+
::-webkit-scrollbar-thumb:hover {
|
|
112
|
+
background: #0356a8;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (min-width: 1024px) {
|
|
116
|
+
.header-notificaion-dropdown {
|
|
117
|
+
right: -4vmax;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@media (max-width: 360px) {
|
|
122
|
+
.header-notificaion-dropdown {
|
|
123
|
+
right: -10vmax;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
`,
|
|
127
|
+
TWStyles,
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
@state() isShowDropdown: boolean = false
|
|
131
|
+
@state() selectedTab: string = 'Info'
|
|
132
|
+
@state() notificationInfoData: UserNotification = {} as UserNotification
|
|
133
|
+
@state() notificationContentData: any[] = []
|
|
134
|
+
@state() isDataLoaded: boolean = false
|
|
135
|
+
|
|
136
|
+
@property({ type: String }) accessToken = '' // prod || dev
|
|
137
|
+
@property({ type: String }) refreshToken = '' // prod || dev
|
|
138
|
+
|
|
139
|
+
constructor() {
|
|
140
|
+
super()
|
|
141
|
+
this.handleClickOutside = this.handleClickOutside.bind(this)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Function to format date
|
|
146
|
+
*/
|
|
147
|
+
formatDate(date: string) {
|
|
148
|
+
// Check if the provided date is today
|
|
149
|
+
let result
|
|
150
|
+
if (isToday(date)) {
|
|
151
|
+
// Return time difference if it's today (e.g., "7 hours ago")
|
|
152
|
+
result = `${formatDistanceStrict(date, new Date(), { locale: id })} lalu`
|
|
153
|
+
} else {
|
|
154
|
+
// Return formatted date (e.g., "12 Sep 2024, 09:00")
|
|
155
|
+
result = format(date, 'dd MMM yyyy, HH:mm', { locale: id })
|
|
156
|
+
}
|
|
157
|
+
return result
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
override async connectedCallback() {
|
|
161
|
+
super.connectedCallback()
|
|
162
|
+
this.isShowDropdown = false
|
|
163
|
+
|
|
164
|
+
if (this.accessToken && this.refreshToken) await this.loadData()
|
|
165
|
+
document.addEventListener('click', this.handleClickOutside)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
override async disconnectedCallback() {
|
|
169
|
+
super.disconnectedCallback()
|
|
170
|
+
document.removeEventListener('click', this.handleClickOutside)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async apiFetch(url: string, options: any) {
|
|
174
|
+
const response = await customFetch(
|
|
175
|
+
url,
|
|
176
|
+
this.refreshToken,
|
|
177
|
+
this.accessToken,
|
|
178
|
+
{ ...options }
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return response
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private async getNotifRubrik() {
|
|
185
|
+
const req = this.apiFetch(
|
|
186
|
+
'https://cds.kompas.cloud/api/v1/article/pilihanku',
|
|
187
|
+
{
|
|
188
|
+
method: 'GET',
|
|
189
|
+
headers: {
|
|
190
|
+
'Content-Type': 'application/json',
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
return req
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private async getNotifList() {
|
|
198
|
+
const req = await this.apiFetch(
|
|
199
|
+
'https://api.kompas.cloud/account/api/v1/users/notification',
|
|
200
|
+
{
|
|
201
|
+
method: 'GET',
|
|
202
|
+
headers: {
|
|
203
|
+
'Content-Type': 'application/json',
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
return req
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private async getEpaperData() {
|
|
211
|
+
const formatDate = 'yyyy/MM/dd'
|
|
212
|
+
const startDate = format(subDays(new Date(), 1), formatDate)
|
|
213
|
+
const endDate = format(new Date(), formatDate).toString()
|
|
214
|
+
const req = await this.apiFetch(
|
|
215
|
+
`https://apiepaper.kompas.cloud/products?startDate=${startDate}&endDate=${endDate}&sort=desc`,
|
|
216
|
+
{
|
|
217
|
+
method: 'GET',
|
|
218
|
+
headers: {
|
|
219
|
+
'Content-Type': 'application/json',
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return req
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private async notifRead(notifId: string) {
|
|
228
|
+
const req = await this.apiFetch(
|
|
229
|
+
'https://api.kompas.cloud/account/api/v1/users/notification/read',
|
|
230
|
+
{
|
|
231
|
+
headers: {
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
},
|
|
234
|
+
method: 'PUT',
|
|
235
|
+
body: JSON.stringify({
|
|
236
|
+
notificationId: notifId,
|
|
237
|
+
}),
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
return req
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private async loadData() {
|
|
244
|
+
const apiCalls = [
|
|
245
|
+
this.getNotifList(),
|
|
246
|
+
this.getNotifRubrik(),
|
|
247
|
+
this.getEpaperData(),
|
|
248
|
+
]
|
|
249
|
+
this.isDataLoaded = false
|
|
250
|
+
|
|
251
|
+
Promise.all(apiCalls)
|
|
252
|
+
.then(responses =>
|
|
253
|
+
Promise.all(responses.map(response => response.json()))
|
|
254
|
+
)
|
|
255
|
+
.then(values => {
|
|
256
|
+
this.notificationInfoData = (
|
|
257
|
+
values[0] as ApiResponse<UserNotification>
|
|
258
|
+
).data
|
|
259
|
+
const rubrikData = (values[1] as ApiRubrikResponse<any>).result
|
|
260
|
+
const ePaperData = (values[2] as ApiResponse<any>).data[0]
|
|
261
|
+
const { publishDate, description, thumbnail, title, url } = ePaperData
|
|
262
|
+
|
|
263
|
+
const data = {
|
|
264
|
+
title,
|
|
265
|
+
category: [{ name: 'ePaper', slug: 'epaper' }],
|
|
266
|
+
publishedDateGmt: format(
|
|
267
|
+
new Date(publishDate),
|
|
268
|
+
'yyyy-MM-dd HH:mm:ss'
|
|
269
|
+
),
|
|
270
|
+
permalink: url,
|
|
271
|
+
thumbnails: {
|
|
272
|
+
sizes: { thumbnailSquareMedium: { permalink: thumbnail } },
|
|
273
|
+
},
|
|
274
|
+
excerpt: description,
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
this.notificationContentData = [data, ...rubrikData]
|
|
278
|
+
this.notificationContentData.sort(
|
|
279
|
+
(a: any, b: any) =>
|
|
280
|
+
new Date(b.publishedDateGmt).getTime() -
|
|
281
|
+
new Date(a.publishedDateGmt).getTime()
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
this.isDataLoaded = true
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private toggleDropdown() {
|
|
289
|
+
this.isShowDropdown = !this.isShowDropdown
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private notificationIcon() {
|
|
293
|
+
const notificationIndicator = () => {
|
|
294
|
+
if (!this.notificationInfoData.notificationCount) return html``
|
|
295
|
+
return html`<div
|
|
296
|
+
class="header-account--notification-indicator animate-ping"
|
|
297
|
+
></div>`
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return html`
|
|
301
|
+
<button
|
|
302
|
+
@click=${this.toggleDropdown}
|
|
303
|
+
class="cursor-pointer relative flex items-center"
|
|
304
|
+
>
|
|
305
|
+
${notificationIndicator()}
|
|
306
|
+
<div class="flex flex-row items-center self-center">
|
|
307
|
+
<div class="text-white cursor-pointer mt-0.5">
|
|
308
|
+
${unsafeSVG(getFontAwesomeIcon('fas', 'bell', 21, 21))}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</button>
|
|
312
|
+
`
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private handleClick = (value: string) => {
|
|
316
|
+
this.selectedTab = value
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private redirectTo = async (notifId: string, link: string) => {
|
|
320
|
+
const res = await this.notifRead(notifId)
|
|
321
|
+
|
|
322
|
+
if (res.status === 200) {
|
|
323
|
+
this.notificationInfoData = {
|
|
324
|
+
...this.notificationInfoData,
|
|
325
|
+
notificationList: this.notificationInfoData.notificationList.map(
|
|
326
|
+
notifItem => ({
|
|
327
|
+
...notifItem,
|
|
328
|
+
isRead:
|
|
329
|
+
notifItem.notificationId === notifId ? true : notifItem.isRead,
|
|
330
|
+
})
|
|
331
|
+
),
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (link) {
|
|
336
|
+
const url = new URL(decodeURIComponent(link))
|
|
337
|
+
|
|
338
|
+
window.open(url, '_blank')
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private handleClickOutside(event: Event) {
|
|
343
|
+
if (!this.isShowDropdown) return
|
|
344
|
+
|
|
345
|
+
const popup = this.shadowRoot?.getElementById('headerNotification')
|
|
346
|
+
const ev = event.composedPath()
|
|
347
|
+
if (this.isShowDropdown && popup && !ev?.includes(popup as HTMLElement)) {
|
|
348
|
+
this.isShowDropdown = false // Close the popup
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Notification Info
|
|
353
|
+
private notificationInfoSection() {
|
|
354
|
+
const { notificationList }: { notificationList: NotificationList[] } =
|
|
355
|
+
this.notificationInfoData
|
|
356
|
+
|
|
357
|
+
// Empty state
|
|
358
|
+
if (!notificationList.length)
|
|
359
|
+
return html`
|
|
360
|
+
<div class="text-center px-4 pt-6 pb-32">
|
|
361
|
+
<img
|
|
362
|
+
src="../../assets/empty-state-notification.png"
|
|
363
|
+
alt="empty-state-notification"
|
|
364
|
+
class="w-auto mx-auto"
|
|
365
|
+
/>
|
|
366
|
+
<p class="font-bold text-center text-lg py-2">Belum Ada Notifikasi</p>
|
|
367
|
+
<p class="px-2">
|
|
368
|
+
Kami akan memberitahukan Anda ketika ada informasi dan pemberitahuan
|
|
369
|
+
terbaru.
|
|
370
|
+
</p>
|
|
371
|
+
</div>
|
|
372
|
+
`
|
|
373
|
+
|
|
374
|
+
const notificationInfoListTemplate = (item: NotificationList) => {
|
|
375
|
+
const imgUrl =
|
|
376
|
+
item.label === 'Akun'
|
|
377
|
+
? `https://cdn-www.kompas.id/assets/notifikasi-akun.svg`
|
|
378
|
+
: `https://cdn-www.kompas.id/assets/langganan-anda-telah-berakhir.svg`
|
|
379
|
+
|
|
380
|
+
return html`<div class="w-3/4 pr-1">
|
|
381
|
+
<div class="flex items-center mb-1 text-xs">
|
|
382
|
+
<span class="text-green-500 bg-green-100 rounded-sm p-1 px-2">
|
|
383
|
+
${item.label}
|
|
384
|
+
</span>
|
|
385
|
+
<span class="text-grey-400 ml-2"
|
|
386
|
+
>${this.formatDate(item.time)}</span
|
|
387
|
+
>
|
|
388
|
+
</div>
|
|
389
|
+
<div>
|
|
390
|
+
<p class="text-sm font-bold">${item.title}</p>
|
|
391
|
+
<p class="text-sm">${item.description}</p>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
<div class="w-1/4">
|
|
395
|
+
<img src="${imgUrl}" alt="${item.label}-alt" class="w-14 h-14" />
|
|
396
|
+
</div>`
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return html`<div class="h-[300px] overflow-y-scroll">
|
|
400
|
+
${notificationList.map(
|
|
401
|
+
item =>
|
|
402
|
+
html`<button
|
|
403
|
+
class="flex w-full text-left items-start p-4 cursor-pointer justify-between ${!item.isRead
|
|
404
|
+
? 'bg-blue-100'
|
|
405
|
+
: ''}"
|
|
406
|
+
@click=${() => this.redirectTo(item.notificationId, item.link)}
|
|
407
|
+
>
|
|
408
|
+
${notificationInfoListTemplate(item)}
|
|
409
|
+
</button> `
|
|
410
|
+
)}
|
|
411
|
+
</div>
|
|
412
|
+
<!-- Footer Link -->
|
|
413
|
+
<div class="px-4 py-4 text-center">
|
|
414
|
+
<a href="#" @click="${() =>
|
|
415
|
+
this.redirectToNotification(
|
|
416
|
+
'info'
|
|
417
|
+
)}" class="text-base font-bold text-blue-500">
|
|
418
|
+
Lihat Selengkapnya</a
|
|
419
|
+
>
|
|
420
|
+
</div>
|
|
421
|
+
</div>`
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
private redirectToNotification(tab: string) {
|
|
425
|
+
window.open(
|
|
426
|
+
`https://account.kompas.id/manage-account/notification/${tab}`,
|
|
427
|
+
'_blank'
|
|
428
|
+
)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Notification Content
|
|
432
|
+
private notificationContentSection() {
|
|
433
|
+
// Empty state
|
|
434
|
+
if (!this.notificationContentData.length) {
|
|
435
|
+
return html`
|
|
436
|
+
<div class="text-center px-4 pt-6 pb-32">
|
|
437
|
+
<img
|
|
438
|
+
src="../../assets/empty-state-notification.png"
|
|
439
|
+
alt="empty-state-notification"
|
|
440
|
+
class="w-auto mx-auto"
|
|
441
|
+
/>
|
|
442
|
+
<p class="font-bold text-center text-lg py-2">Belum Ada Notifikasi</p>
|
|
443
|
+
<p class="px-2">
|
|
444
|
+
Kami akan memberitahukan Anda ketika ada informasi dan pemberitahuan
|
|
445
|
+
terbaru.
|
|
446
|
+
</p>
|
|
447
|
+
</div>
|
|
448
|
+
`
|
|
449
|
+
}
|
|
450
|
+
const notificationContentListTemplate = (item: any) => html`<div
|
|
451
|
+
class="w-3/4 pr-1 "
|
|
452
|
+
>
|
|
453
|
+
<div class="flex items-center mb-1 text-xs">
|
|
454
|
+
<span class="text-green-500 bg-green-100 rounded-sm p-1 px-2">
|
|
455
|
+
${item.category[0].name}
|
|
456
|
+
</span>
|
|
457
|
+
<span class="text-grey-400 ml-2">
|
|
458
|
+
${this.formatDate(item.publishedDateGmt)}</span
|
|
459
|
+
>
|
|
460
|
+
</div>
|
|
461
|
+
<p class="font-bold text-base text-customTextColor py-1">
|
|
462
|
+
${item.title}
|
|
463
|
+
</p>
|
|
464
|
+
</div>
|
|
465
|
+
<div class="w-1/4">
|
|
466
|
+
<img
|
|
467
|
+
src="${item.thumbnails.sizes.thumbnailSquareMedium.permalink}"
|
|
468
|
+
alt="content-img-alt"
|
|
469
|
+
class="w-14 h-14 object-cover"
|
|
470
|
+
/>
|
|
471
|
+
</div>`
|
|
472
|
+
|
|
473
|
+
return html`<div class="h-[300px] overflow-y-scroll">
|
|
474
|
+
${this.notificationContentData.map(
|
|
475
|
+
item =>
|
|
476
|
+
html`<button
|
|
477
|
+
class="flex w-full text-left items-start p-4 cursor-pointer justify-between ${!item.isRead
|
|
478
|
+
? 'bg-blue-100'
|
|
479
|
+
: ''}"
|
|
480
|
+
@click=${() => window.open(item.permalink, '_blank')}
|
|
481
|
+
>
|
|
482
|
+
${notificationContentListTemplate(item)}
|
|
483
|
+
</button> `
|
|
484
|
+
)}
|
|
485
|
+
</div>
|
|
486
|
+
<!-- Footer Link -->
|
|
487
|
+
<div class="px-4 py-4 text-center">
|
|
488
|
+
<a href="#" @click="${() =>
|
|
489
|
+
this.redirectToNotification(
|
|
490
|
+
'konten'
|
|
491
|
+
)}" class="text-base font-bold text-blue-500">
|
|
492
|
+
Lihat Selengkapnya</a
|
|
493
|
+
>
|
|
494
|
+
</div>
|
|
495
|
+
</div>`
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private notificationPopup() {
|
|
499
|
+
const tab = ['Info', 'Konten']
|
|
500
|
+
|
|
501
|
+
return html`
|
|
502
|
+
<div
|
|
503
|
+
id="notificationPopup"
|
|
504
|
+
class="header-notificaion-dropdown rounded-lg shadow-lg z-50 mt-2 ${this
|
|
505
|
+
.isShowDropdown
|
|
506
|
+
? 'active'
|
|
507
|
+
: ''}"
|
|
508
|
+
>
|
|
509
|
+
<div class="sticky">
|
|
510
|
+
<div class="flex justify-center py-2 border-b border-grey-300">
|
|
511
|
+
<span class="font-bold text-base">Notifikasi</span>
|
|
512
|
+
</div>
|
|
513
|
+
<!-- Tabs for Info and Content -->
|
|
514
|
+
<div class="flex justify-between">
|
|
515
|
+
${tab.map(
|
|
516
|
+
item =>
|
|
517
|
+
html`
|
|
518
|
+
<button
|
|
519
|
+
class="focus:outline-none py-2 w-1/2 ${this.selectedTab ===
|
|
520
|
+
item && 'link-active'}"
|
|
521
|
+
@click=${() => this.handleClick(item)}
|
|
522
|
+
>
|
|
523
|
+
${item}
|
|
524
|
+
</button>
|
|
525
|
+
`
|
|
526
|
+
)}
|
|
527
|
+
</div>
|
|
528
|
+
</div>
|
|
529
|
+
|
|
530
|
+
<!-- Notification Content -->
|
|
531
|
+
${this.selectedTab === 'Info'
|
|
532
|
+
? this.notificationInfoSection()
|
|
533
|
+
: this.notificationContentSection()}
|
|
534
|
+
</div>
|
|
535
|
+
`
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
render() {
|
|
539
|
+
return html`<div id="headerNotification" class="relative">
|
|
540
|
+
${this.isDataLoaded
|
|
541
|
+
? this.notificationIcon()
|
|
542
|
+
: html`<div class="text-white cursor-pointer mt-0.5 animate-spin">
|
|
543
|
+
${unsafeSVG(getFontAwesomeIcon('fa', 'circle-notch', 21, 21))}
|
|
544
|
+
</div>`}
|
|
545
|
+
${this.isShowDropdown ? this.notificationPopup() : ''}
|
|
546
|
+
</div>`
|
|
547
|
+
}
|
|
548
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface NotificationList {
|
|
2
|
+
notificationId: string
|
|
3
|
+
id: number
|
|
4
|
+
title: string
|
|
5
|
+
label: string
|
|
6
|
+
description: string
|
|
7
|
+
isRead: boolean
|
|
8
|
+
time: string // ISO date string
|
|
9
|
+
link: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Interface for the overall notification state
|
|
13
|
+
export interface UserNotification {
|
|
14
|
+
notificationList: NotificationList[]
|
|
15
|
+
notificationCount: number
|
|
16
|
+
loadMore: boolean
|
|
17
|
+
next: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface Meta {
|
|
21
|
+
cache: boolean
|
|
22
|
+
time: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ApiResponse<T> {
|
|
26
|
+
success: boolean
|
|
27
|
+
code: number
|
|
28
|
+
message: string
|
|
29
|
+
meta: Meta
|
|
30
|
+
data: T
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ApiRubrikResponse<T> {
|
|
34
|
+
success: boolean
|
|
35
|
+
code: number
|
|
36
|
+
message: string
|
|
37
|
+
meta: Meta
|
|
38
|
+
result: T
|
|
39
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
/* eslint-disable no-undef */
|
|
3
|
+
// Define types for the API responses
|
|
4
|
+
interface RefreshTokenResponse {
|
|
5
|
+
data: {
|
|
6
|
+
accessToken: string
|
|
7
|
+
deviceKeyId: string
|
|
8
|
+
isVerified: boolean
|
|
9
|
+
refreshToken: string
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ApiResponse<T> extends Response {
|
|
14
|
+
json(): Promise<T>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface RequestInit {
|
|
18
|
+
method?: string
|
|
19
|
+
headers?: Headers | { [key: string]: string }
|
|
20
|
+
body?: BodyInit | null
|
|
21
|
+
mode?: RequestMode
|
|
22
|
+
credentials?: RequestCredentials
|
|
23
|
+
cache?: RequestCache
|
|
24
|
+
redirect?: RequestRedirect
|
|
25
|
+
referrer?: string
|
|
26
|
+
referrerPolicy?: ReferrerPolicy
|
|
27
|
+
integrity?: string
|
|
28
|
+
keepalive?: boolean
|
|
29
|
+
signal?: AbortSignal
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Function to refresh the token
|
|
33
|
+
async function refreshAccessToken(refreshToken: string): Promise<string> {
|
|
34
|
+
const response = await fetch(
|
|
35
|
+
'https://api.kompas.cloud/account/api/v1/tokens/refresh',
|
|
36
|
+
{
|
|
37
|
+
method: 'POST',
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
refreshToken,
|
|
40
|
+
}),
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'application/json',
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error('Failed to refresh token')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const data: RefreshTokenResponse = await response.json()
|
|
52
|
+
return data.data.accessToken
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Custom fetch function that retries after 401
|
|
56
|
+
export async function customFetch<T>(
|
|
57
|
+
url: string,
|
|
58
|
+
refreshToken: string,
|
|
59
|
+
accessToken: string,
|
|
60
|
+
options: RequestInit = {}
|
|
61
|
+
): Promise<ApiResponse<T>> {
|
|
62
|
+
try {
|
|
63
|
+
// Retrieve and set access token in headers
|
|
64
|
+
const headers = new Headers(options.headers || {})
|
|
65
|
+
if (accessToken) {
|
|
66
|
+
headers.set('Authorization', `Bearer ${accessToken}`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Set updated headers
|
|
70
|
+
options.headers = headers
|
|
71
|
+
let response = await fetch(url, options)
|
|
72
|
+
|
|
73
|
+
// Retry request if 401 error occurs
|
|
74
|
+
if (response.status === 401) {
|
|
75
|
+
const newToken = await refreshAccessToken(refreshToken)
|
|
76
|
+
headers.set('Authorization', `Bearer ${newToken}`)
|
|
77
|
+
// Retry the original request with the new token
|
|
78
|
+
response = await fetch(url, { ...options, headers })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return response as ApiResponse<T>
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('Error making API call:', error)
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -597,8 +597,9 @@ export class KompasIdPaywallBody extends LitElement {
|
|
|
597
597
|
let buttonContent
|
|
598
598
|
const textColorClass = this.isDark ? 'text-blue-300' : 'text-blue-600'
|
|
599
599
|
const buttonTextColorClass = this.isDark ? 'text-white' : 'text-grey-600'
|
|
600
|
+
const isPrevHistoryExist = window.history.length > 1
|
|
600
601
|
|
|
601
|
-
if (type === 'epaper') {
|
|
602
|
+
if (type === 'epaper' && isPrevHistoryExist) {
|
|
602
603
|
buttonContent = html` <button
|
|
603
604
|
@click=${this.redirectToPrevUrl}
|
|
604
605
|
class="hidden md:block w-8 h-8 pl-4 ${textColorClass}"
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { KompasMeteredPaywall } from './components/kompasid-metered-paywall/Komp
|
|
|
7
7
|
export { KompasFreewall } from './components/kompasid-freewall/KompasFreewall.js'
|
|
8
8
|
export { KompasMeteredWallRegister } from './components/kompasid-metered-wall-register/KompasMeteredWallRegister.js'
|
|
9
9
|
export { KompasHeaderAccount } from './components/kompasid-header-account/KompasHeaderAccount.js'
|
|
10
|
+
export { KompasHeaderNotification } from './components/kompasid-header-notification/KompasHeaderNotification.js'
|
|
10
11
|
export { KompasGracePeriod } from './components/kompasid-grace-period/KompasGracePeriod.js'
|
|
11
12
|
|
|
12
13
|
declare global {
|