diff --git a/app/teknav-store.js b/app/teknav-store.js new file mode 100644 index 0000000..8bf4594 --- /dev/null +++ b/app/teknav-store.js @@ -0,0 +1,156 @@ +// teknav-store.js — Frontend state management via localStorage (ES module) +import { TeknavData } from './teknav-data.js'; + +export const TeknavStore = (() => { + const DATA_VERSION = '2026-05-02-v1'; + + const LS = { + get: (k, def) => { try { const v = localStorage.getItem(k); return v ? JSON.parse(v) : def; } catch { return def; } }, + set: (k, v) => { try { localStorage.setItem(k, JSON.stringify(v)); } catch {} }, + }; + + // Version check — Clear old 2024 placeholder data + if (localStorage.getItem('teknav_data_version') !== DATA_VERSION) { + localStorage.removeItem('teknav_articles'); + localStorage.removeItem('teknav_activity'); + localStorage.setItem('teknav_data_version', DATA_VERSION); + } + + // Session + const getUser = () => LS.get('teknav_user', null); + const setUser = (user) => LS.set('teknav_user', user); + const logout = () => { localStorage.removeItem('teknav_user'); }; + + const login = (_email, _password) => { + // localStorage auth removed. All auth goes through /api/auth/login. + return null; + }; + + // Articles (seed + created) + const getArticles = () => { + const created = LS.get('teknav_articles', []); + return [...TeknavData.articles, ...created]; + }; + + const createArticle = (article) => { + const articles = LS.get('teknav_articles', []); + const newArt = { ...article, id: 'art_' + Date.now(), date: new Date().toLocaleDateString('fa-IR'), views: 0, reactions: 0 }; + LS.set('teknav_articles', [newArt, ...articles]); + addActivity('مقاله جدید ایجاد شد', newArt.title, 'draft'); + return newArt; + }; + + const updateArticle = (id, updates) => { + // Update in seed if exists + const seedIdx = TeknavData.articles.findIndex(a => a.id === id); + if (seedIdx !== -1) { + Object.assign(TeknavData.articles[seedIdx], updates); + return; + } + const articles = LS.get('teknav_articles', []); + const idx = articles.findIndex(a => a.id === id); + if (idx !== -1) { articles[idx] = { ...articles[idx], ...updates }; LS.set('teknav_articles', articles); } + addActivity('مقاله ویرایش شد', updates.title || id, 'edit'); + }; + + const deleteArticle = (id) => { + const articles = LS.get('teknav_articles', []); + LS.set('teknav_articles', articles.filter(a => a.id !== id)); + addActivity('مقاله حذف شد', id, 'delete'); + }; + + // Saved articles + const getSaved = () => LS.get('teknav_saved', []); + const toggleSaved = (id) => { + const saved = getSaved(); + const newSaved = saved.includes(id) ? saved.filter(s => s !== id) : [...saved, id]; + LS.set('teknav_saved', newSaved); + return newSaved.includes(id); + }; + const isSaved = (id) => getSaved().includes(id); + + // Newsletter + const subscribeNewsletter = (email) => { + const subs = LS.get('teknav_newsletter', []); + if (subs.includes(email)) return false; + LS.set('teknav_newsletter', [...subs, email]); + return true; + }; + + // Reactions + const getReactions = () => LS.get('teknav_reactions', {}); + const toggleReaction = (articleId, type) => { + const reactions = getReactions(); + const key = `${articleId}_${type}`; + const reacted = reactions[key]; + reactions[key] = !reacted; + LS.set('teknav_reactions', reactions); + return !reacted; + }; + const hasReacted = (articleId, type) => getReactions()[`${articleId}_${type}`] || false; + + // Activity log + const getActivity = () => { + const extra = LS.get('teknav_activity', []); + return [...extra, ...TeknavData.activityLog]; + }; + const addActivity = (action, target, type) => { + const log = LS.get('teknav_activity', []); + const user = getUser(); + log.unshift({ id: Date.now(), user: user?.name || 'سیستم', action, target, time: 'همین الان', type }); + LS.set('teknav_activity', log.slice(0, 50)); + }; + + // Users management + const getUsers = () => { + const overrides = LS.get('teknav_users_override', {}); + return TeknavData.users.map(u => ({ ...u, ...(overrides[u.id] || {}) })); + }; + const updateUser = (id, updates) => { + const overrides = LS.get('teknav_users_override', {}); + overrides[id] = { ...(overrides[id] || {}), ...updates }; + LS.set('teknav_users_override', overrides); + }; + + // Categories management + const getCategories = () => { + const extra = LS.get('teknav_categories', []); + return [...TeknavData.categories, ...extra]; + }; + const addCategory = (cat) => { + const cats = LS.get('teknav_categories', []); + cats.push({ ...cat, id: 'cat_' + Date.now(), articleCount: 0, authorCount: 0, avgReadTime: 0 }); + LS.set('teknav_categories', cats); + }; + const deleteCategory = (id) => { + const cats = LS.get('teknav_categories', []); + LS.set('teknav_categories', cats.filter(c => c.id !== id)); + }; + + // Tags management + const getTags = () => { + const extra = LS.get('teknav_tags', []); + return [...TeknavData.tags, ...extra]; + }; + const addTag = (name) => { + const tags = LS.get('teknav_tags', []); + tags.push({ id: Date.now(), name, count: 0 }); + LS.set('teknav_tags', tags); + }; + const deleteTag = (id) => { + const tags = LS.get('teknav_tags', []); + LS.set('teknav_tags', tags.filter(t => t.id !== id)); + }; + + return { + getUser, setUser, logout, login, + getArticles, createArticle, updateArticle, deleteArticle, + getSaved, toggleSaved, isSaved, + subscribeNewsletter, + getReactions, toggleReaction, hasReacted, + getActivity, addActivity, + getUsers, updateUser, + getCategories, addCategory, deleteCategory, + getTags, addTag, deleteTag, + }; +})();