@kompasid/lit-web-components 0.8.8 → 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/demo/index.html CHANGED
@@ -54,6 +54,7 @@
54
54
  import '../dist/src/components/kompasid-freewall/KompasFreewall.js'
55
55
  import '../dist/src/components/kompasid-metered-wall-register/KompasMeteredWallRegister.js'
56
56
  import '../dist/src/components/kompasid-header-account/KompasHeaderAccount.js'
57
+ import '../dist/src/components/kompasid-header-notification/KompasHeaderNotification.js'
57
58
  import '../dist/src/components/kompasid-grace-period/KompasGracePeriod.js'
58
59
 
59
60
  const type = 'reguler'
@@ -103,6 +104,18 @@
103
104
  const totalGracePeriod = '5'
104
105
  const productId = 9802032
105
106
 
107
+ const getCookie = name => {
108
+ const value = `; ${document.cookie}`
109
+ const parts = value.split(`; ${name}=`)
110
+ if (parts && parts.length === 2) {
111
+ return parts.pop().split(';').shift()
112
+ }
113
+ return null
114
+ }
115
+
116
+ const accessToken = getCookie('kompas._token')
117
+ const refreshToken = getCookie('kompas._token_refresh')
118
+
106
119
  const userData = {
107
120
  userName: 'User Fullname',
108
121
  expired: 'Berakhir: 6 hari lagi',
@@ -134,6 +147,11 @@
134
147
  <div
135
148
  style="max-width: 1280px; margin: auto; display: flex;padding:0 30px"
136
149
  >
150
+ <kompasid-header-notification
151
+ style="margin-left: auto; margin-right: 5px; display: flex; align-items: center"
152
+ .accessToken=${accessToken}
153
+ .refreshToken=${refreshToken}
154
+ ></kompasid-header-notification>
137
155
  <kompasid-header-account
138
156
  .userData=${userData}
139
157
  .cart_url=${cartUrl}
@@ -161,7 +179,7 @@
161
179
  .tracker_page_domain=${trackerPageDomain}
162
180
  .metered_wall_type=${meteredWallType}
163
181
  .tracker_metered_wall_balance=${trackerMeteredWallBalance}
164
- style="margin-left: auto; height: 25px; display: flex; align-items: center"
182
+ style="margin-left: 10px; height: 25px; display: flex; align-items: center"
165
183
  ></kompasid-header-account>
166
184
  </div>
167
185
  </header>
@@ -0,0 +1,35 @@
1
+ import { LitElement } from 'lit';
2
+ import { UserNotification } from './types.js';
3
+ export declare class KompasHeaderNotification extends LitElement {
4
+ static styles: import("lit").CSSResult[];
5
+ isShowDropdown: boolean;
6
+ selectedTab: string;
7
+ notificationInfoData: UserNotification;
8
+ notificationContentData: any[];
9
+ isDataLoaded: boolean;
10
+ accessToken: string;
11
+ refreshToken: string;
12
+ constructor();
13
+ /**
14
+ * Function to format date
15
+ */
16
+ formatDate(date: string): string;
17
+ connectedCallback(): Promise<void>;
18
+ disconnectedCallback(): Promise<void>;
19
+ private apiFetch;
20
+ private getNotifRubrik;
21
+ private getNotifList;
22
+ private getEpaperData;
23
+ private notifRead;
24
+ private loadData;
25
+ private toggleDropdown;
26
+ private notificationIcon;
27
+ private handleClick;
28
+ private redirectTo;
29
+ private handleClickOutside;
30
+ private notificationInfoSection;
31
+ private redirectToNotification;
32
+ private notificationContentSection;
33
+ private notificationPopup;
34
+ render(): import("lit").TemplateResult<1>;
35
+ }
@@ -0,0 +1,482 @@
1
+ import { __decorate } from "tslib";
2
+ import { LitElement, html, css } from 'lit';
3
+ import { format, isToday, formatDistanceStrict, subDays } from 'date-fns';
4
+ import { id } from 'date-fns/locale/id';
5
+ import { customElement, property, state } from 'lit/decorators.js';
6
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
7
+ import { getFontAwesomeIcon } from '../../utils/fontawesome-setup.js';
8
+ import { TWStyles } from '../../../tailwind/tailwind.js';
9
+ import { customFetch } from './utils.js';
10
+ let KompasHeaderNotification = class KompasHeaderNotification extends LitElement {
11
+ constructor() {
12
+ super();
13
+ this.isShowDropdown = false;
14
+ this.selectedTab = 'Info';
15
+ this.notificationInfoData = {};
16
+ this.notificationContentData = [];
17
+ this.isDataLoaded = false;
18
+ this.accessToken = ''; // prod || dev
19
+ this.refreshToken = ''; // prod || dev
20
+ this.handleClick = (value) => {
21
+ this.selectedTab = value;
22
+ };
23
+ this.redirectTo = async (notifId, link) => {
24
+ const res = await this.notifRead(notifId);
25
+ if (res.status === 200) {
26
+ this.notificationInfoData = {
27
+ ...this.notificationInfoData,
28
+ notificationList: this.notificationInfoData.notificationList.map(notifItem => ({
29
+ ...notifItem,
30
+ isRead: notifItem.notificationId === notifId ? true : notifItem.isRead,
31
+ })),
32
+ };
33
+ }
34
+ if (link) {
35
+ const url = new URL(decodeURIComponent(link));
36
+ window.open(url, '_blank');
37
+ }
38
+ };
39
+ this.handleClickOutside = this.handleClickOutside.bind(this);
40
+ }
41
+ /**
42
+ * Function to format date
43
+ */
44
+ formatDate(date) {
45
+ // Check if the provided date is today
46
+ let result;
47
+ if (isToday(date)) {
48
+ // Return time difference if it's today (e.g., "7 hours ago")
49
+ result = `${formatDistanceStrict(date, new Date(), { locale: id })} lalu`;
50
+ }
51
+ else {
52
+ // Return formatted date (e.g., "12 Sep 2024, 09:00")
53
+ result = format(date, 'dd MMM yyyy, HH:mm', { locale: id });
54
+ }
55
+ return result;
56
+ }
57
+ async connectedCallback() {
58
+ super.connectedCallback();
59
+ this.isShowDropdown = false;
60
+ if (this.accessToken && this.refreshToken)
61
+ await this.loadData();
62
+ document.addEventListener('click', this.handleClickOutside);
63
+ }
64
+ async disconnectedCallback() {
65
+ super.disconnectedCallback();
66
+ document.removeEventListener('click', this.handleClickOutside);
67
+ }
68
+ async apiFetch(url, options) {
69
+ const response = await customFetch(url, this.refreshToken, this.accessToken, { ...options });
70
+ return response;
71
+ }
72
+ async getNotifRubrik() {
73
+ const req = this.apiFetch('https://cds.kompas.cloud/api/v1/article/pilihanku', {
74
+ method: 'GET',
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ },
78
+ });
79
+ return req;
80
+ }
81
+ async getNotifList() {
82
+ const req = await this.apiFetch('https://api.kompas.cloud/account/api/v1/users/notification', {
83
+ method: 'GET',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ });
88
+ return req;
89
+ }
90
+ async getEpaperData() {
91
+ const formatDate = 'yyyy/MM/dd';
92
+ const startDate = format(subDays(new Date(), 1), formatDate);
93
+ const endDate = format(new Date(), formatDate).toString();
94
+ const req = await this.apiFetch(`https://apiepaper.kompas.cloud/products?startDate=${startDate}&endDate=${endDate}&sort=desc`, {
95
+ method: 'GET',
96
+ headers: {
97
+ 'Content-Type': 'application/json',
98
+ },
99
+ });
100
+ return req;
101
+ }
102
+ async notifRead(notifId) {
103
+ const req = await this.apiFetch('https://api.kompas.cloud/account/api/v1/users/notification/read', {
104
+ headers: {
105
+ 'Content-Type': 'application/json',
106
+ },
107
+ method: 'PUT',
108
+ body: JSON.stringify({
109
+ notificationId: notifId,
110
+ }),
111
+ });
112
+ return req;
113
+ }
114
+ async loadData() {
115
+ const apiCalls = [
116
+ this.getNotifList(),
117
+ this.getNotifRubrik(),
118
+ this.getEpaperData(),
119
+ ];
120
+ this.isDataLoaded = false;
121
+ Promise.all(apiCalls)
122
+ .then(responses => Promise.all(responses.map(response => response.json())))
123
+ .then(values => {
124
+ this.notificationInfoData = values[0].data;
125
+ const rubrikData = values[1].result;
126
+ const ePaperData = values[2].data[0];
127
+ const { publishDate, description, thumbnail, title, url } = ePaperData;
128
+ const data = {
129
+ title,
130
+ category: [{ name: 'ePaper', slug: 'epaper' }],
131
+ publishedDateGmt: format(new Date(publishDate), 'yyyy-MM-dd HH:mm:ss'),
132
+ permalink: url,
133
+ thumbnails: {
134
+ sizes: { thumbnailSquareMedium: { permalink: thumbnail } },
135
+ },
136
+ excerpt: description,
137
+ };
138
+ this.notificationContentData = [data, ...rubrikData];
139
+ this.notificationContentData.sort((a, b) => new Date(b.publishedDateGmt).getTime() -
140
+ new Date(a.publishedDateGmt).getTime());
141
+ this.isDataLoaded = true;
142
+ });
143
+ }
144
+ toggleDropdown() {
145
+ this.isShowDropdown = !this.isShowDropdown;
146
+ }
147
+ notificationIcon() {
148
+ const notificationIndicator = () => {
149
+ if (!this.notificationInfoData.notificationCount)
150
+ return html ``;
151
+ return html `<div
152
+ class="header-account--notification-indicator animate-ping"
153
+ ></div>`;
154
+ };
155
+ return html `
156
+ <button
157
+ @click=${this.toggleDropdown}
158
+ class="cursor-pointer relative flex items-center"
159
+ >
160
+ ${notificationIndicator()}
161
+ <div class="flex flex-row items-center self-center">
162
+ <div class="text-white cursor-pointer mt-0.5">
163
+ ${unsafeSVG(getFontAwesomeIcon('fas', 'bell', 21, 21))}
164
+ </div>
165
+ </div>
166
+ </button>
167
+ `;
168
+ }
169
+ handleClickOutside(event) {
170
+ var _a;
171
+ if (!this.isShowDropdown)
172
+ return;
173
+ const popup = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.getElementById('headerNotification');
174
+ const ev = event.composedPath();
175
+ if (this.isShowDropdown && popup && !(ev === null || ev === void 0 ? void 0 : ev.includes(popup))) {
176
+ this.isShowDropdown = false; // Close the popup
177
+ }
178
+ }
179
+ // Notification Info
180
+ notificationInfoSection() {
181
+ const { notificationList } = this.notificationInfoData;
182
+ // Empty state
183
+ if (!notificationList.length)
184
+ return html `
185
+ <div class="text-center px-4 pt-6 pb-32">
186
+ <img
187
+ src="../../assets/empty-state-notification.png"
188
+ alt="empty-state-notification"
189
+ class="w-auto mx-auto"
190
+ />
191
+ <p class="font-bold text-center text-lg py-2">Belum Ada Notifikasi</p>
192
+ <p class="px-2">
193
+ Kami akan memberitahukan Anda ketika ada informasi dan pemberitahuan
194
+ terbaru.
195
+ </p>
196
+ </div>
197
+ `;
198
+ const notificationInfoListTemplate = (item) => {
199
+ const imgUrl = item.label === 'Akun'
200
+ ? `https://cdn-www.kompas.id/assets/notifikasi-akun.svg`
201
+ : `https://cdn-www.kompas.id/assets/langganan-anda-telah-berakhir.svg`;
202
+ return html `<div class="w-3/4 pr-1">
203
+ <div class="flex items-center mb-1 text-xs">
204
+ <span class="text-green-500 bg-green-100 rounded-sm p-1 px-2">
205
+ ${item.label}
206
+ </span>
207
+ <span class="text-grey-400 ml-2"
208
+ >${this.formatDate(item.time)}</span
209
+ >
210
+ </div>
211
+ <div>
212
+ <p class="text-sm font-bold">${item.title}</p>
213
+ <p class="text-sm">${item.description}</p>
214
+ </div>
215
+ </div>
216
+ <div class="w-1/4">
217
+ <img src="${imgUrl}" alt="${item.label}-alt" class="w-14 h-14" />
218
+ </div>`;
219
+ };
220
+ return html `<div class="h-[300px] overflow-y-scroll">
221
+ ${notificationList.map(item => html `<button
222
+ class="flex w-full text-left items-start p-4 cursor-pointer justify-between ${!item.isRead
223
+ ? 'bg-blue-100'
224
+ : ''}"
225
+ @click=${() => this.redirectTo(item.notificationId, item.link)}
226
+ >
227
+ ${notificationInfoListTemplate(item)}
228
+ </button> `)}
229
+ </div>
230
+ <!-- Footer Link -->
231
+ <div class="px-4 py-4 text-center">
232
+ <a href="#" @click="${() => this.redirectToNotification('info')}" class="text-base font-bold text-blue-500">
233
+ Lihat Selengkapnya</a
234
+ >
235
+ </div>
236
+ </div>`;
237
+ }
238
+ redirectToNotification(tab) {
239
+ window.open(`https://account.kompas.id/manage-account/notification/${tab}`, '_blank');
240
+ }
241
+ // Notification Content
242
+ notificationContentSection() {
243
+ // Empty state
244
+ if (!this.notificationContentData.length) {
245
+ return html `
246
+ <div class="text-center px-4 pt-6 pb-32">
247
+ <img
248
+ src="../../assets/empty-state-notification.png"
249
+ alt="empty-state-notification"
250
+ class="w-auto mx-auto"
251
+ />
252
+ <p class="font-bold text-center text-lg py-2">Belum Ada Notifikasi</p>
253
+ <p class="px-2">
254
+ Kami akan memberitahukan Anda ketika ada informasi dan pemberitahuan
255
+ terbaru.
256
+ </p>
257
+ </div>
258
+ `;
259
+ }
260
+ const notificationContentListTemplate = (item) => html `<div
261
+ class="w-3/4 pr-1 "
262
+ >
263
+ <div class="flex items-center mb-1 text-xs">
264
+ <span class="text-green-500 bg-green-100 rounded-sm p-1 px-2">
265
+ ${item.category[0].name}
266
+ </span>
267
+ <span class="text-grey-400 ml-2">
268
+ ${this.formatDate(item.publishedDateGmt)}</span
269
+ >
270
+ </div>
271
+ <p class="font-bold text-base text-customTextColor py-1">
272
+ ${item.title}
273
+ </p>
274
+ </div>
275
+ <div class="w-1/4">
276
+ <img
277
+ src="${item.thumbnails.sizes.thumbnailSquareMedium.permalink}"
278
+ alt="content-img-alt"
279
+ class="w-14 h-14 object-cover"
280
+ />
281
+ </div>`;
282
+ return html `<div class="h-[300px] overflow-y-scroll">
283
+ ${this.notificationContentData.map(item => html `<button
284
+ class="flex w-full text-left items-start p-4 cursor-pointer justify-between ${!item.isRead
285
+ ? 'bg-blue-100'
286
+ : ''}"
287
+ @click=${() => window.open(item.permalink, '_blank')}
288
+ >
289
+ ${notificationContentListTemplate(item)}
290
+ </button> `)}
291
+ </div>
292
+ <!-- Footer Link -->
293
+ <div class="px-4 py-4 text-center">
294
+ <a href="#" @click="${() => this.redirectToNotification('konten')}" class="text-base font-bold text-blue-500">
295
+ Lihat Selengkapnya</a
296
+ >
297
+ </div>
298
+ </div>`;
299
+ }
300
+ notificationPopup() {
301
+ const tab = ['Info', 'Konten'];
302
+ return html `
303
+ <div
304
+ id="notificationPopup"
305
+ class="header-notificaion-dropdown rounded-lg shadow-lg z-50 mt-2 ${this
306
+ .isShowDropdown
307
+ ? 'active'
308
+ : ''}"
309
+ >
310
+ <div class="sticky">
311
+ <div class="flex justify-center py-2 border-b border-grey-300">
312
+ <span class="font-bold text-base">Notifikasi</span>
313
+ </div>
314
+ <!-- Tabs for Info and Content -->
315
+ <div class="flex justify-between">
316
+ ${tab.map(item => html `
317
+ <button
318
+ class="focus:outline-none py-2 w-1/2 ${this.selectedTab ===
319
+ item && 'link-active'}"
320
+ @click=${() => this.handleClick(item)}
321
+ >
322
+ ${item}
323
+ </button>
324
+ `)}
325
+ </div>
326
+ </div>
327
+
328
+ <!-- Notification Content -->
329
+ ${this.selectedTab === 'Info'
330
+ ? this.notificationInfoSection()
331
+ : this.notificationContentSection()}
332
+ </div>
333
+ `;
334
+ }
335
+ render() {
336
+ return html `<div id="headerNotification" class="relative">
337
+ ${this.isDataLoaded
338
+ ? this.notificationIcon()
339
+ : html `<div class="text-white cursor-pointer mt-0.5 animate-spin">
340
+ ${unsafeSVG(getFontAwesomeIcon('fa', 'circle-notch', 21, 21))}
341
+ </div>`}
342
+ ${this.isShowDropdown ? this.notificationPopup() : ''}
343
+ </div>`;
344
+ }
345
+ };
346
+ KompasHeaderNotification.styles = [
347
+ css `
348
+ :host {
349
+ font-family: 'PT Sans', sans-serif;
350
+ }
351
+
352
+ .header-account--notification-indicator {
353
+ position: absolute;
354
+ top: 0;
355
+ height: 0.5rem;
356
+ width: 0.5rem;
357
+ background-color: #ff7a00;
358
+ border-radius: 50%;
359
+ right: -0.403rem;
360
+ top: -0.281rem;
361
+ }
362
+
363
+ .header-notificaion-dropdown {
364
+ width: 20.5rem;
365
+ background-color: #ffffff;
366
+ font-family: 'PT Sans', sans-serif;
367
+ font-size: 0.875rem;
368
+ position: absolute;
369
+ right: -7.5vmax;
370
+ }
371
+
372
+ .w-14 {
373
+ width: 3.25rem;
374
+ }
375
+
376
+ .h-\\[300px\\] {
377
+ height: 300px;
378
+ }
379
+
380
+ .h-14 {
381
+ height: 3.25rem;
382
+ }
383
+
384
+ .bg-green-100 {
385
+ --tw-bg-opacity: 1;
386
+ background-color: rgb(238 252 210 / var(--tw-bg-opacity));
387
+ }
388
+
389
+ .overflow-y-scroll {
390
+ overflow-y: scroll;
391
+ }
392
+
393
+ .link-active {
394
+ color: #0468cb;
395
+ border-color: #0468cb;
396
+ border-bottom-width: 3px;
397
+ background-color: transparent !important;
398
+ }
399
+
400
+ .animate-spin {
401
+ animation: spin 2s linear infinite;
402
+ }
403
+
404
+ @keyframes spin {
405
+ from {
406
+ transform: rotate(0deg);
407
+ }
408
+ to {
409
+ transform: rotate(360deg);
410
+ }
411
+ }
412
+
413
+ .object-cover {
414
+ object-fit: cover;
415
+ }
416
+
417
+ .z-index-max {
418
+ z-index: 99999;
419
+ }
420
+
421
+ /* Custom Scrollbar for header */
422
+ /* width */
423
+ ::-webkit-scrollbar {
424
+ width: 4px;
425
+ }
426
+
427
+ /* Track */
428
+ ::-webkit-scrollbar-track {
429
+ background: transparent;
430
+ }
431
+
432
+ /* Handle */
433
+ ::-webkit-scrollbar-thumb {
434
+ background: #0356a8;
435
+ border-radius: 100px;
436
+ }
437
+
438
+ /* Handle on hover */
439
+ ::-webkit-scrollbar-thumb:hover {
440
+ background: #0356a8;
441
+ }
442
+
443
+ @media (min-width: 1024px) {
444
+ .header-notificaion-dropdown {
445
+ right: -4vmax;
446
+ }
447
+ }
448
+
449
+ @media (max-width: 360px) {
450
+ .header-notificaion-dropdown {
451
+ right: -10vmax;
452
+ }
453
+ }
454
+ `,
455
+ TWStyles,
456
+ ];
457
+ __decorate([
458
+ state()
459
+ ], KompasHeaderNotification.prototype, "isShowDropdown", void 0);
460
+ __decorate([
461
+ state()
462
+ ], KompasHeaderNotification.prototype, "selectedTab", void 0);
463
+ __decorate([
464
+ state()
465
+ ], KompasHeaderNotification.prototype, "notificationInfoData", void 0);
466
+ __decorate([
467
+ state()
468
+ ], KompasHeaderNotification.prototype, "notificationContentData", void 0);
469
+ __decorate([
470
+ state()
471
+ ], KompasHeaderNotification.prototype, "isDataLoaded", void 0);
472
+ __decorate([
473
+ property({ type: String })
474
+ ], KompasHeaderNotification.prototype, "accessToken", void 0);
475
+ __decorate([
476
+ property({ type: String })
477
+ ], KompasHeaderNotification.prototype, "refreshToken", void 0);
478
+ KompasHeaderNotification = __decorate([
479
+ customElement('kompasid-header-notification')
480
+ ], KompasHeaderNotification);
481
+ export { KompasHeaderNotification };
482
+ //# sourceMappingURL=KompasHeaderNotification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KompasHeaderNotification.js","sourceRoot":"","sources":["../../../../src/components/kompasid-header-notification/KompasHeaderNotification.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { format, isToday, formatDistanceStrict, subDays } from 'date-fns'\nimport { id } from 'date-fns/locale/id'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js'\nimport { getFontAwesomeIcon } from '../../utils/fontawesome-setup.js'\nimport { TWStyles } from '../../../tailwind/tailwind.js'\nimport {\n UserNotification,\n NotificationList,\n ApiResponse,\n ApiRubrikResponse,\n} from './types.js'\nimport { customFetch } from './utils.js'\n\n@customElement('kompasid-header-notification')\nexport class KompasHeaderNotification extends LitElement {\n static styles = [\n css`\n :host {\n font-family: 'PT Sans', sans-serif;\n }\n\n .header-account--notification-indicator {\n position: absolute;\n top: 0;\n height: 0.5rem;\n width: 0.5rem;\n background-color: #ff7a00;\n border-radius: 50%;\n right: -0.403rem;\n top: -0.281rem;\n }\n\n .header-notificaion-dropdown {\n width: 20.5rem;\n background-color: #ffffff;\n font-family: 'PT Sans', sans-serif;\n font-size: 0.875rem;\n position: absolute;\n right: -7.5vmax;\n }\n\n .w-14 {\n width: 3.25rem;\n }\n\n .h-\\\\[300px\\\\] {\n height: 300px;\n }\n\n .h-14 {\n height: 3.25rem;\n }\n\n .bg-green-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(238 252 210 / var(--tw-bg-opacity));\n }\n\n .overflow-y-scroll {\n overflow-y: scroll;\n }\n\n .link-active {\n color: #0468cb;\n border-color: #0468cb;\n border-bottom-width: 3px;\n background-color: transparent !important;\n }\n\n .animate-spin {\n animation: spin 2s linear infinite;\n }\n\n @keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n .object-cover {\n object-fit: cover;\n }\n\n .z-index-max {\n z-index: 99999;\n }\n\n /* Custom Scrollbar for header */\n /* width */\n ::-webkit-scrollbar {\n width: 4px;\n }\n\n /* Track */\n ::-webkit-scrollbar-track {\n background: transparent;\n }\n\n /* Handle */\n ::-webkit-scrollbar-thumb {\n background: #0356a8;\n border-radius: 100px;\n }\n\n /* Handle on hover */\n ::-webkit-scrollbar-thumb:hover {\n background: #0356a8;\n }\n\n @media (min-width: 1024px) {\n .header-notificaion-dropdown {\n right: -4vmax;\n }\n }\n\n @media (max-width: 360px) {\n .header-notificaion-dropdown {\n right: -10vmax;\n }\n }\n `,\n TWStyles,\n ]\n\n @state() isShowDropdown: boolean = false\n @state() selectedTab: string = 'Info'\n @state() notificationInfoData: UserNotification = {} as UserNotification\n @state() notificationContentData: any[] = []\n @state() isDataLoaded: boolean = false\n\n @property({ type: String }) accessToken = '' // prod || dev\n @property({ type: String }) refreshToken = '' // prod || dev\n\n constructor() {\n super()\n this.handleClickOutside = this.handleClickOutside.bind(this)\n }\n\n /**\n * Function to format date\n */\n formatDate(date: string) {\n // Check if the provided date is today\n let result\n if (isToday(date)) {\n // Return time difference if it's today (e.g., \"7 hours ago\")\n result = `${formatDistanceStrict(date, new Date(), { locale: id })} lalu`\n } else {\n // Return formatted date (e.g., \"12 Sep 2024, 09:00\")\n result = format(date, 'dd MMM yyyy, HH:mm', { locale: id })\n }\n return result\n }\n\n override async connectedCallback() {\n super.connectedCallback()\n this.isShowDropdown = false\n\n if (this.accessToken && this.refreshToken) await this.loadData()\n document.addEventListener('click', this.handleClickOutside)\n }\n\n override async disconnectedCallback() {\n super.disconnectedCallback()\n document.removeEventListener('click', this.handleClickOutside)\n }\n\n private async apiFetch(url: string, options: any) {\n const response = await customFetch(\n url,\n this.refreshToken,\n this.accessToken,\n { ...options }\n )\n\n return response\n }\n\n private async getNotifRubrik() {\n const req = this.apiFetch(\n 'https://cds.kompas.cloud/api/v1/article/pilihanku',\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n return req\n }\n\n private async getNotifList() {\n const req = await this.apiFetch(\n 'https://api.kompas.cloud/account/api/v1/users/notification',\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n return req\n }\n\n private async getEpaperData() {\n const formatDate = 'yyyy/MM/dd'\n const startDate = format(subDays(new Date(), 1), formatDate)\n const endDate = format(new Date(), formatDate).toString()\n const req = await this.apiFetch(\n `https://apiepaper.kompas.cloud/products?startDate=${startDate}&endDate=${endDate}&sort=desc`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n\n return req\n }\n\n private async notifRead(notifId: string) {\n const req = await this.apiFetch(\n 'https://api.kompas.cloud/account/api/v1/users/notification/read',\n {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'PUT',\n body: JSON.stringify({\n notificationId: notifId,\n }),\n }\n )\n return req\n }\n\n private async loadData() {\n const apiCalls = [\n this.getNotifList(),\n this.getNotifRubrik(),\n this.getEpaperData(),\n ]\n this.isDataLoaded = false\n\n Promise.all(apiCalls)\n .then(responses =>\n Promise.all(responses.map(response => response.json()))\n )\n .then(values => {\n this.notificationInfoData = (\n values[0] as ApiResponse<UserNotification>\n ).data\n const rubrikData = (values[1] as ApiRubrikResponse<any>).result\n const ePaperData = (values[2] as ApiResponse<any>).data[0]\n const { publishDate, description, thumbnail, title, url } = ePaperData\n\n const data = {\n title,\n category: [{ name: 'ePaper', slug: 'epaper' }],\n publishedDateGmt: format(\n new Date(publishDate),\n 'yyyy-MM-dd HH:mm:ss'\n ),\n permalink: url,\n thumbnails: {\n sizes: { thumbnailSquareMedium: { permalink: thumbnail } },\n },\n excerpt: description,\n }\n\n this.notificationContentData = [data, ...rubrikData]\n this.notificationContentData.sort(\n (a: any, b: any) =>\n new Date(b.publishedDateGmt).getTime() -\n new Date(a.publishedDateGmt).getTime()\n )\n\n this.isDataLoaded = true\n })\n }\n\n private toggleDropdown() {\n this.isShowDropdown = !this.isShowDropdown\n }\n\n private notificationIcon() {\n const notificationIndicator = () => {\n if (!this.notificationInfoData.notificationCount) return html``\n return html`<div\n class=\"header-account--notification-indicator animate-ping\"\n ></div>`\n }\n\n return html`\n <button\n @click=${this.toggleDropdown}\n class=\"cursor-pointer relative flex items-center\"\n >\n ${notificationIndicator()}\n <div class=\"flex flex-row items-center self-center\">\n <div class=\"text-white cursor-pointer mt-0.5\">\n ${unsafeSVG(getFontAwesomeIcon('fas', 'bell', 21, 21))}\n </div>\n </div>\n </button>\n `\n }\n\n private handleClick = (value: string) => {\n this.selectedTab = value\n }\n\n private redirectTo = async (notifId: string, link: string) => {\n const res = await this.notifRead(notifId)\n\n if (res.status === 200) {\n this.notificationInfoData = {\n ...this.notificationInfoData,\n notificationList: this.notificationInfoData.notificationList.map(\n notifItem => ({\n ...notifItem,\n isRead:\n notifItem.notificationId === notifId ? true : notifItem.isRead,\n })\n ),\n }\n }\n\n if (link) {\n const url = new URL(decodeURIComponent(link))\n\n window.open(url, '_blank')\n }\n }\n\n private handleClickOutside(event: Event) {\n if (!this.isShowDropdown) return\n\n const popup = this.shadowRoot?.getElementById('headerNotification')\n const ev = event.composedPath()\n if (this.isShowDropdown && popup && !ev?.includes(popup as HTMLElement)) {\n this.isShowDropdown = false // Close the popup\n }\n }\n\n // Notification Info\n private notificationInfoSection() {\n const { notificationList }: { notificationList: NotificationList[] } =\n this.notificationInfoData\n\n // Empty state\n if (!notificationList.length)\n return html`\n <div class=\"text-center px-4 pt-6 pb-32\">\n <img\n src=\"../../assets/empty-state-notification.png\"\n alt=\"empty-state-notification\"\n class=\"w-auto mx-auto\"\n />\n <p class=\"font-bold text-center text-lg py-2\">Belum Ada Notifikasi</p>\n <p class=\"px-2\">\n Kami akan memberitahukan Anda ketika ada informasi dan pemberitahuan\n terbaru.\n </p>\n </div>\n `\n\n const notificationInfoListTemplate = (item: NotificationList) => {\n const imgUrl =\n item.label === 'Akun'\n ? `https://cdn-www.kompas.id/assets/notifikasi-akun.svg`\n : `https://cdn-www.kompas.id/assets/langganan-anda-telah-berakhir.svg`\n\n return html`<div class=\"w-3/4 pr-1\">\n <div class=\"flex items-center mb-1 text-xs\">\n <span class=\"text-green-500 bg-green-100 rounded-sm p-1 px-2\">\n ${item.label}\n </span>\n <span class=\"text-grey-400 ml-2\"\n >${this.formatDate(item.time)}</span\n >\n </div>\n <div>\n <p class=\"text-sm font-bold\">${item.title}</p>\n <p class=\"text-sm\">${item.description}</p>\n </div>\n </div>\n <div class=\"w-1/4\">\n <img src=\"${imgUrl}\" alt=\"${item.label}-alt\" class=\"w-14 h-14\" />\n </div>`\n }\n\n return html`<div class=\"h-[300px] overflow-y-scroll\">\n ${notificationList.map(\n item =>\n html`<button\n class=\"flex w-full text-left items-start p-4 cursor-pointer justify-between ${!item.isRead\n ? 'bg-blue-100'\n : ''}\"\n @click=${() => this.redirectTo(item.notificationId, item.link)}\n >\n ${notificationInfoListTemplate(item)}\n </button> `\n )}\n </div>\n <!-- Footer Link -->\n <div class=\"px-4 py-4 text-center\">\n <a href=\"#\" @click=\"${() =>\n this.redirectToNotification(\n 'info'\n )}\" class=\"text-base font-bold text-blue-500\">\n Lihat Selengkapnya</a\n >\n </div>\n </div>`\n }\n\n private redirectToNotification(tab: string) {\n window.open(\n `https://account.kompas.id/manage-account/notification/${tab}`,\n '_blank'\n )\n }\n\n // Notification Content\n private notificationContentSection() {\n // Empty state\n if (!this.notificationContentData.length) {\n return html`\n <div class=\"text-center px-4 pt-6 pb-32\">\n <img\n src=\"../../assets/empty-state-notification.png\"\n alt=\"empty-state-notification\"\n class=\"w-auto mx-auto\"\n />\n <p class=\"font-bold text-center text-lg py-2\">Belum Ada Notifikasi</p>\n <p class=\"px-2\">\n Kami akan memberitahukan Anda ketika ada informasi dan pemberitahuan\n terbaru.\n </p>\n </div>\n `\n }\n const notificationContentListTemplate = (item: any) => html`<div\n class=\"w-3/4 pr-1 \"\n >\n <div class=\"flex items-center mb-1 text-xs\">\n <span class=\"text-green-500 bg-green-100 rounded-sm p-1 px-2\">\n ${item.category[0].name}\n </span>\n <span class=\"text-grey-400 ml-2\">\n ${this.formatDate(item.publishedDateGmt)}</span\n >\n </div>\n <p class=\"font-bold text-base text-customTextColor py-1\">\n ${item.title}\n </p>\n </div>\n <div class=\"w-1/4\">\n <img\n src=\"${item.thumbnails.sizes.thumbnailSquareMedium.permalink}\"\n alt=\"content-img-alt\"\n class=\"w-14 h-14 object-cover\"\n />\n </div>`\n\n return html`<div class=\"h-[300px] overflow-y-scroll\">\n ${this.notificationContentData.map(\n item =>\n html`<button\n class=\"flex w-full text-left items-start p-4 cursor-pointer justify-between ${!item.isRead\n ? 'bg-blue-100'\n : ''}\"\n @click=${() => window.open(item.permalink, '_blank')}\n >\n ${notificationContentListTemplate(item)}\n </button> `\n )}\n </div>\n <!-- Footer Link -->\n <div class=\"px-4 py-4 text-center\">\n <a href=\"#\" @click=\"${() =>\n this.redirectToNotification(\n 'konten'\n )}\" class=\"text-base font-bold text-blue-500\">\n Lihat Selengkapnya</a\n >\n </div>\n </div>`\n }\n\n private notificationPopup() {\n const tab = ['Info', 'Konten']\n\n return html`\n <div\n id=\"notificationPopup\"\n class=\"header-notificaion-dropdown rounded-lg shadow-lg z-50 mt-2 ${this\n .isShowDropdown\n ? 'active'\n : ''}\"\n >\n <div class=\"sticky\">\n <div class=\"flex justify-center py-2 border-b border-grey-300\">\n <span class=\"font-bold text-base\">Notifikasi</span>\n </div>\n <!-- Tabs for Info and Content -->\n <div class=\"flex justify-between\">\n ${tab.map(\n item =>\n html`\n <button\n class=\"focus:outline-none py-2 w-1/2 ${this.selectedTab ===\n item && 'link-active'}\"\n @click=${() => this.handleClick(item)}\n >\n ${item}\n </button>\n `\n )}\n </div>\n </div>\n\n <!-- Notification Content -->\n ${this.selectedTab === 'Info'\n ? this.notificationInfoSection()\n : this.notificationContentSection()}\n </div>\n `\n }\n\n render() {\n return html`<div id=\"headerNotification\" class=\"relative\">\n ${this.isDataLoaded\n ? this.notificationIcon()\n : html`<div class=\"text-white cursor-pointer mt-0.5 animate-spin\">\n ${unsafeSVG(getFontAwesomeIcon('fa', 'circle-notch', 21, 21))}\n </div>`}\n ${this.isShowDropdown ? this.notificationPopup() : ''}\n </div>`\n }\n}\n"]}
@@ -0,0 +1,35 @@
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;
9
+ link: string;
10
+ }
11
+ export interface UserNotification {
12
+ notificationList: NotificationList[];
13
+ notificationCount: number;
14
+ loadMore: boolean;
15
+ next: number;
16
+ }
17
+ interface Meta {
18
+ cache: boolean;
19
+ time: number;
20
+ }
21
+ export interface ApiResponse<T> {
22
+ success: boolean;
23
+ code: number;
24
+ message: string;
25
+ meta: Meta;
26
+ data: T;
27
+ }
28
+ export interface ApiRubrikResponse<T> {
29
+ success: boolean;
30
+ code: number;
31
+ message: string;
32
+ meta: Meta;
33
+ result: T;
34
+ }
35
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/components/kompasid-header-notification/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface NotificationList {\n notificationId: string\n id: number\n title: string\n label: string\n description: string\n isRead: boolean\n time: string // ISO date string\n link: string\n}\n\n// Interface for the overall notification state\nexport interface UserNotification {\n notificationList: NotificationList[]\n notificationCount: number\n loadMore: boolean\n next: number\n}\n\ninterface Meta {\n cache: boolean\n time: number\n}\n\nexport interface ApiResponse<T> {\n success: boolean\n code: number\n message: string\n meta: Meta\n data: T\n}\n\nexport interface ApiRubrikResponse<T> {\n success: boolean\n code: number\n message: string\n meta: Meta\n result: T\n}\n"]}