Files
kashilo/js/services/categories.js

205 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Categories Service - Handles category tree and translations
*/
import { directus } from './directus.js'
import { getCurrentLanguage, LOCALE_TO_DIRECTUS } from '../i18n.js'
class CategoriesService {
constructor() {
this.cache = null
this.cacheTimestamp = 0
this.cacheTimeout = 24 * 60 * 60 * 1000 // 24 hours
this.storageKey = 'dgray_categories'
this.storageTimestampKey = 'dgray_categories_ts'
this._pending = null
this._loadFromStorage()
}
_loadFromStorage() {
try {
const data = localStorage.getItem(this.storageKey)
const ts = parseInt(localStorage.getItem(this.storageTimestampKey) || '0', 10)
if (data && ts && Date.now() - ts < this.cacheTimeout) {
this.cache = JSON.parse(data)
this.cacheTimestamp = ts
}
} catch (e) {
// ignore corrupt storage
}
}
_saveToStorage() {
try {
localStorage.setItem(this.storageKey, JSON.stringify(this.cache))
localStorage.setItem(this.storageTimestampKey, String(this.cacheTimestamp))
} catch (e) {
// storage full or unavailable
}
}
async getAll() {
if (this.cache && Date.now() - this.cacheTimestamp < this.cacheTimeout) {
return this.cache
}
if (this._pending) return this._pending
this._pending = directus.getCategories().then(categories => {
this.cache = categories
this.cacheTimestamp = Date.now()
this._saveToStorage()
this._pending = null
return categories
}).catch(err => {
this._pending = null
throw err
})
return this._pending
}
async getById(id) {
return directus.getCategory(id)
}
async getBySlug(slug) {
return directus.getCategory(slug)
}
async getTree() {
const all = await this.getAll()
return this.buildTree(all)
}
buildTree(categories, parentId = null) {
return categories
.filter(cat => (cat.parent?.id || cat.parent) === parentId)
.map(cat => ({
...cat,
children: this.buildTree(categories, cat.id)
}))
}
async getSubcategories(parentId) {
return directus.getSubcategories(parentId)
}
async getRootCategories() {
const all = await this.getAll()
return all.filter(cat => !cat.parent)
}
async getCategoryPath(categoryId) {
const all = await this.getAll()
const path = []
let current = all.find(c => c.id === categoryId)
while (current) {
path.unshift(current)
const parentId = current.parent?.id || current.parent
current = parentId ? all.find(c => c.id === parentId) : null
}
return path
}
async getCategoryWithChildren(categoryId) {
const all = await this.getAll()
const category = all.find(c => c.id === categoryId)
if (!category) return null
const collectChildren = (parentId) => {
return all
.filter(c => (c.parent?.id || c.parent) === parentId)
.map(c => ({
...c,
children: collectChildren(c.id)
}))
}
return {
...category,
children: collectChildren(categoryId)
}
}
getTranslatedName(category, lang = null) {
const currentLang = lang || getCurrentLanguage()
const directusCode = LOCALE_TO_DIRECTUS[currentLang] || currentLang
if (category.translations && Array.isArray(category.translations)) {
const translation = category.translations.find(
t => t.languages_code === directusCode
|| t.languages_code === currentLang
|| t.languages_code?.startsWith(currentLang)
)
if (translation?.name) {
return translation.name
}
}
return category.name
}
formatCategoryPath(categories, lang = null) {
return categories
.map(cat => this.getTranslatedName(cat, lang))
.join(' ')
}
async searchCategories(query) {
if (!query || query.length < 2) return []
const all = await this.getAll()
const lowerQuery = query.toLowerCase()
return all.filter(cat => {
if (cat.name?.toLowerCase().includes(lowerQuery)) return true
if (cat.slug?.toLowerCase().includes(lowerQuery)) return true
if (cat.translations) {
return cat.translations.some(t =>
t.name?.toLowerCase().includes(lowerQuery)
)
}
return false
})
}
async getCategoriesForSelect(includeChildren = true) {
const tree = await this.getTree()
const options = []
const flatten = (categories, depth = 0) => {
for (const cat of categories) {
options.push({
id: cat.id,
name: this.getTranslatedName(cat),
slug: cat.slug,
icon: cat.icon,
depth,
label: ' '.repeat(depth) + this.getTranslatedName(cat)
})
if (includeChildren && cat.children?.length > 0) {
flatten(cat.children, depth + 1)
}
}
}
flatten(tree)
return options
}
clearCache() {
this.cache = null
this.cacheTimestamp = 0
localStorage.removeItem(this.storageKey)
localStorage.removeItem(this.storageTimestampKey)
}
}
export const categoriesService = new CategoriesService()