Wochenreport

This commit is contained in:
itsrubberduck
2026-05-06 17:38:36 +02:00
parent 2bcd27c635
commit f38b47acbd
14 changed files with 954 additions and 1 deletions

View File

@@ -0,0 +1,41 @@
import mongoose from 'mongoose'
export type FeedbackProduct = 'classroom' | 'liveatc'
export interface FeedbackSubmissionDocument extends mongoose.Document {
product: FeedbackProduct
name?: string
email?: string
discordHandle?: string
excitement: number
highlightSelections: string[]
highlightNotes?: string
frictionSelections: string[]
frictionNotes?: string
classroomNotes?: string
hostingInterest?: string
otherIdeas?: string
contactConsent: boolean
createdAt: Date
}
const feedbackSubmissionSchema = new mongoose.Schema<FeedbackSubmissionDocument>({
product: { type: String, enum: ['classroom', 'liveatc'], default: 'classroom', index: true },
name: { type: String, trim: true },
email: { type: String, lowercase: true, trim: true },
discordHandle: { type: String, trim: true },
excitement: { type: Number, required: true, min: 1, max: 5 },
highlightSelections: { type: [String], default: () => [] },
highlightNotes: { type: String, trim: true },
frictionSelections: { type: [String], default: () => [] },
frictionNotes: { type: String, trim: true },
classroomNotes: { type: String, trim: true },
hostingInterest: { type: String, trim: true },
otherIdeas: { type: String, trim: true },
contactConsent: { type: Boolean, default: false },
createdAt: { type: Date, default: () => new Date(), index: true },
})
export const FeedbackSubmission =
(mongoose.models.FeedbackSubmission as mongoose.Model<FeedbackSubmissionDocument> | undefined) ||
mongoose.model<FeedbackSubmissionDocument>('FeedbackSubmission', feedbackSubmissionSchema)

View File

@@ -0,0 +1,23 @@
import mongoose from 'mongoose'
export interface KpiReportDeliveryDocument extends mongoose.Document {
weekKey: string
periodStart: Date
periodEnd: Date
recipient: string
sentAt: Date
mailAccepted: boolean
}
const kpiReportDeliverySchema = new mongoose.Schema<KpiReportDeliveryDocument>({
weekKey: { type: String, required: true, unique: true, trim: true, index: true },
periodStart: { type: Date, required: true },
periodEnd: { type: Date, required: true },
recipient: { type: String, required: true, trim: true },
sentAt: { type: Date, default: () => new Date() },
mailAccepted: { type: Boolean, default: false },
})
export const KpiReportDelivery =
(mongoose.models.KpiReportDelivery as mongoose.Model<KpiReportDeliveryDocument> | undefined) ||
mongoose.model<KpiReportDeliveryDocument>('KpiReportDelivery', kpiReportDeliverySchema)

View File

@@ -0,0 +1,28 @@
import mongoose from 'mongoose'
export type LandingAnalyticsEventType = 'view' | 'scrolled' | 'waitlist_submit'
export type AnalyticsProduct = 'classroom' | 'liveatc'
export interface LandingAnalyticsEventDocument extends mongoose.Document {
type: LandingAnalyticsEventType
product: AnalyticsProduct
sessionId?: string
path?: string
source?: string
scrollDepth?: number
createdAt: Date
}
const landingAnalyticsEventSchema = new mongoose.Schema<LandingAnalyticsEventDocument>({
type: { type: String, enum: ['view', 'scrolled', 'waitlist_submit'], required: true, index: true },
product: { type: String, enum: ['classroom', 'liveatc'], default: 'classroom', index: true },
sessionId: { type: String, trim: true, index: true },
path: { type: String, trim: true },
source: { type: String, trim: true },
scrollDepth: { type: Number, min: 0, max: 100 },
createdAt: { type: Date, default: () => new Date(), index: true },
})
export const LandingAnalyticsEvent =
(mongoose.models.LandingAnalyticsEvent as mongoose.Model<LandingAnalyticsEventDocument> | undefined) ||
mongoose.model<LandingAnalyticsEventDocument>('LandingAnalyticsEvent', landingAnalyticsEventSchema)

View File

@@ -0,0 +1,27 @@
import mongoose from 'mongoose'
export type ProductUsageKey = 'classroom' | 'liveatc'
export interface ProductUsageSessionDocument extends mongoose.Document {
user?: mongoose.Types.ObjectId
product: ProductUsageKey
path?: string
durationSeconds: number
startedAt: Date
endedAt: Date
createdAt: Date
}
const productUsageSessionSchema = new mongoose.Schema<ProductUsageSessionDocument>({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true },
product: { type: String, enum: ['classroom', 'liveatc'], required: true, index: true },
path: { type: String, trim: true },
durationSeconds: { type: Number, required: true, min: 0 },
startedAt: { type: Date, required: true },
endedAt: { type: Date, required: true, index: true },
createdAt: { type: Date, default: () => new Date() },
})
export const ProductUsageSession =
(mongoose.models.ProductUsageSession as mongoose.Model<ProductUsageSessionDocument> | undefined) ||
mongoose.model<ProductUsageSessionDocument>('ProductUsageSession', productUsageSessionSchema)

View File

@@ -7,6 +7,7 @@ export interface WaitlistEntryDocument extends mongoose.Document {
name?: string
notes?: string
source?: string
product: 'classroom' | 'liveatc'
consentPrivacy: boolean
consentTerms: boolean
joinedAt: Date
@@ -28,6 +29,7 @@ const waitlistSchema = new mongoose.Schema<WaitlistEntryDocument>({
name: { type: String, trim: true },
notes: { type: String, trim: true },
source: { type: String, default: 'landing' },
product: { type: String, enum: ['classroom', 'liveatc'], default: 'classroom', index: true },
consentPrivacy: { type: Boolean, default: false },
consentTerms: { type: Boolean, default: false },
joinedAt: { type: Date, default: () => new Date() },