- Set up pnpm workspace with frontend (React + Vite) and backend (Express + Prisma) - Configure TypeScript, ESLint, and Prettier - Add Prisma schema for database models (User, Course, Lesson, Progress, etc.) - Create basic frontend structure with Tailwind CSS and shadcn/ui - Add environment configuration files - Update README with project overview and setup instructions - Complete Phase 0: Project initialization
233 lines
6.3 KiB
Plaintext
233 lines
6.3 KiB
Plaintext
// prisma/schema.prisma
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
previewFeatures = ["fullTextIndex"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "mysql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ============================================
|
|
// AUTHENTICATION & USERS
|
|
// ============================================
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
email String @unique
|
|
displayName String? @map("display_name")
|
|
passwordHash String? @map("password_hash")
|
|
authProvider String @default("local") @map("auth_provider") // local, google, microsoft
|
|
providerId String? @map("provider_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
documents Document[]
|
|
comments Comment[]
|
|
bugReports BugReport[]
|
|
|
|
@@map("users")
|
|
}
|
|
|
|
// ============================================
|
|
// DICTIONARY / TERMS
|
|
// ============================================
|
|
|
|
model Term {
|
|
id String @id @default(uuid())
|
|
wordText String @map("word_text") @db.VarChar(255)
|
|
normalizedText String @map("normalized_text") @db.VarChar(255)
|
|
language String @default("hr") @db.VarChar(10)
|
|
wordType WordType @map("word_type")
|
|
cefrLevel CefrLevel @map("cefr_level")
|
|
shortDescription String? @map("short_description") @db.Text
|
|
tags String? @db.Text // JSON array stored as text
|
|
iconAssetId String? @map("icon_asset_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
media TermMedia[]
|
|
examples TermExample[]
|
|
sentenceTokens SentenceToken[]
|
|
|
|
@@index([normalizedText])
|
|
@@fulltext([wordText, normalizedText])
|
|
@@map("terms")
|
|
}
|
|
|
|
model TermMedia {
|
|
id String @id @default(uuid())
|
|
termId String @map("term_id")
|
|
kind MediaKind
|
|
url String @db.VarChar(500)
|
|
durationMs Int? @map("duration_ms")
|
|
width Int?
|
|
height Int?
|
|
checksum String? @db.VarChar(64)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
term Term @relation(fields: [termId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([termId])
|
|
@@map("term_media")
|
|
}
|
|
|
|
model TermExample {
|
|
id String @id @default(uuid())
|
|
termId String @map("term_id")
|
|
exampleText String @map("example_text") @db.Text
|
|
notes String? @db.Text
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
term Term @relation(fields: [termId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([termId])
|
|
@@map("term_examples")
|
|
}
|
|
|
|
// ============================================
|
|
// DOCUMENTS & SENTENCES (ZNAKOPIS)
|
|
// ============================================
|
|
|
|
model Document {
|
|
id String @id @default(uuid())
|
|
ownerUserId String? @map("owner_user_id")
|
|
title String @db.VarChar(255)
|
|
description String? @db.Text
|
|
visibility Visibility @default(PRIVATE)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
owner User? @relation(fields: [ownerUserId], references: [id], onDelete: SetNull)
|
|
pages DocumentPage[]
|
|
|
|
@@index([ownerUserId])
|
|
@@map("documents")
|
|
}
|
|
|
|
model DocumentPage {
|
|
id String @id @default(uuid())
|
|
documentId String @map("document_id")
|
|
pageIndex Int @map("page_index")
|
|
title String? @db.VarChar(255)
|
|
|
|
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
sentences Sentence[]
|
|
|
|
@@unique([documentId, pageIndex])
|
|
@@index([documentId])
|
|
@@map("document_pages")
|
|
}
|
|
|
|
model Sentence {
|
|
id String @id @default(uuid())
|
|
documentPageId String @map("document_page_id")
|
|
sentenceIndex Int @map("sentence_index")
|
|
rawText String? @map("raw_text") @db.Text
|
|
|
|
documentPage DocumentPage @relation(fields: [documentPageId], references: [id], onDelete: Cascade)
|
|
tokens SentenceToken[]
|
|
|
|
@@unique([documentPageId, sentenceIndex])
|
|
@@index([documentPageId])
|
|
@@map("sentences")
|
|
}
|
|
|
|
model SentenceToken {
|
|
id String @id @default(uuid())
|
|
sentenceId String @map("sentence_id")
|
|
tokenIndex Int @map("token_index")
|
|
termId String? @map("term_id")
|
|
displayText String @map("display_text") @db.VarChar(255)
|
|
isPunctuation Boolean @default(false) @map("is_punctuation")
|
|
|
|
sentence Sentence @relation(fields: [sentenceId], references: [id], onDelete: Cascade)
|
|
term Term? @relation(fields: [termId], references: [id], onDelete: SetNull)
|
|
|
|
@@unique([sentenceId, tokenIndex])
|
|
@@index([sentenceId])
|
|
@@index([termId])
|
|
@@map("sentence_tokens")
|
|
}
|
|
|
|
// ============================================
|
|
// COMMUNITY & SUPPORT
|
|
// ============================================
|
|
|
|
model Comment {
|
|
id String @id @default(uuid())
|
|
userId String @map("user_id")
|
|
content String @db.Text
|
|
context String? @db.VarChar(255) // e.g., "term:uuid" or "general"
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@map("comments")
|
|
}
|
|
|
|
model BugReport {
|
|
id String @id @default(uuid())
|
|
userId String? @map("user_id")
|
|
title String @db.VarChar(255)
|
|
description String @db.Text
|
|
status BugStatus @default(OPEN)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@map("bug_reports")
|
|
}
|
|
|
|
// ============================================
|
|
// ENUMS
|
|
// ============================================
|
|
|
|
enum WordType {
|
|
NOUN
|
|
VERB
|
|
ADJECTIVE
|
|
ADVERB
|
|
PRONOUN
|
|
PREPOSITION
|
|
CONJUNCTION
|
|
INTERJECTION
|
|
PHRASE
|
|
OTHER
|
|
}
|
|
|
|
enum CefrLevel {
|
|
A1
|
|
A2
|
|
B1
|
|
B2
|
|
C1
|
|
C2
|
|
}
|
|
|
|
enum MediaKind {
|
|
VIDEO
|
|
IMAGE
|
|
ILLUSTRATION
|
|
}
|
|
|
|
enum Visibility {
|
|
PRIVATE
|
|
SHARED
|
|
PUBLIC
|
|
}
|
|
|
|
enum BugStatus {
|
|
OPEN
|
|
IN_PROGRESS
|
|
RESOLVED
|
|
CLOSED
|
|
}
|
|
|