File size: 9,978 Bytes
da819ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
const express = require('express');
const jwt = require('jsonwebtoken');
const SourceText = require('../models/SourceText');
const router = express.Router();

// Middleware to verify token (simplified - matches auth.js)
const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'Access token required' });
  }

  // For our simplified system, just check if token exists and has the right format
  if (token.startsWith('user_') || token.startsWith('visitor_')) {
    req.user = { token }; // We'll get user details from localStorage on frontend
    next();
  } else {
    return res.status(403).json({ error: 'Invalid token' });
  }
};

// Get all source texts with filters
router.get('/', authenticateToken, async (req, res) => {
  try {
    const {
      sourceLanguage,
      sourceCulture,
      targetCulture,
      difficulty,
      sourceType,
      tags,
      page = 1,
      limit = 10
    } = req.query;

    const filter = { isActive: true };
    
    if (sourceLanguage) filter.sourceLanguage = sourceLanguage;
    if (sourceCulture) filter.sourceCulture = sourceCulture;
    if (targetCulture) filter.targetCultures = targetCulture;
    if (difficulty) filter.difficulty = difficulty;
    if (sourceType) filter.sourceType = sourceType;
    if (tags) filter.tags = { $in: tags.split(',') };

    const skip = (page - 1) * limit;
    
    const texts = await SourceText.find(filter)
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(parseInt(limit))
      .populate('createdBy', 'username');

    const total = await SourceText.countDocuments(filter);

    res.json({
      texts,
      pagination: {
        page: parseInt(page),
        limit: parseInt(limit),
        total,
        pages: Math.ceil(total / limit)
      }
    });
  } catch (error) {
    console.error('Get source texts error:', error);
    res.status(500).json({ error: 'Failed to get source texts' });
  }
});

// Get a specific source text by ID
router.get('/:id', authenticateToken, async (req, res) => {
  try {
    const text = await SourceText.findById(req.params.id)
      .populate('createdBy', 'username');

    if (!text) {
      return res.status(404).json({ error: 'Source text not found' });
    }

    // Increment usage count
    text.usageCount += 1;
    await text.save();

    res.json(text);
  } catch (error) {
    console.error('Get source text error:', error);
    res.status(500).json({ error: 'Failed to get source text' });
  }
});

// Create a new source text
router.post('/', authenticateToken, async (req, res) => {
  try {
    const {
      title,
      content,
      sourceLanguage,
      sourceCulture,
      sourceUrl,
      sourceType,
      culturalElements,
      difficulty,
      tags,
      context,
      targetCultures,
      imageUrl,
      imageAlt,
      translationBrief,
      weekNumber,
      category
    } = req.body;

    // Validate required fields
    if (!title || !content || !sourceLanguage || !sourceCulture) {
      return res.status(400).json({ 
        error: 'Title, content, source language, and source culture are required' 
      });
    }

    const sourceText = new SourceText({
      title,
      content,
      sourceLanguage,
      sourceCulture,
      sourceUrl,
      sourceType,
      culturalElements: culturalElements || [],
      difficulty: difficulty || 'intermediate',
      tags: tags || [],
      context,
      targetCultures: targetCultures || [],
      imageUrl,
      imageAlt,
      translationBrief,
      weekNumber,
      category,
      createdBy: req.user.userId
    });

    await sourceText.save();

    res.status(201).json({
      message: 'Source text created successfully',
      sourceText
    });
  } catch (error) {
    console.error('Create source text error:', error);
    res.status(500).json({ error: 'Failed to create source text' });
  }
});

// Update a source text
router.put('/:id', authenticateToken, async (req, res) => {
  try {
    const sourceText = await SourceText.findById(req.params.id);
    
    if (!sourceText) {
      return res.status(404).json({ error: 'Source text not found' });
    }

    // Only allow creators or admins to update
    if (sourceText.createdBy.toString() !== req.user.userId && req.user.role !== 'admin') {
      return res.status(403).json({ error: 'Not authorized to update this source text' });
    }

    const updatedText = await SourceText.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    );

    res.json({
      message: 'Source text updated successfully',
      sourceText: updatedText
    });
  } catch (error) {
    console.error('Update source text error:', error);
    res.status(500).json({ error: 'Failed to update source text' });
  }
});

// Delete a source text (soft delete)
router.delete('/:id', authenticateToken, async (req, res) => {
  try {
    const sourceText = await SourceText.findById(req.params.id);
    
    if (!sourceText) {
      return res.status(404).json({ error: 'Source text not found' });
    }

    // Only allow creators or admins to delete
    if (sourceText.createdBy.toString() !== req.user.userId && req.user.role !== 'admin') {
      return res.status(403).json({ error: 'Not authorized to delete this source text' });
    }

    sourceText.isActive = false;
    await sourceText.save();

    res.json({ message: 'Source text deleted successfully' });
  } catch (error) {
    console.error('Delete source text error:', error);
    res.status(500).json({ error: 'Failed to delete source text' });
  }
});

// Rate a source text
router.post('/:id/rate', authenticateToken, async (req, res) => {
  try {
    const { rating } = req.body;
    
    if (!rating || rating < 1 || rating > 5) {
      return res.status(400).json({ error: 'Rating must be between 1 and 5' });
    }

    const sourceText = await SourceText.findById(req.params.id);
    
    if (!sourceText) {
      return res.status(404).json({ error: 'Source text not found' });
    }

    // Update average rating
    const newTotal = (sourceText.averageRating * sourceText.ratingCount) + rating;
    sourceText.ratingCount += 1;
    sourceText.averageRating = newTotal / sourceText.ratingCount;

    await sourceText.save();

    res.json({
      message: 'Rating submitted successfully',
      averageRating: sourceText.averageRating,
      ratingCount: sourceText.ratingCount
    });
  } catch (error) {
    console.error('Rate source text error:', error);
    res.status(500).json({ error: 'Failed to submit rating' });
  }
});

// Get weekly practice data by week number (temporarily public for testing)
router.get('/weekly-practice/week/:weekNumber', async (req, res) => {
  try {
    const weekNumber = parseInt(req.params.weekNumber);
    
    if (isNaN(weekNumber) || weekNumber < 1 || weekNumber > 6) {
      return res.status(400).json({ error: 'Invalid week number. Must be between 1 and 6.' });
    }

    // Debug: Log what we're searching for
    console.log(`Searching for weekly practice week ${weekNumber}`);
    
    const practices = await SourceText.find({
      category: 'weekly-practice',
      weekNumber: weekNumber,
      isActive: true
    }).sort({ createdAt: 1 });

    console.log(`Found ${practices.length} practices for week ${weekNumber}`);

    if (practices.length === 0) {
      // Debug: Check what's actually in the database
      const allTexts = await SourceText.find({});
      console.log(`Total texts in database: ${allTexts.length}`);
      console.log('Sample texts:', allTexts.slice(0, 3).map(t => ({ title: t.title, category: t.category, weekNumber: t.weekNumber })));
      
      return res.status(404).json({ 
        error: `No weekly practice found for week ${weekNumber}`,
        practices: [],
        weekNumber: weekNumber,
        debug: {
          totalTexts: allTexts.length,
          sampleTexts: allTexts.slice(0, 3).map(t => ({ title: t.title, category: t.category, weekNumber: t.weekNumber }))
        }
      });
    }

    res.json({
      weekNumber: weekNumber,
      practices: practices,
      total: practices.length
    });
  } catch (error) {
    console.error('Get weekly practice error:', error);
    res.status(500).json({ error: 'Failed to get weekly practice data' });
  }
});

// Get cultural elements for a source text
router.get('/:id/cultural-elements', authenticateToken, async (req, res) => {
  try {
    const sourceText = await SourceText.findById(req.params.id);
    
    if (!sourceText) {
      return res.status(404).json({ error: 'Source text not found' });
    }

    res.json({
      culturalElements: sourceText.culturalElements,
      content: sourceText.content
    });
  } catch (error) {
    console.error('Get cultural elements error:', error);
    res.status(500).json({ error: 'Failed to get cultural elements' });
  }
});

// Highlight cultural elements in text
router.post('/:id/highlight', authenticateToken, async (req, res) => {
  try {
    const { culturalElements } = req.body;
    
    const sourceText = await SourceText.findById(req.params.id);
    
    if (!sourceText) {
      return res.status(404).json({ error: 'Source text not found' });
    }

    // Only allow creators or admins to update cultural elements
    if (sourceText.createdBy.toString() !== req.user.userId && req.user.role !== 'admin') {
      return res.status(403).json({ error: 'Not authorized to update this source text' });
    }

    sourceText.culturalElements = culturalElements;
    await sourceText.save();

    res.json({
      message: 'Cultural elements updated successfully',
      culturalElements: sourceText.culturalElements
    });
  } catch (error) {
    console.error('Highlight cultural elements error:', error);
    res.status(500).json({ error: 'Failed to update cultural elements' });
  }
});

module.exports = router;