Spaces:
Sleeping
Sleeping
Site Maintainer
Ordering support: position field, append new items, admin position endpoints, sort by position+createdAt
8776c0f
| const mongoose = require('mongoose'); | |
| const culturalElementSchema = new mongoose.Schema({ | |
| element: { type: String, required: true }, | |
| description: { type: String, required: true }, | |
| significance: { type: String, required: true } | |
| }); | |
| const sourceTextSchema = new mongoose.Schema({ | |
| title: { type: String, required: true }, | |
| content: { type: String, required: true }, | |
| sourceLanguage: { type: String, required: true }, | |
| sourceType: { | |
| type: String, | |
| enum: ['api', 'manual', 'practice', 'tutorial', 'weekly-practice'], | |
| default: 'manual' | |
| }, | |
| category: { | |
| type: String, | |
| enum: ['practice', 'tutorial', 'weekly-practice'], | |
| required: true | |
| }, | |
| weekNumber: { | |
| type: Number, | |
| required: function() { return this.category !== 'practice'; } | |
| }, | |
| translationBrief: { type: String }, | |
| imageUrl: { type: String }, | |
| imageAlt: { type: String }, | |
| // Image-only configuration | |
| imageSize: { type: Number, default: 200 }, | |
| // Add 'portrait-split' for week 4–6 advanced layout (non-breaking, additive) | |
| imageAlignment: { type: String, enum: ['left', 'center', 'right', 'portrait-split'], default: 'center' }, | |
| // Optional display ordering within a week/category (ascending). If absent, fallback to createdAt. | |
| position: { type: Number }, | |
| culturalElements: [culturalElementSchema], | |
| difficulty: { | |
| type: String, | |
| enum: ['beginner', 'intermediate', 'advanced'], | |
| default: 'intermediate' | |
| }, | |
| tags: [String], | |
| targetCultures: [String], | |
| isActive: { type: Boolean, default: true }, | |
| usageCount: { type: Number, default: 0 }, | |
| averageRating: { type: Number, default: 0 }, | |
| ratingCount: { type: Number, default: 0 }, | |
| // Video subtitling specific fields | |
| interfaceType: { type: String, enum: ['standard', 'video-subtitling'] }, | |
| videoSource: { type: String }, | |
| totalSegments: { type: Number }, | |
| segmentId: { type: Number }, | |
| startTime: { type: String }, | |
| endTime: { type: String }, | |
| duration: { type: String }, | |
| isCurrent: { type: Boolean, default: false }, | |
| parentTask: { type: String }, | |
| // Configuration fields | |
| configType: { type: String }, | |
| description: { type: String }, | |
| // Protection fields | |
| isProtected: { type: Boolean, default: false }, | |
| protectedReason: { type: String }, | |
| lastModified: { type: Date, default: Date.now }, | |
| modificationHistory: [{ | |
| action: { type: String, required: true }, | |
| timestamp: { type: Date, default: Date.now }, | |
| reason: { type: String } | |
| }] | |
| }, { | |
| timestamps: true | |
| }); | |
| module.exports = mongoose.model('SourceText', sourceTextSchema); |