commit 0f85059e92640ba4f6129c333a394606ef2b23c9 Author: Alexander Andreev Date: Fri Jan 2 14:57:37 2026 +0300 feat: core domain models (habits, village, quests) diff --git a/prisma/migrations/20260102113639_init/migration.sql b/prisma/migrations/20260102113639_init/migration.sql new file mode 100644 index 0000000..ac0bba9 --- /dev/null +++ b/prisma/migrations/20260102113639_init/migration.sql @@ -0,0 +1,9 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "email" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..2a5a444 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "sqlite" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..902d0e2 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,116 @@ +// prisma/schema.prisma + +// Enums +// VillageObjectType: Defines the possible types of objects that can be placed in the village grid. +enum VillageObjectType { + HOUSE + FIELD + ROAD + FENCE + OBSTACLE +} + +// CropType: Defines the types of crops that can be planted. +enum CropType { + BLUEBERRIES + CORN +} + +// User: Represents a registered user and stores their core profile, +// settings, and in-game resources like coins and experience points. +model User { + id Int @id @default(autoincrement()) + email String @unique + password String + nickname String? + avatar String? @default("/avatars/default.png") + + coins Int @default(0) + exp Int @default(0) + + // User settings + soundOn Boolean @default(true) + confettiOn Boolean @default(true) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // Relations + habits Habit[] + dailyVisits DailyVisit[] + village Village? +} + +// Habit: A recurring task a user wants to build. Each habit belongs to a +// single user. The `daysOfWeek` field stores which days the habit is active. +model Habit { + id Int @id @default(autoincrement()) + name String + // `daysOfWeek` is a JSON array of numbers [0, 1, ..., 6], where 0 is Sunday. + daysOfWeek Json + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // Relations + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int + completions HabitCompletion[] +} + +// HabitCompletion: Records a single completion of a habit on a specific date. +// This creates a history of the user's progress for each habit. +model HabitCompletion { + id Int @id @default(autoincrement()) + date DateTime @db.Date // Store only the date part + + // Relations + habit Habit @relation(fields: [habitId], references: [id], onDelete: Cascade) + habitId Int + + @@unique([habitId, date]) // A habit can only be completed once per day +} + +// DailyVisit: Tracks the user's daily visit for the "I visited the site today" +// quest and for calculating 5-day streaks. +model DailyVisit { + id Int @id @default(autoincrement()) + date DateTime @db.Date // Store only the date part + + // Relations + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int + + @@unique([userId, date]) // A user can only have one recorded visit per day +} + +// Village: The user's personal village, which acts as a container for all +// village objects. Each user has exactly one village. +model Village { + id Int @id @default(autoincrement()) + + // Relations + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int @unique // Each user has only one village + objects VillageObject[] +} + +// VillageObject: An object (e.g., house, field, obstacle) placed on the +// village grid. It stores the object's type, its coordinates, and optionally +// details if it's an obstacle or a planted crop. +model VillageObject { + id Int @id @default(autoincrement()) + type VillageObjectType + x Int + y Int + obstacleMetadata String? // Stores metadata for obstacles (e.g., "rock", "bush"). + + // Crop details (only if type is FIELD) + cropType CropType? + plantedAt DateTime? + + // Relations + village Village @relation(fields: [villageId], references: [id], onDelete: Cascade) + villageId Int + + @@unique([villageId, x, y]) // Ensure only one object per grid cell per village +} \ No newline at end of file