- Implement JWT-based authentication with login/logout - Add user management routes and middleware - Create admin panel for managing words and categories - Add authentication store and API client - Update database schema with User model - Configure CORS and authentication middleware - Add login page and protected routes
1409 lines
40 KiB
Markdown
1409 lines
40 KiB
Markdown
# Znakovni.hr - Proof of Concept Implementation Plan
|
|
|
|
## Executive Summary
|
|
This document provides a complete implementation plan for a 1:1 functional and visual replica of the Znakovni.hr Croatian Sign Language web application. This is a proof-of-concept build optimized for 1-2 concurrent users with local storage.
|
|
|
|
---
|
|
|
|
## 1. Technology Stack (PoC-Optimized)
|
|
|
|
### Frontend
|
|
- **Framework**: React 18.2+ with TypeScript
|
|
- **Build Tool**: Vite 5+
|
|
- **UI Framework**: Tailwind CSS 3+ (for exact pixel-perfect replication)
|
|
- **Component Library**: shadcn/ui (customizable, matches screenshot aesthetics)
|
|
- **State Management**: Zustand (lightweight sentence/document state)
|
|
- **Video Player**: Plyr (clean, customizable controls)
|
|
- **Drag & Drop**: @dnd-kit/core (sentence token reordering)
|
|
- **Routing**: React Router v6
|
|
- **HTTP Client**: Axios
|
|
- **Forms**: React Hook Form + Zod
|
|
- **Icons**: Lucide React (modern, clean icons)
|
|
|
|
### Backend
|
|
- **Runtime**: Node.js 20 LTS
|
|
- **Framework**: Express.js with TypeScript
|
|
- **Authentication**: Simple admin user + local user management (OAuth deferred to later phases)
|
|
- **Session**: express-session with MySQL session store
|
|
- **Validation**: Zod (shared with frontend)
|
|
- **File Upload**: Multer (for video/image uploads)
|
|
- **CORS**: cors middleware
|
|
|
|
### Database
|
|
- **RDBMS**: MySQL 8.0+
|
|
- **ORM**: Prisma 5+ (type-safe, excellent MySQL support)
|
|
- **Migrations**: Prisma Migrate
|
|
- **Full-Text Search**: MySQL FULLTEXT indexes
|
|
|
|
### Media Storage
|
|
- **Storage**: Local filesystem (`/uploads` directory)
|
|
- **Structure**: `/uploads/videos/`, `/uploads/icons/`, `/uploads/documents/`
|
|
- **Serving**: Express static middleware with proper MIME types
|
|
- **Video Format**: MP4 (H.264) for maximum compatibility
|
|
|
|
### Development Tools
|
|
- **Package Manager**: pnpm
|
|
- **Monorepo Structure**: pnpm workspaces (frontend + backend)
|
|
- **Linting**: ESLint + Prettier
|
|
- **Type Checking**: TypeScript strict mode
|
|
- **Environment**: dotenv
|
|
- **Development**: Concurrently (run frontend + backend together)
|
|
|
|
### Deployment (PoC)
|
|
- **Server**: Single VPS or local machine
|
|
- **Process Manager**: PM2
|
|
- **Reverse Proxy**: nginx (optional, for production-like setup)
|
|
- **Database**: MySQL server (local or managed)
|
|
|
|
---
|
|
|
|
## 2. Project Folder Structure
|
|
|
|
```
|
|
turkshop/
|
|
├── packages/
|
|
│ ├── frontend/ # React + Vite application
|
|
│ │ ├── public/
|
|
│ │ │ └── assets/ # Static assets
|
|
│ │ ├── src/
|
|
│ │ │ ├── components/ # Reusable UI components
|
|
│ │ │ │ ├── ui/ # shadcn/ui base components
|
|
│ │ │ │ ├── layout/ # Sidebar, Header, Layout
|
|
│ │ │ │ ├── dictionary/ # WordCard, WordGrid, FilterBar
|
|
│ │ │ │ ├── znakopis/ # SentenceBuilder, TokenTray
|
|
│ │ │ │ ├── video/ # VideoPlayer, PlaylistControls
|
|
│ │ │ │ └── cloud/ # DocumentBrowser, DocumentList
|
|
│ │ │ ├── pages/ # Route pages
|
|
│ │ │ │ ├── Home.tsx
|
|
│ │ │ │ ├── Dictionary.tsx
|
|
│ │ │ │ ├── Znakopis.tsx
|
|
│ │ │ │ ├── VideoSentence.tsx
|
|
│ │ │ │ ├── Cloud.tsx
|
|
│ │ │ │ ├── Auth.tsx
|
|
│ │ │ │ ├── Help.tsx
|
|
│ │ │ │ ├── Community.tsx
|
|
│ │ │ │ ├── Comments.tsx
|
|
│ │ │ │ ├── BugReport.tsx
|
|
│ │ │ │ └── admin/
|
|
│ │ │ │ └── UserManagement.tsx
|
|
│ │ │ ├── stores/ # Zustand stores
|
|
│ │ │ │ ├── authStore.ts
|
|
│ │ │ │ ├── sentenceStore.ts
|
|
│ │ │ │ └── documentStore.ts
|
|
│ │ │ ├── lib/ # Utilities
|
|
│ │ │ │ ├── api.ts # Axios instance
|
|
│ │ │ │ ├── utils.ts
|
|
│ │ │ │ └── constants.ts
|
|
│ │ │ ├── types/ # TypeScript types
|
|
│ │ │ │ └── index.ts
|
|
│ │ │ ├── hooks/ # Custom React hooks
|
|
│ │ │ ├── App.tsx
|
|
│ │ │ ├── main.tsx
|
|
│ │ │ └── index.css # Tailwind imports
|
|
│ │ ├── package.json
|
|
│ │ ├── vite.config.ts
|
|
│ │ ├── tailwind.config.js
|
|
│ │ └── tsconfig.json
|
|
│ │
|
|
│ └── backend/ # Express + TypeScript API
|
|
│ ├── src/
|
|
│ │ ├── routes/ # API route handlers
|
|
│ │ │ ├── auth.ts
|
|
│ │ │ ├── admin.ts
|
|
│ │ │ ├── terms.ts
|
|
│ │ │ ├── documents.ts
|
|
│ │ │ ├── sentences.ts
|
|
│ │ │ ├── playlists.ts
|
|
│ │ │ ├── community.ts
|
|
│ │ │ └── index.ts
|
|
│ │ ├── controllers/ # Business logic
|
|
│ │ │ ├── authController.ts
|
|
│ │ │ ├── adminController.ts
|
|
│ │ │ ├── termController.ts
|
|
│ │ │ ├── documentController.ts
|
|
│ │ │ └── sentenceController.ts
|
|
│ │ ├── middleware/ # Express middleware
|
|
│ │ │ ├── auth.ts
|
|
│ │ │ ├── validation.ts
|
|
│ │ │ ├── errorHandler.ts
|
|
│ │ │ └── upload.ts
|
|
│ │ ├── services/ # External services
|
|
│ │ │ ├── authService.ts
|
|
│ │ │ ├── storageService.ts
|
|
│ │ │ └── playlistService.ts
|
|
│ │ ├── lib/ # Utilities
|
|
│ │ │ ├── prisma.ts # Prisma client
|
|
│ │ │ └── auth.ts # Auth utilities (bcrypt, session)
|
|
│ │ ├── types/ # TypeScript types
|
|
│ │ │ └── index.ts
|
|
│ │ ├── config/ # Configuration
|
|
│ │ │ └── index.ts
|
|
│ │ └── server.ts # Express app entry
|
|
│ ├── prisma/
|
|
│ │ ├── schema.prisma # Database schema
|
|
│ │ ├── migrations/ # Migration files
|
|
│ │ └── seed.ts # Seed data
|
|
│ ├── uploads/ # Local file storage
|
|
│ │ ├── videos/
|
|
│ │ ├── icons/
|
|
│ │ └── documents/
|
|
│ ├── package.json
|
|
│ └── tsconfig.json
|
|
│
|
|
├── shared/ # Shared types/schemas (optional)
|
|
│ ├── types/
|
|
│ └── schemas/
|
|
│
|
|
├── package.json # Root package.json (workspace)
|
|
├── pnpm-workspace.yaml
|
|
├── .gitignore
|
|
├── README.md
|
|
└── main-plan.md # This file
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Database Schema (Prisma + MySQL)
|
|
|
|
### Complete Prisma Schema
|
|
|
|
```prisma
|
|
// prisma/schema.prisma
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
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")
|
|
role UserRole @default(USER)
|
|
isActive Boolean @default(true) @map("is_active")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
documents Document[]
|
|
comments Comment[]
|
|
bugReports BugReport[]
|
|
|
|
@@map("users")
|
|
}
|
|
|
|
enum UserRole {
|
|
ADMIN
|
|
USER
|
|
}
|
|
|
|
// ============================================
|
|
// 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
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. API Routes & Endpoints
|
|
|
|
### Base URL
|
|
- Development: `http://localhost:3000/api`
|
|
- Production: `https://yourdomain.com/api`
|
|
|
|
### 4.1 Authentication Routes (`/api/auth`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| POST | `/auth/login` | Login with email/password | No |
|
|
| POST | `/auth/logout` | Logout current user | Yes |
|
|
| GET | `/auth/me` | Get current user info | Yes |
|
|
|
|
### 4.2 Admin User Management Routes (`/api/admin/users`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| GET | `/admin/users` | List all users | Yes (Admin) |
|
|
| POST | `/admin/users` | Create new user | Yes (Admin) |
|
|
| PUT | `/admin/users/:id` | Update user | Yes (Admin) |
|
|
| DELETE | `/admin/users/:id` | Delete user | Yes (Admin) |
|
|
| PUT | `/admin/users/:id/password` | Reset user password | Yes (Admin) |
|
|
| PUT | `/admin/users/:id/activate` | Activate/deactivate user | Yes (Admin) |
|
|
|
|
### 4.3 Dictionary/Terms Routes (`/api/terms`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| GET | `/terms` | List/search terms with filters | No |
|
|
| GET | `/terms/:id` | Get single term details | No |
|
|
| GET | `/terms/:id/media` | Get all media for a term | No |
|
|
| GET | `/terms/:id/examples` | Get examples for a term | No |
|
|
| POST | `/terms` | Create new term (admin) | Yes (Admin) |
|
|
| PUT | `/terms/:id` | Update term (admin) | Yes (Admin) |
|
|
| DELETE | `/terms/:id` | Delete term (admin) | Yes (Admin) |
|
|
|
|
**Query Parameters for GET `/terms`:**
|
|
- `query` (string): Search text
|
|
- `wordType` (enum): Filter by word type
|
|
- `cefrLevel` (enum): Filter by CEFR level
|
|
- `page` (number): Page number (default: 1)
|
|
- `limit` (number): Items per page (default: 20)
|
|
- `sortBy` (string): Sort field (default: wordText)
|
|
- `sortOrder` (asc|desc): Sort direction (default: asc)
|
|
|
|
### 4.4 Document Routes (`/api/documents`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| GET | `/documents` | List user's documents | Yes |
|
|
| GET | `/documents/:id` | Get full document with pages/sentences | Yes |
|
|
| POST | `/documents` | Create new document | Yes |
|
|
| PUT | `/documents/:id` | Update document metadata | Yes |
|
|
| DELETE | `/documents/:id` | Delete document | Yes |
|
|
| PUT | `/documents/:id/content` | Update document content (pages/sentences) | Yes |
|
|
| POST | `/documents/:id/share` | Generate share link | Yes |
|
|
|
|
### 4.5 Sentence Routes (`/api/sentences`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| GET | `/sentences/:id` | Get sentence with tokens | Yes |
|
|
| POST | `/sentences` | Create new sentence | Yes |
|
|
| PUT | `/sentences/:id` | Update sentence | Yes |
|
|
| DELETE | `/sentences/:id` | Delete sentence | Yes |
|
|
| POST | `/sentences/:id/tokens` | Add token to sentence | Yes |
|
|
| PUT | `/sentences/:id/tokens/:tokenId` | Update token | Yes |
|
|
| DELETE | `/sentences/:id/tokens/:tokenId` | Remove token | Yes |
|
|
| PUT | `/sentences/:id/reorder` | Reorder tokens | Yes |
|
|
|
|
### 4.6 Playlist Routes (`/api/playlists`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| POST | `/playlists/generate` | Generate video playlist from sentence | No |
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"sentenceId": "uuid",
|
|
"tokenIds": ["uuid1", "uuid2", "uuid3"]
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"playlist": [
|
|
{
|
|
"tokenId": "uuid1",
|
|
"termId": "uuid",
|
|
"displayText": "Dobar",
|
|
"videoUrl": "/uploads/videos/dobar.mp4",
|
|
"durationMs": 2500,
|
|
"thumbnailUrl": "/uploads/videos/dobar-thumb.jpg"
|
|
}
|
|
],
|
|
"totalDuration": 7500
|
|
}
|
|
```
|
|
|
|
### 4.7 Community Routes (`/api/community`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| GET | `/community/comments` | List comments | No |
|
|
| POST | `/community/comments` | Create comment | Yes |
|
|
| DELETE | `/community/comments/:id` | Delete own comment | Yes |
|
|
|
|
### 4.8 Bug Report Routes (`/api/bugs`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| GET | `/bugs` | List bug reports | Yes (Admin) |
|
|
| GET | `/bugs/:id` | Get bug report details | Yes |
|
|
| POST | `/bugs` | Submit bug report | Yes |
|
|
| PUT | `/bugs/:id` | Update bug status (admin) | Yes (Admin) |
|
|
|
|
### 4.9 Upload Routes (`/api/uploads`)
|
|
|
|
| Method | Endpoint | Description | Auth Required |
|
|
|--------|----------|-------------|---------------|
|
|
| POST | `/uploads/video` | Upload video file | Yes (Admin) |
|
|
| POST | `/uploads/icon` | Upload icon/image | Yes (Admin) |
|
|
|
|
### 4.10 Static File Serving
|
|
|
|
| Path | Description |
|
|
|------|-------------|
|
|
| `/uploads/videos/*` | Video files |
|
|
| `/uploads/icons/*` | Icon/image files |
|
|
| `/uploads/documents/*` | Document exports |
|
|
|
|
---
|
|
|
|
## 5. UI/UX Specifications (Exact Replication)
|
|
|
|
### 5.1 Design System (From Screenshots)
|
|
|
|
**Color Palette:**
|
|
- Primary Blue: `#2563eb` (buttons, active states)
|
|
- Sidebar Background: `#1e293b` (dark slate)
|
|
- Card Colors (word difficulty):
|
|
- Green: `#10b981` (A1-A2)
|
|
- Yellow: `#f59e0b` (B1-B2)
|
|
- Orange: `#f97316` (C1-C2)
|
|
- Background: `#f8fafc` (light gray)
|
|
- Text Primary: `#0f172a`
|
|
- Text Secondary: `#64748b`
|
|
- Border: `#e2e8f0`
|
|
|
|
**Typography:**
|
|
- Font Family: Inter, system-ui, sans-serif
|
|
- Headings: 600-700 weight
|
|
- Body: 400-500 weight
|
|
- Sizes: 14px (body), 16px (large), 12px (small)
|
|
|
|
**Spacing:**
|
|
- Base unit: 4px (Tailwind default)
|
|
- Card padding: 16px
|
|
- Section gaps: 24px
|
|
- Grid gap: 16px
|
|
|
|
**Border Radius:**
|
|
- Cards: 8px
|
|
- Buttons: 6px
|
|
- Inputs: 6px
|
|
|
|
### 5.2 Layout Components
|
|
|
|
**Sidebar (Left Navigation):**
|
|
- Width: 240px
|
|
- Fixed position
|
|
- Dark background (#1e293b)
|
|
- White text with hover states
|
|
- Logo at top
|
|
- Navigation items with icons
|
|
- Auth section at bottom
|
|
|
|
**Main Content Area:**
|
|
- Margin-left: 240px (sidebar width)
|
|
- Padding: 24px
|
|
- Max-width: 1400px (for large screens)
|
|
|
|
**Filter Bar (Dictionary Page):**
|
|
- Sticky top position
|
|
- White background with shadow
|
|
- Flex layout: inputs on left, actions on right
|
|
- Input fields: text search, dropdowns for filters
|
|
- Action buttons: Search (blue), Text toggle, Reset
|
|
|
|
**Word Card Grid:**
|
|
- CSS Grid: 4-5 columns on desktop, responsive
|
|
- Gap: 16px
|
|
- Card structure:
|
|
- Icon area (top, colored background)
|
|
- Word label (bottom, white background)
|
|
- Action buttons (overlay on hover)
|
|
|
|
### 5.3 Page-Specific Layouts
|
|
|
|
**Home (Početna):**
|
|
- Hero section with large heading
|
|
- 2x2 grid of feature cards
|
|
- Each card: icon, title, description, link
|
|
|
|
**Dictionary (Riječi):**
|
|
- Filter bar (sticky)
|
|
- Word card grid
|
|
- Pagination at bottom
|
|
- Word detail modal (overlay)
|
|
|
|
**Znakopis (Sentence Builder):**
|
|
- Two-column layout:
|
|
- Left: Token tray (current sentence)
|
|
- Right: Document panel (sentence list, page controls)
|
|
- Drag-and-drop interface for tokens
|
|
- Save/Load buttons in header
|
|
|
|
**Video Rečenica (Video Sentence):**
|
|
- Two-column layout:
|
|
- Left: Large video player (60% width)
|
|
- Right: Sentence panel (40% width)
|
|
- Video controls below player
|
|
- Highlighted current token in sentence list
|
|
|
|
**Oblak (Cloud):**
|
|
- Document list with cards
|
|
- Upload/Create buttons
|
|
- Document metadata display
|
|
- Load/Delete actions per document
|
|
|
|
### 5.4 Component Library (shadcn/ui)
|
|
|
|
Components to install:
|
|
- `button` - All action buttons
|
|
- `input` - Text inputs
|
|
- `select` - Dropdowns
|
|
- `card` - Word cards, document cards
|
|
- `dialog` - Modals (word detail, confirmations)
|
|
- `dropdown-menu` - User menu
|
|
- `tabs` - Znakopis/Oblak tabs
|
|
- `toast` - Notifications
|
|
- `avatar` - User avatar
|
|
- `badge` - CEFR level badges
|
|
- `separator` - Dividers
|
|
- `scroll-area` - Scrollable lists
|
|
|
|
### 5.5 Croatian Text (Exact Labels)
|
|
|
|
**Navigation:**
|
|
- Početna (Home)
|
|
- Riječi (Words/Dictionary)
|
|
- Znakopis (Sentence Builder)
|
|
- Video rečenica (Video Sentence)
|
|
- Oblak (Cloud)
|
|
- Korištenje aplikacije (Help)
|
|
- Zajednica (Community)
|
|
- Komentari (Comments)
|
|
- Prijavi grešku (Bug Report)
|
|
- Admin Panel (Admin only - User Management)
|
|
|
|
**Dictionary Page:**
|
|
- Riječ (Word) - search input label
|
|
- Tip riječi (Word Type) - dropdown label
|
|
- CEFR razina (CEFR Level) - dropdown label
|
|
- Traži (Search) - button
|
|
- Text - toggle button
|
|
- Reset - button
|
|
- Dodaj (Add) - card action
|
|
- Info - card action
|
|
|
|
**Znakopis Page:**
|
|
- Popis rečenica (Sentence List)
|
|
- stranica (page)
|
|
- Učitajte dokument (Load Document)
|
|
- Spremi (Save)
|
|
- Nova rečenica (New Sentence)
|
|
|
|
**Auth:**
|
|
- Prijavi se (Sign In)
|
|
- Odjavi se (Sign Out)
|
|
- Email adresa (Email Address)
|
|
- Lozinka (Password)
|
|
|
|
**Admin Panel:**
|
|
- Korisnici (Users)
|
|
- Dodaj korisnika (Add User)
|
|
- Uredi korisnika (Edit User)
|
|
- Obriši korisnika (Delete User)
|
|
- Resetiraj lozinku (Reset Password)
|
|
- Aktiviraj/Deaktiviraj (Activate/Deactivate)
|
|
- Uloga (Role)
|
|
- Aktivan (Active)
|
|
|
|
---
|
|
|
|
## 6. Implementation Milestones
|
|
|
|
**Note on Authentication Strategy:**
|
|
For the initial phases, we will implement a simple admin user with local user management. OAuth integration (Google, Microsoft) will be deferred to later phases (Phase 8+) to focus on core functionality first. The admin user will be able to create and manage local users through an admin panel.
|
|
|
|
### Phase 0: Project Setup (Week 1)
|
|
**Goal:** Initialize project structure and development environment
|
|
|
|
**Tasks:**
|
|
1. Create monorepo structure with pnpm workspaces
|
|
2. Initialize frontend (Vite + React + TypeScript)
|
|
3. Initialize backend (Express + TypeScript)
|
|
4. Set up Prisma with MySQL
|
|
5. Configure Tailwind CSS and shadcn/ui
|
|
6. Set up ESLint, Prettier, and TypeScript configs
|
|
7. Create `.env.example` files
|
|
8. Set up Git repository and `.gitignore`
|
|
9. Create initial README with setup instructions
|
|
|
|
**Deliverables:**
|
|
- ✅ Project compiles and runs (frontend + backend)
|
|
- ✅ Database connection works
|
|
- ✅ Basic "Hello World" on both ends
|
|
|
|
---
|
|
|
|
### Phase 1: Core Infrastructure (Week 2)
|
|
**Goal:** Build basic authentication and layout with admin user
|
|
|
|
**Backend Tasks:**
|
|
1. Implement Prisma schema (User model with role and isActive fields)
|
|
2. Run initial migration
|
|
3. Create seed script with default admin user
|
|
4. Implement simple session-based authentication
|
|
5. Create auth routes (login, logout, me)
|
|
6. Create auth middleware (isAuthenticated, isAdmin)
|
|
7. Set up CORS and security headers
|
|
8. Create admin user management routes (CRUD)
|
|
|
|
**Frontend Tasks:**
|
|
1. Create layout components (Sidebar, Header, Layout)
|
|
2. Set up React Router with routes
|
|
3. Create login page (simple email/password)
|
|
4. Implement auth store (Zustand)
|
|
5. Create auth API client
|
|
6. Implement protected routes
|
|
7. Create admin panel page for user management
|
|
8. Build user list, create, edit, delete components
|
|
|
|
**Deliverables:**
|
|
- ✅ Admin can login with default credentials
|
|
- ✅ Session persists across page reloads
|
|
- ✅ Sidebar navigation works
|
|
- ✅ Protected routes redirect to login
|
|
- ✅ Admin can create/edit/delete local users
|
|
- ✅ Admin can reset user passwords
|
|
- ✅ Admin can activate/deactivate users
|
|
|
|
---
|
|
|
|
### Phase 2: Dictionary Module (Week 3-4)
|
|
**Goal:** Complete dictionary browsing and search
|
|
|
|
**Backend Tasks:**
|
|
1. Implement Term, TermMedia, TermExample models
|
|
2. Run migrations
|
|
3. Create seed script with sample terms
|
|
4. Implement term routes (list, get, search)
|
|
5. Add filtering logic (wordType, cefrLevel, query)
|
|
6. Add pagination
|
|
7. Implement full-text search
|
|
8. Set up file upload for videos/icons
|
|
9. Create static file serving
|
|
|
|
**Frontend Tasks:**
|
|
1. Create Dictionary page layout
|
|
2. Build FilterBar component
|
|
3. Build WordCard component
|
|
4. Build WordGrid component
|
|
5. Implement term API client
|
|
6. Add search and filter functionality
|
|
7. Create WordDetailModal component
|
|
8. Integrate video player (Plyr)
|
|
9. Add pagination controls
|
|
10. Implement "Text" toggle view
|
|
|
|
**Deliverables:**
|
|
- ✅ Dictionary page displays word cards
|
|
- ✅ Search and filters work
|
|
- ✅ Word detail modal shows video
|
|
- ✅ Pagination works
|
|
- ✅ UI matches screenshots exactly
|
|
|
|
---
|
|
|
|
### Phase 3: Sentence Builder (Znakopis) (Week 5-6)
|
|
**Goal:** Build sentence composition workspace
|
|
|
|
**Backend Tasks:**
|
|
1. Implement Document, DocumentPage, Sentence, SentenceToken models
|
|
2. Run migrations
|
|
3. Create document routes (CRUD)
|
|
4. Create sentence routes (CRUD)
|
|
5. Implement token management (add, remove, reorder)
|
|
6. Add document content update endpoint
|
|
7. Implement document listing for user
|
|
|
|
**Frontend Tasks:**
|
|
1. Create Znakopis page layout
|
|
2. Build sentence store (Zustand)
|
|
3. Implement "Add to sentence" from Dictionary
|
|
4. Create TokenTray component
|
|
5. Implement drag-and-drop reordering (@dnd-kit)
|
|
6. Create SentenceList component
|
|
7. Build DocumentPanel component
|
|
8. Implement save/load document
|
|
9. Add page management (multi-page documents)
|
|
10. Create new sentence functionality
|
|
|
|
**Deliverables:**
|
|
- ✅ Users can add words from dictionary to sentence
|
|
- ✅ Tokens can be reordered via drag-and-drop
|
|
- ✅ Sentences can be saved to documents
|
|
- ✅ Documents can be loaded from cloud
|
|
- ✅ Multi-page documents work
|
|
- ✅ UI matches screenshots exactly
|
|
|
|
---
|
|
|
|
### Phase 4: Video Sentence Player (Week 7)
|
|
**Goal:** Implement video playback of sentences
|
|
|
|
**Backend Tasks:**
|
|
1. Create playlist generation endpoint
|
|
2. Implement playlist resolver (tokens → videos)
|
|
3. Handle missing media gracefully
|
|
4. Add video metadata to responses
|
|
|
|
**Frontend Tasks:**
|
|
1. Create VideoSentence page layout
|
|
2. Build VideoPlayer component with Plyr
|
|
3. Implement playlist logic
|
|
4. Add playback controls (play, pause, next, prev)
|
|
5. Implement token highlighting sync
|
|
6. Add speed control
|
|
7. Add loop functionality
|
|
8. Implement preloading of next video
|
|
9. Create sentence panel (right side)
|
|
|
|
**Deliverables:**
|
|
- ✅ Sentences play as sequential videos
|
|
- ✅ Playback controls work
|
|
- ✅ Current token is highlighted
|
|
- ✅ Videos transition smoothly
|
|
- ✅ UI matches screenshots exactly
|
|
|
|
---
|
|
|
|
### Phase 5: Cloud Documents (Week 8)
|
|
**Goal:** Complete document management
|
|
|
|
**Backend Tasks:**
|
|
1. Implement document visibility settings
|
|
2. Add share link generation
|
|
3. Create document export functionality
|
|
4. Add document search/filtering
|
|
|
|
**Frontend Tasks:**
|
|
1. Create Oblak (Cloud) page
|
|
2. Build DocumentBrowser component
|
|
3. Implement document list with cards
|
|
4. Add upload document functionality
|
|
5. Create document metadata editor
|
|
6. Implement load document into Znakopis
|
|
7. Add delete document with confirmation
|
|
8. Implement document sharing UI
|
|
|
|
**Deliverables:**
|
|
- ✅ Users can view all their documents
|
|
- ✅ Documents can be loaded into editor
|
|
- ✅ Documents can be deleted
|
|
- ✅ Share links work
|
|
- ✅ UI matches screenshots exactly
|
|
|
|
---
|
|
|
|
### Phase 6: Community & Support (Week 9)
|
|
**Goal:** Add community features
|
|
|
|
**Backend Tasks:**
|
|
1. Implement Comment and BugReport models
|
|
2. Run migrations
|
|
3. Create community routes
|
|
4. Create bug report routes
|
|
5. Add admin endpoints for bug management
|
|
|
|
**Frontend Tasks:**
|
|
1. Create Help page (Korištenje aplikacije)
|
|
2. Create Community page (Zajednica)
|
|
3. Create Comments page (Komentari)
|
|
4. Build comment form and list
|
|
5. Create BugReport page (Prijavi grešku)
|
|
6. Build bug report form
|
|
7. Add toast notifications
|
|
|
|
**Deliverables:**
|
|
- ✅ Users can post comments
|
|
- ✅ Users can submit bug reports
|
|
- ✅ Help page has documentation
|
|
- ✅ UI matches screenshots exactly
|
|
|
|
---
|
|
|
|
### Phase 7: Polish & Testing (Week 10)
|
|
**Goal:** Final refinements and quality assurance
|
|
|
|
**Tasks:**
|
|
1. Responsive design testing (mobile, tablet, desktop)
|
|
2. Cross-browser testing (Chrome, Firefox, Safari, Edge)
|
|
3. Accessibility audit (keyboard nav, ARIA labels, screen readers)
|
|
4. Performance optimization (lazy loading, code splitting)
|
|
5. Error handling improvements
|
|
6. Loading states and skeletons
|
|
7. Form validation refinements
|
|
8. Video loading optimization
|
|
9. Database query optimization
|
|
10. Security audit
|
|
11. Write E2E tests (Playwright)
|
|
12. Write unit tests for critical functions
|
|
13. Documentation updates
|
|
14. Deployment preparation
|
|
|
|
**Deliverables:**
|
|
- ✅ App works on all major browsers
|
|
- ✅ Mobile responsive
|
|
- ✅ Accessible (WCAG 2.1 AA)
|
|
- ✅ Fast loading times
|
|
- ✅ No critical bugs
|
|
- ✅ Tests pass
|
|
- ✅ Ready for deployment
|
|
|
|
---
|
|
|
|
### Phase 8: OAuth Integration (Future Phase)
|
|
**Goal:** Add OAuth authentication providers (deferred from Phase 1)
|
|
|
|
**Backend Tasks:**
|
|
1. Set up Passport.js with Google OAuth strategy
|
|
2. Set up Passport.js with Microsoft OAuth strategy
|
|
3. Update User model to support authProvider and providerId fields
|
|
4. Create OAuth callback routes
|
|
5. Implement account linking (OAuth to existing local accounts)
|
|
6. Update auth middleware to support OAuth sessions
|
|
|
|
**Frontend Tasks:**
|
|
1. Add OAuth buttons to login page
|
|
2. Create OAuth callback handling
|
|
3. Add account linking UI
|
|
4. Update user profile to show auth provider
|
|
|
|
**Deliverables:**
|
|
- ✅ Users can sign in with Google
|
|
- ✅ Users can sign in with Microsoft
|
|
- ✅ OAuth accounts can be linked to existing local accounts
|
|
- ✅ Session management works with OAuth
|
|
|
|
---
|
|
|
|
## 7. Environment Configuration
|
|
|
|
### Backend `.env` (packages/backend/.env)
|
|
|
|
```env
|
|
# Server
|
|
NODE_ENV=development
|
|
PORT=3000
|
|
FRONTEND_URL=http://localhost:5173
|
|
|
|
# Database
|
|
DATABASE_URL="mysql://user:password@localhost:3306/znakovni"
|
|
|
|
# Session
|
|
SESSION_SECRET=your-super-secret-session-key-change-in-production
|
|
|
|
# Default Admin User (created on first run)
|
|
ADMIN_EMAIL=admin@znakovni.hr
|
|
ADMIN_PASSWORD=change-this-password-immediately
|
|
|
|
# File Upload
|
|
UPLOAD_DIR=./uploads
|
|
MAX_FILE_SIZE=104857600
|
|
# 100MB in bytes
|
|
|
|
# CORS
|
|
CORS_ORIGIN=http://localhost:5173
|
|
```
|
|
|
|
### Frontend `.env` (packages/frontend/.env)
|
|
|
|
```env
|
|
VITE_API_URL=http://localhost:3000/api
|
|
VITE_UPLOADS_URL=http://localhost:3000/uploads
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Key Features & User Flows
|
|
|
|
### 8.1 Dictionary Search Flow
|
|
1. User navigates to "Riječi" (Dictionary)
|
|
2. User enters search term in "Riječ" input
|
|
3. User optionally selects "Tip riječi" (word type) filter
|
|
4. User optionally selects "CEFR razina" (level) filter
|
|
5. User clicks "Traži" (Search)
|
|
6. Grid updates with filtered results
|
|
7. User clicks "Info" on a card
|
|
8. Modal opens showing:
|
|
- Word details
|
|
- Sign video (auto-plays)
|
|
- Examples
|
|
- Metadata
|
|
9. User can click "Dodaj" to add word to current sentence
|
|
|
|
### 8.2 Sentence Building Flow
|
|
1. User searches for words in Dictionary
|
|
2. User clicks "Dodaj" on multiple word cards
|
|
3. Words are added to sentence store
|
|
4. User navigates to "Znakopis"
|
|
5. Tokens appear in TokenTray
|
|
6. User drags tokens to reorder
|
|
7. User can remove tokens
|
|
8. User clicks "Spremi" (Save)
|
|
9. Document is saved to cloud (if logged in) or local storage
|
|
10. User can create multiple sentences across pages
|
|
|
|
### 8.3 Video Playback Flow
|
|
1. User builds or loads a sentence in Znakopis
|
|
2. User navigates to "Video rečenica"
|
|
3. Sentence tokens appear in right panel
|
|
4. User clicks play button
|
|
5. Videos play sequentially for each token
|
|
6. Current token is highlighted
|
|
7. User can pause, skip, or adjust speed
|
|
8. User can navigate between pages/sentences
|
|
|
|
### 8.4 Cloud Document Flow
|
|
1. User logs in
|
|
2. User creates sentences in Znakopis
|
|
3. User saves document to cloud
|
|
4. User navigates to "Oblak"
|
|
5. Document appears in list
|
|
6. User can:
|
|
- Load document into Znakopis
|
|
- Delete document
|
|
- Share document (get link)
|
|
- View document metadata
|
|
|
|
---
|
|
|
|
## 9. Technical Implementation Details
|
|
|
|
### 9.1 Video Playlist Resolution Algorithm
|
|
|
|
```typescript
|
|
// Pseudocode for playlist generation
|
|
async function generatePlaylist(sentenceId: string) {
|
|
// 1. Fetch sentence with tokens
|
|
const sentence = await prisma.sentence.findUnique({
|
|
where: { id: sentenceId },
|
|
include: {
|
|
tokens: {
|
|
include: { term: { include: { media: true } } },
|
|
orderBy: { tokenIndex: 'asc' }
|
|
}
|
|
}
|
|
});
|
|
|
|
// 2. Map tokens to video URLs
|
|
const playlist = sentence.tokens.map(token => {
|
|
if (!token.term) {
|
|
return null; // Skip punctuation or free-text
|
|
}
|
|
|
|
// Find primary video media
|
|
const video = token.term.media.find(m => m.kind === 'VIDEO');
|
|
|
|
if (!video) {
|
|
console.warn(`No video for term ${token.term.id}`);
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
tokenId: token.id,
|
|
termId: token.term.id,
|
|
displayText: token.displayText,
|
|
videoUrl: video.url,
|
|
durationMs: video.durationMs,
|
|
thumbnailUrl: video.url.replace('.mp4', '-thumb.jpg')
|
|
};
|
|
}).filter(Boolean);
|
|
|
|
return {
|
|
playlist,
|
|
totalDuration: playlist.reduce((sum, item) => sum + item.durationMs, 0)
|
|
};
|
|
}
|
|
```
|
|
|
|
### 9.2 Full-Text Search Implementation
|
|
|
|
```typescript
|
|
// MySQL full-text search query
|
|
async function searchTerms(query: string, filters: Filters) {
|
|
const where = {
|
|
AND: [
|
|
query ? {
|
|
OR: [
|
|
{ wordText: { contains: query } },
|
|
{ normalizedText: { contains: query } }
|
|
]
|
|
} : {},
|
|
filters.wordType ? { wordType: filters.wordType } : {},
|
|
filters.cefrLevel ? { cefrLevel: filters.cefrLevel } : {}
|
|
]
|
|
};
|
|
|
|
return await prisma.term.findMany({
|
|
where,
|
|
include: { media: true },
|
|
orderBy: { wordText: 'asc' },
|
|
skip: (filters.page - 1) * filters.limit,
|
|
take: filters.limit
|
|
});
|
|
}
|
|
```
|
|
|
|
### 9.3 Token Reordering Logic
|
|
|
|
```typescript
|
|
// Frontend: Drag-and-drop reorder
|
|
function handleDragEnd(event: DragEndEvent) {
|
|
const { active, over } = event;
|
|
|
|
if (active.id !== over.id) {
|
|
const oldIndex = tokens.findIndex(t => t.id === active.id);
|
|
const newIndex = tokens.findIndex(t => t.id === over.id);
|
|
|
|
const reordered = arrayMove(tokens, oldIndex, newIndex);
|
|
|
|
// Update local state
|
|
setSentenceTokens(reordered);
|
|
|
|
// Sync to backend
|
|
await api.put(`/sentences/${sentenceId}/reorder`, {
|
|
tokenIds: reordered.map(t => t.id)
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Deployment Guide (PoC)
|
|
|
|
### 10.1 Prerequisites
|
|
- Ubuntu 20.04+ or similar Linux server
|
|
- Node.js 20 LTS installed
|
|
- MySQL 8.0+ installed and running
|
|
- pnpm installed globally
|
|
- nginx installed (optional, for reverse proxy)
|
|
|
|
### 10.2 Deployment Steps
|
|
|
|
**1. Clone and Install**
|
|
```bash
|
|
git clone <your-repo-url>
|
|
cd turkshop
|
|
pnpm install
|
|
```
|
|
|
|
**2. Configure Environment**
|
|
```bash
|
|
# Backend
|
|
cd packages/backend
|
|
cp .env.example .env
|
|
# Edit .env with production values
|
|
nano .env
|
|
|
|
# Frontend
|
|
cd ../frontend
|
|
cp .env.example .env
|
|
# Edit .env with production API URL
|
|
nano .env
|
|
```
|
|
|
|
**3. Database Setup**
|
|
```bash
|
|
cd packages/backend
|
|
npx prisma migrate deploy
|
|
npx prisma db seed
|
|
```
|
|
|
|
**4. Build Applications**
|
|
```bash
|
|
# From root
|
|
pnpm build
|
|
```
|
|
|
|
**5. Start with PM2**
|
|
```bash
|
|
# Install PM2
|
|
npm install -g pm2
|
|
|
|
# Start backend
|
|
cd packages/backend
|
|
pm2 start dist/server.js --name znakovni-api
|
|
|
|
# Serve frontend (with nginx or serve)
|
|
cd ../frontend
|
|
pm2 serve dist 5173 --name znakovni-frontend
|
|
```
|
|
|
|
**6. Configure nginx (Optional)**
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name yourdomain.com;
|
|
|
|
# Frontend
|
|
location / {
|
|
root /path/to/turkshop/packages/frontend/dist;
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
|
|
# Backend API
|
|
location /api {
|
|
proxy_pass http://localhost:3000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection 'upgrade';
|
|
proxy_set_header Host $host;
|
|
proxy_cache_bypass $http_upgrade;
|
|
}
|
|
|
|
# Uploads
|
|
location /uploads {
|
|
proxy_pass http://localhost:3000;
|
|
}
|
|
}
|
|
```
|
|
|
|
**7. Set up SSL (Production)**
|
|
```bash
|
|
sudo apt install certbot python3-certbot-nginx
|
|
sudo certbot --nginx -d yourdomain.com
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Development Workflow
|
|
|
|
### 11.1 Initial Setup
|
|
|
|
```bash
|
|
# Clone repo
|
|
git clone <repo-url>
|
|
cd turkshop
|
|
|
|
# Install dependencies
|
|
pnpm install
|
|
|
|
# Set up environment files
|
|
cp packages/backend/.env.example packages/backend/.env
|
|
cp packages/frontend/.env.example packages/frontend/.env
|
|
|
|
# Edit .env files with your local MySQL credentials and OAuth keys
|
|
|
|
# Run database migrations
|
|
cd packages/backend
|
|
npx prisma migrate dev
|
|
npx prisma db seed
|
|
|
|
# Return to root
|
|
cd ../..
|
|
```
|
|
|
|
### 11.2 Running Development Servers
|
|
|
|
```bash
|
|
# From root directory
|
|
pnpm dev
|
|
|
|
# This runs both frontend (port 5173) and backend (port 3000) concurrently
|
|
```
|
|
|
|
### 11.3 Database Management
|
|
|
|
```bash
|
|
# Create new migration
|
|
cd packages/backend
|
|
npx prisma migrate dev --name description_of_change
|
|
|
|
# Reset database (WARNING: deletes all data)
|
|
npx prisma migrate reset
|
|
|
|
# Open Prisma Studio (GUI for database)
|
|
npx prisma studio
|
|
|
|
# Generate Prisma Client after schema changes
|
|
npx prisma generate
|
|
```
|
|
|
|
### 11.4 Adding shadcn/ui Components
|
|
|
|
```bash
|
|
cd packages/frontend
|
|
npx shadcn-ui@latest add button
|
|
npx shadcn-ui@latest add input
|
|
npx shadcn-ui@latest add card
|
|
# etc.
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Success Criteria
|
|
|
|
### Functional Requirements ✅
|
|
- [ ] Users can browse and search dictionary with filters
|
|
- [ ] Word detail modal displays video and metadata
|
|
- [ ] Users can build sentences by adding words
|
|
- [ ] Tokens can be reordered via drag-and-drop
|
|
- [ ] Sentences can be saved to documents
|
|
- [ ] Documents can be loaded from cloud
|
|
- [ ] Video sentence player works with sequential playback
|
|
- [ ] Admin can login with credentials
|
|
- [ ] Admin can create/edit/delete local users
|
|
- [ ] Admin can reset user passwords
|
|
- [ ] Admin can activate/deactivate users
|
|
- [ ] Regular users can login with their credentials
|
|
- [ ] Cloud document management works
|
|
- [ ] Comments and bug reports can be submitted
|
|
|
|
### Visual Requirements ✅
|
|
- [ ] UI matches screenshots pixel-perfect
|
|
- [ ] Croatian text labels are exact
|
|
- [ ] Color scheme matches original
|
|
- [ ] Typography matches original
|
|
- [ ] Layout and spacing match original
|
|
- [ ] Icons and graphics match original
|
|
|
|
### Performance Requirements ✅
|
|
- [ ] Dictionary search returns results < 500ms
|
|
- [ ] Video playback starts < 1s
|
|
- [ ] Page load time < 2s
|
|
- [ ] Smooth animations (60fps)
|
|
|
|
### Quality Requirements ✅
|
|
- [ ] TypeScript strict mode with no errors
|
|
- [ ] All forms have validation
|
|
- [ ] Error handling on all API calls
|
|
- [ ] Loading states for async operations
|
|
- [ ] Responsive design (desktop, tablet, mobile)
|
|
- [ ] Accessible (keyboard navigation, ARIA labels)
|
|
|
|
---
|
|
|
|
## 13. Next Steps
|
|
|
|
1. **Review and approve this plan**
|
|
2. **Prepare MySQL database** (local or cloud)
|
|
3. **Begin Phase 0: Project Setup**
|
|
4. **Iterate through phases sequentially**
|
|
5. **Test continuously during development**
|
|
6. **Deploy to production server**
|
|
7. **Phase 8+ (Future): Set up OAuth credentials** (Google Cloud Console, Microsoft Azure) when ready to add OAuth
|
|
|
|
---
|
|
|
|
## 14. Resources & References
|
|
|
|
### Documentation
|
|
- React: https://react.dev
|
|
- Vite: https://vitejs.dev
|
|
- TypeScript: https://www.typescriptlang.org
|
|
- Tailwind CSS: https://tailwindcss.com
|
|
- shadcn/ui: https://ui.shadcn.com
|
|
- Prisma: https://www.prisma.io
|
|
- Express: https://expressjs.com
|
|
- Passport.js: https://www.passportjs.org
|
|
- Plyr: https://plyr.io
|
|
- @dnd-kit: https://dndkit.com
|
|
|
|
### Original Specifications
|
|
- `/usr/src/znakovni/original/project1.md` - Detailed functional spec
|
|
- `/usr/src/znakovni/original/project2.md` - Replication requirements
|
|
- `/usr/src/znakovni/original/*.png` - UI screenshots
|
|
|
|
---
|
|
|
|
**END OF PLAN**
|
|
|