Update files
This commit is contained in:
parent
48a3955c37
commit
4150c03bdb
|
|
@ -19,6 +19,8 @@ router.get('/', async (req, res) => {
|
|||
.sort({ usageCount: -1, name: 1 })
|
||||
.select('name category description usageCount');
|
||||
|
||||
console.log(`[TAGS API] Found ${tags.length} tags with status 'approved'`);
|
||||
|
||||
// Группировка по категориям
|
||||
const grouped = {
|
||||
theme: [],
|
||||
|
|
@ -33,6 +35,8 @@ router.get('/', async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
console.log(`[TAGS API] Grouped:`, Object.keys(grouped).map(k => `${k}: ${grouped[k].length}`).join(', '));
|
||||
|
||||
res.json({ tags: grouped, all: tags });
|
||||
} catch (error) {
|
||||
logError('Ошибка получения тегов', error);
|
||||
|
|
|
|||
|
|
@ -53,10 +53,20 @@ async function initTags() {
|
|||
for (const tagData of INITIAL_TAGS) {
|
||||
const existing = await Tag.findOne({ name: tagData.name });
|
||||
if (!existing) {
|
||||
await Tag.create(tagData);
|
||||
// Явно устанавливаем статус
|
||||
await Tag.create({
|
||||
...tagData,
|
||||
status: 'approved' // Явно устанавливаем статус
|
||||
});
|
||||
created++;
|
||||
console.log(`✅ Создан тег: ${tagData.name}`);
|
||||
} else {
|
||||
// Обновляем статус существующего тега, если он не approved
|
||||
if (existing.status !== 'approved') {
|
||||
existing.status = 'approved';
|
||||
await existing.save();
|
||||
console.log(`✅ Обновлен статус тега: ${tagData.name}`);
|
||||
}
|
||||
skipped++;
|
||||
console.log(`⏭️ Тег уже существует: ${tagData.name}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,15 @@
|
|||
.empty-state p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-state-hint {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
text-align: center;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
|
|
@ -124,6 +133,10 @@
|
|||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .btn-primary {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { useSearchParams, useNavigate } from 'react-router-dom'
|
||||
import { getPosts } from '../utils/api'
|
||||
import PostCard from '../components/PostCard'
|
||||
import CreatePostModal from '../components/CreatePostModal'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { Plus, Settings } from 'lucide-react'
|
||||
import { hapticFeedback } from '../utils/telegram'
|
||||
import './Feed.css'
|
||||
|
||||
export default function Feed({ user }) {
|
||||
const [searchParams] = useSearchParams()
|
||||
const navigate = useNavigate()
|
||||
const [posts, setPosts] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
||||
|
|
@ -153,10 +154,41 @@ export default function Feed({ user }) {
|
|||
</div>
|
||||
) : posts.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<p>Пока нет постов</p>
|
||||
<button className="btn-primary" onClick={handleCreatePost}>
|
||||
Создать первый пост
|
||||
</button>
|
||||
{filter === 'interests' ? (
|
||||
<>
|
||||
<p>Нет постов по вашим интересам</p>
|
||||
<p className="empty-state-hint">
|
||||
Проверьте выбранные теги в настройках профиля
|
||||
</p>
|
||||
<button
|
||||
className="btn-primary"
|
||||
onClick={() => {
|
||||
hapticFeedback('light')
|
||||
navigate('/profile')
|
||||
}}
|
||||
>
|
||||
<Settings size={18} style={{ marginRight: '8px' }} />
|
||||
Открыть настройки
|
||||
</button>
|
||||
</>
|
||||
) : filter === 'following' ? (
|
||||
<>
|
||||
<p>Нет постов от подписок</p>
|
||||
<p className="empty-state-hint">
|
||||
Подпишитесь на пользователей, чтобы видеть их посты здесь
|
||||
</p>
|
||||
<button className="btn-primary" onClick={handleCreatePost}>
|
||||
Создать пост
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p>Пока нет постов</p>
|
||||
<button className="btn-primary" onClick={handleCreatePost}>
|
||||
Создать первый пост
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -685,6 +685,20 @@
|
|||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.empty-tags-message {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.empty-tags-message code {
|
||||
background: var(--bg-primary);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Модалка с информацией о теге */
|
||||
.tag-info-modal {
|
||||
max-width: 400px;
|
||||
|
|
|
|||
|
|
@ -123,6 +123,9 @@ export default function Profile({ user, setUser }) {
|
|||
try {
|
||||
setLoadingTags(true)
|
||||
const data = await getTags()
|
||||
console.log('[Profile] Tags data received:', data)
|
||||
console.log('[Profile] Tags grouped:', data.tags)
|
||||
console.log('[Profile] Tags all:', data.all)
|
||||
setAllTags(data.tags || { theme: [], style: [], mood: [], technical: [] })
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки тегов:', error)
|
||||
|
|
@ -537,57 +540,72 @@ export default function Profile({ user, setUser }) {
|
|||
<div className="spinner" />
|
||||
</div>
|
||||
) : (
|
||||
Object.entries(TAG_CATEGORIES).map(([categoryKey, categoryName]) => {
|
||||
const categoryTags = allTags[categoryKey] || []
|
||||
if (categoryTags.length === 0) return null
|
||||
(() => {
|
||||
const hasAnyTags = Object.values(allTags).some(tags => tags.length > 0)
|
||||
|
||||
return (
|
||||
<div key={categoryKey} className="tag-category-section">
|
||||
<h3 className="tag-category-title">{categoryName}</h3>
|
||||
<div className="tags-grid">
|
||||
{categoryTags.map(tag => {
|
||||
const isSelected = selectedTags.includes(tag.name)
|
||||
return (
|
||||
<div
|
||||
key={tag.name}
|
||||
className={`tag-preference-item ${isSelected ? 'selected' : ''}`}
|
||||
onClick={() => toggleTag(tag.name)}
|
||||
>
|
||||
<div className="tag-preference-content">
|
||||
<span className="tag-preference-name">{tag.name}</span>
|
||||
{tag.description && (
|
||||
<div
|
||||
className="tag-preference-description"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setShowTagInfo({
|
||||
name: tag.name,
|
||||
description: tag.description,
|
||||
category: TAG_CATEGORIES[tag.category] || tag.category
|
||||
})
|
||||
hapticFeedback('light')
|
||||
}}
|
||||
>
|
||||
<Info size={12} />
|
||||
<span>Нажмите для описания</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="tag-preference-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
onChange={() => toggleTag(tag.name)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
if (!hasAnyTags) {
|
||||
return (
|
||||
<div className="empty-tags-message">
|
||||
<p>Теги не найдены. Убедитесь, что скрипт инициализации тегов был запущен.</p>
|
||||
<p style={{ fontSize: '12px', color: 'var(--text-secondary)', marginTop: '8px' }}>
|
||||
Запустите: <code>node backend/scripts/initTags.js</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return Object.entries(TAG_CATEGORIES).map(([categoryKey, categoryName]) => {
|
||||
const categoryTags = allTags[categoryKey] || []
|
||||
if (categoryTags.length === 0) return null
|
||||
|
||||
return (
|
||||
<div key={categoryKey} className="tag-category-section">
|
||||
<h3 className="tag-category-title">{categoryName}</h3>
|
||||
<div className="tags-grid">
|
||||
{categoryTags.map(tag => {
|
||||
const isSelected = selectedTags.includes(tag.name)
|
||||
return (
|
||||
<div
|
||||
key={tag.name}
|
||||
className={`tag-preference-item ${isSelected ? 'selected' : ''}`}
|
||||
onClick={() => toggleTag(tag.name)}
|
||||
>
|
||||
<div className="tag-preference-content">
|
||||
<span className="tag-preference-name">{tag.name}</span>
|
||||
{tag.description && (
|
||||
<div
|
||||
className="tag-preference-description"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setShowTagInfo({
|
||||
name: tag.name,
|
||||
description: tag.description,
|
||||
category: TAG_CATEGORIES[tag.category] || tag.category
|
||||
})
|
||||
hapticFeedback('light')
|
||||
}}
|
||||
>
|
||||
<Info size={12} />
|
||||
<span>Нажмите для описания</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="tag-preference-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
onChange={() => toggleTag(tag.name)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
})()
|
||||
)}
|
||||
|
||||
{selectedTags.length > 0 && (
|
||||
|
|
|
|||
Loading…
Reference in New Issue