import { t, i18n } from '../../i18n.js'
import { auth } from '../../services/auth.js'
import { notificationsService } from '../../services/notifications.js'
import { router } from '../../router.js'
class PageNotifications extends HTMLElement {
constructor() {
super()
this.loading = true
}
connectedCallback() {
this._unsubs = []
this.render()
this.loadNotifications()
this._unsubs.push(i18n.subscribe(() => this.render()))
this._unsubs.push(notificationsService.subscribe(() => {
this.loading = false
this.updateContent()
}))
}
disconnectedCallback() {
this._unsubs.forEach(fn => fn())
this._unsubs = []
}
async loadNotifications() {
if (!auth.isLoggedIn()) {
this.loading = false
this.updateContent()
return
}
await notificationsService.refresh()
}
render() {
this.innerHTML = /* html */`
`
this.setupEventListeners()
}
updateContent() {
const container = this.querySelector('#notifications-content')
if (container) {
container.innerHTML = this.renderContent()
this.setupEventListeners()
}
}
renderContent() {
if (!auth.isLoggedIn()) {
return /* html */`
🔔
${t('auth.loginRequired')}
`
}
if (this.loading) {
return /* html */`
${Array(3).fill(0).map(() => `
`).join('')}
`
}
const notifications = notificationsService.notifications
if (notifications.length === 0) {
return /* html */`
🔔
${t('notifications.empty')}
`
}
return /* html */`
${notifications.map(n => this.renderNotification(n)).join('')}
`
}
renderNotification(n) {
const typeText = t(`notifications.${n.type}`)
const date = new Date(n.date_created)
const timeAgo = this.formatTimeAgo(date)
return /* html */`
`
}
getIcon(type) {
const icons = {
listing_created: '📝',
listing_published: '✅',
listing_expired: '⏰',
new_message: '💬',
favorite_added: '❤️'
}
return icons[type] || '🔔'
}
formatTimeAgo(date) {
const now = new Date()
const diffMs = now - date
const diffMin = Math.floor(diffMs / 60000)
const diffHours = Math.floor(diffMs / 3600000)
const diffDays = Math.floor(diffMs / 86400000)
if (diffMin < 1) return t('messages.today')
if (diffMin < 60) return `${diffMin} min`
if (diffHours < 24) return `${diffHours}h`
if (diffDays === 1) return t('messages.yesterday')
return t('messages.daysAgo', { days: diffDays })
}
setupEventListeners() {
this.querySelector('#mark-all-read')?.addEventListener('click', async () => {
await notificationsService.markAllRead()
this.render()
})
this.querySelector('#login-btn')?.addEventListener('click', () => {
document.querySelector('auth-modal')?.show('login')
})
this.querySelectorAll('.notification-item').forEach(item => {
item.addEventListener('click', async (e) => {
if (e.target.closest('.notification-delete')) return
const id = item.dataset.id
const type = item.dataset.type
const ref = item.dataset.ref
await notificationsService.markRead(id)
if (ref) {
if (type === 'new_message') {
router.navigate(`/messages`)
} else {
router.navigate(`/listing/${ref}`)
}
}
})
})
this.querySelectorAll('.notification-delete').forEach(btn => {
btn.addEventListener('click', async (e) => {
e.stopPropagation()
await notificationsService.remove(btn.dataset.delete)
})
})
}
}
customElements.define('page-notifications', PageNotifications)
const style = document.createElement('style')
style.textContent = /* css */`
page-notifications .notifications-page {
padding: var(--space-lg) 0;
}
page-notifications .page-header {
margin-bottom: var(--space-xl);
}
page-notifications .page-header-row {
display: flex;
justify-content: space-between;
align-items: center;
}
page-notifications .page-header h1 {
margin: 0;
}
page-notifications .notifications-list {
display: flex;
flex-direction: column;
gap: 1px;
background-color: var(--color-border);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
overflow: hidden;
}
page-notifications .notification-item {
display: flex;
align-items: center;
gap: var(--space-md);
padding: var(--space-md) var(--space-lg);
background-color: var(--color-bg);
cursor: pointer;
transition: background-color var(--transition-fast);
}
page-notifications .notification-item:hover {
background-color: var(--color-bg-secondary);
}
page-notifications .notification-item.unread {
background-color: var(--color-bg-secondary);
}
page-notifications .notification-item.unread .notification-text {
font-weight: var(--font-weight-semibold);
}
page-notifications .notification-icon {
font-size: 1.5rem;
flex-shrink: 0;
filter: grayscale(1);
}
page-notifications .notification-body {
flex: 1;
min-width: 0;
}
page-notifications .notification-text {
margin: 0 0 var(--space-xs);
font-size: var(--font-size-sm);
}
page-notifications .notification-time {
font-size: var(--font-size-xs);
color: var(--color-text-muted);
}
page-notifications .notification-delete {
flex-shrink: 0;
opacity: 0;
transition: opacity var(--transition-fast);
}
page-notifications .notification-item:hover .notification-delete {
opacity: 1;
}
page-notifications .empty-state {
text-align: center;
padding: var(--space-3xl);
}
page-notifications .empty-icon {
font-size: 4rem;
margin-bottom: var(--space-md);
filter: grayscale(1);
}
page-notifications .empty-state h3 {
margin: 0 0 var(--space-sm);
}
page-notifications .notification-skeleton {
padding: var(--space-md) var(--space-lg);
background-color: var(--color-bg);
}
page-notifications .skeleton-line {
height: 14px;
background-color: var(--color-bg-tertiary);
border-radius: var(--radius-sm);
margin-bottom: var(--space-xs);
}
page-notifications .skeleton-line-short {
width: 60%;
}
@media (max-width: 768px) {
page-notifications .notification-delete {
opacity: 1;
}
}
`
document.head.appendChild(style)