Files
znakovni.hr/main-plan.md
johnny2211 c6d6c18466 Add dictionary feature with term management and UI components
- Implement backend API for term CRUD operations
- Add frontend dictionary page with search and filtering
- Integrate shadcn/ui components (Dialog)
- Create term management UI with add/edit/delete functionality
- Update database seed with initial terms
- Add API client for term operations
- Complete Phase 2 of development plan
2026-01-17 18:15:01 +01:00

50 KiB
Raw Permalink Blame History

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.


🎯 CORE APPLICATION FUNCTIONALITY (Quick Reference)

The application is a Croatian Sign Language learning tool with THREE main interconnected screens:

1 RIJEČI (Dictionary) - Browse & Add Words

  • What it shows: Grid of pre-recorded sign language words with icons
  • What you do:
    • Click "Dodaj" (Add) to add words to your sentence
    • Click "Info" to watch the sign video for any word
  • Screenshot reference: 1 dodaj rijeci.png

2 ZNAKOPIS (Sentence Builder) - Order & Save

  • What it shows: All words you added, displayed as draggable tokens
  • What you do:
    • Drag and drop words to arrange them in correct sentence order
    • Create multiple sentences across multiple pages
    • Click "Spremi" (Save) to save document to cloud (stored under your user account)
  • Screenshot reference: 2 dodaj rijeci znakopis.png

3 VIDEO REČENICA (Video Player) - Watch & Learn

  • What it shows: Video player + sentence list
  • What you do:
    • Click Play to watch all sign videos play in sequence
    • Current word being signed is highlighted in the sentence list
    • Control playback (pause, speed, navigate)
  • Screenshot reference: 3 dodaj rijeci recenica.png

🔄 Complete User Journey:

RIJEČI (add words) → ZNAKOPIS (order & save) → VIDEO REČENICA (play & watch)
                                    ↓
                            OBLAK (cloud storage)
                    (documents saved under user account)

Key Technical Points:

  • Words are pre-recorded in the database with associated sign videos
  • Sentence state persists across all three screens (global state management)
  • Documents are saved to database and associated with logged-in user
  • Video playback is synchronized with word highlighting
  • All Croatian text labels must match exactly

📊 DATA FLOW & STATE MANAGEMENT

How the Three Screens Work Together

1. Pre-recorded Words (Database)

Terms Table:
- id: uuid
- wordText: "dobar" (good)
- wordType: ADJECTIVE
- cefrLevel: A1
- iconAssetId: "path/to/icon.png"

TermMedia Table:
- termId: (links to Term)
- kind: VIDEO
- url: "/uploads/videos/dobar.mp4"
- durationMs: 2500

2. User Adds Words (Frontend State → Database)

User clicks "Dodaj" in Riječi
    ↓
Word added to sentenceStore (Zustand)
    ↓
User navigates to Znakopis
    ↓
sentenceStore displays tokens
    ↓
User reorders tokens via drag-and-drop
    ↓
User clicks "Spremi" (Save)
    ↓
API call: POST /api/documents
    ↓
Database creates:
  - Document (title, ownerUserId)
  - DocumentPage (documentId, pageIndex)
  - Sentence (documentPageId, sentenceIndex)
  - SentenceToken[] (sentenceId, termId, tokenIndex, displayText)

3. User Plays Video (Database → Frontend)

User navigates to Video Rečenica
    ↓
Loads document from Oblak or current sentenceStore
    ↓
API call: GET /api/playlists/generate?sentenceId=xxx
    ↓
Backend resolves:
  Sentence → SentenceTokens → Terms → TermMedia (videos)
    ↓
Returns playlist:
  [
    { tokenId, termId, displayText, videoUrl, durationMs },
    { tokenId, termId, displayText, videoUrl, durationMs },
    ...
  ]
    ↓
Frontend plays videos sequentially
    ↓
Highlights current token in sentence list

State Management Strategy

Frontend State (Zustand stores):

  • sentenceStore: Current sentence being built (tokens, order)

    • Used by: Riječi (add), Znakopis (edit), Video Rečenica (play)
    • Persists across navigation
    • Cleared when document is saved or loaded
  • documentStore: Current loaded document

    • Used by: Znakopis (edit), Oblak (list), Video Rečenica (play)
    • Contains: documentId, title, pages[], sentences[]
  • authStore: Current user session

    • Used by: All screens
    • Contains: userId, email, role, isAuthenticated

Backend Persistence:

  • All saved documents stored in MySQL via Prisma
  • Documents linked to User via ownerUserId
  • Full hierarchy: Document → DocumentPage → Sentence → SentenceToken → Term

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/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:

{
  "sentenceId": "uuid",
  "tokenIds": ["uuid1", "uuid2", "uuid3"]
}

Response:

{
  "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)

# 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)

VITE_API_URL=http://localhost:3000/api
VITE_UPLOADS_URL=http://localhost:3000/uploads

8. Key Features & User Flows

CORE FUNCTIONALITY OVERVIEW (Based on Screenshots 1, 2, 3)

The application has THREE main interconnected screens that form the primary workflow:

Screen 1: Riječi (Dictionary) - "1 dodaj rijeci.png"

Purpose: Browse pre-recorded sign language words and add them to your sentence.

Key Elements:

  • Grid of word cards with icons/illustrations
  • Each card has two buttons:
    • "Dodaj" (Add) - Adds the word to the current sentence being built
    • "Info" - Opens a modal/detail view showing the sign video for that word
  • Filter bar at top with search and CEFR level filters
  • Words are color-coded by difficulty level (green/yellow/orange)

User Actions:

  1. Browse or search for words
  2. Click "Dodaj" to add words to the sentence (words accumulate at the top of the page)
  3. Click "Info" to watch the sign video demonstration for any word
  4. Continue adding multiple words to build a complete sentence

Screen 2: Znakopis (Sentence Builder) - "2 dodaj rijeci znakopis.png"

Purpose: Organize the words you've added into a proper sentence order and save as a document.

Key Elements:

  • Top area: Shows the current sentence with all added words as tokens/chips
  • Right panel: "Popis rečenica" (Sentence List) showing:
    • All sentences in the current document
    • Page navigation (e.g., "1 / 2 stranica")
    • Document management controls
  • Main workspace: Area to reorder words via drag-and-drop
  • Action buttons:
    • "Spremi" (Save) - Saves the document to cloud storage under your user account
    • "Učitajte dokument" (Load Document) - Loads a previously saved document
    • "Nova rečenica" (New Sentence) - Creates a new sentence on a new page

User Actions:

  1. View all words added from the Dictionary screen
  2. Drag and drop words to reorder them into the correct sentence structure
  3. Remove unwanted words
  4. Create multiple sentences across multiple pages
  5. Save the complete document to the cloud (stored under user's account)
  6. Load previously saved documents for editing

Screen 3: Video Rečenica (Video Sentence Player) - "3 dodaj rijeci recenica.png"

Purpose: Play all the sign videos for your sentence in sequence, with visual highlighting.

Key Elements:

  • Left side (60%): Large video player showing the current word's sign video
  • Right side (40%): Sentence panel showing:
    • Complete sentence with all words listed
    • Current word being played is highlighted/marked
    • Sentence navigation controls
  • Playback controls:
    • Play/Pause
    • Next/Previous word
    • Speed control
    • Loop options

User Actions:

  1. Load a sentence from Znakopis or Cloud
  2. Click play to start sequential video playback
  3. Watch as each word's sign video plays in order
  4. See visual highlighting on the current word being signed
  5. Control playback (pause, skip, adjust speed)
  6. Navigate between different sentences/pages in the document

8.1 Complete User Workflow (End-to-End)

Step 1: Build a Sentence (Riječi → Znakopis)

  1. User navigates to "Riječi" (Dictionary)
  2. User searches/browses for words they want to use
  3. User clicks "Dodaj" on each word card to add it to their sentence
    • Words accumulate in a sentence builder area (visible at top)
  4. User can click "Info" on any word to preview its sign video
  5. After adding all desired words, user navigates to "Znakopis"
  6. In Znakopis, user sees all added words as draggable tokens
  7. User drags and drops words to arrange them in correct order
  8. User can remove unwanted words
  9. User can create additional sentences on new pages
  10. User clicks "Spremi" (Save) to save the document to cloud
    • Document is stored under the user's account in the portal

Step 2: Review and Play (Znakopis → Video Rečenica)

  1. User navigates to "Video rečenica" (Video Sentence)
  2. User loads their saved sentence/document
  3. Sentence appears in the right panel with all words listed
  4. User clicks Play
  5. Video player shows each word's sign video in sequence
  6. Current word is highlighted in the sentence list
  7. User can control playback, adjust speed, or navigate between sentences

Step 3: Manage Documents (Oblak)

  1. User navigates to "Oblak" (Cloud)
  2. User sees all their saved documents
  3. User can:
    • Load a document back into Znakopis for editing
    • Delete documents
    • Share documents (generate link)
    • View document metadata (creation date, page count, etc.)

8.2 Critical Implementation Requirements

Sentence State Management:

  • Sentence state must persist across all three screens (Riječi → Znakopis → Video Rečenica)
  • When user adds words in Riječi, they must appear in Znakopis
  • When user saves in Znakopis, document must be available in Video Rečenica and Oblak
  • Use Zustand store to maintain sentence/document state globally

Video Synchronization:

  • In Video Rečenica, video playback must be synchronized with word highlighting
  • When video for word N finishes, automatically start video for word N+1
  • Highlight must move to the current word being played
  • Smooth transitions between videos (preload next video)

Document Structure:

  • Documents can have multiple pages
  • Each page can have multiple sentences
  • Each sentence is a sequence of word tokens
  • Tokens maintain reference to original Term (for video lookup)
  • Token order is preserved and editable

Cloud Storage:

  • All saved documents are associated with the logged-in user
  • Documents persist in database (Document → DocumentPage → Sentence → SentenceToken)
  • Users can only see/edit their own documents (unless shared)
  • Documents can be loaded back into Znakopis for editing

9. Technical Implementation Details

9.1 Video Playlist Resolution Algorithm

// 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

// 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

// 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

git clone <your-repo-url>
cd turkshop
pnpm install

2. Configure Environment

# 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

cd packages/backend
npx prisma migrate deploy
npx prisma db seed

4. Build Applications

# From root
pnpm build

5. Start with PM2

# 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)

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)

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com

11. Development Workflow

11.1 Initial Setup

# 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

# From root directory
pnpm dev

# This runs both frontend (port 5173) and backend (port 3000) concurrently

11.3 Database Management

# 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

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

Core Functionality Requirements (CRITICAL)

Screen 1: Riječi (Dictionary)

  • Dictionary displays grid of word cards with icons
  • Each card has "Dodaj" (Add) and "Info" buttons
  • Clicking "Dodaj" adds word to current sentence (visible at top of page)
  • Clicking "Info" opens modal with sign video that auto-plays
  • Search and filter controls work (word type, CEFR level)
  • Words are color-coded by difficulty level

Screen 2: Znakopis (Sentence Builder)

  • All words added from Dictionary appear as draggable tokens
  • Tokens can be reordered via drag-and-drop
  • Tokens can be removed
  • Right panel shows "Popis rečenica" (Sentence List)
  • Users can create multiple sentences across multiple pages
  • "Spremi" (Save) button saves document to cloud under user's account
  • "Učitajte dokument" (Load Document) loads previously saved documents
  • Page navigation works (e.g., "1 / 2 stranica")

Screen 3: Video Rečenica (Video Player)

  • Left side shows large video player (60% width)
  • Right side shows sentence panel with all words (40% width)
  • Clicking Play starts sequential video playback
  • Videos play in order, one after another
  • Current word being signed is highlighted in the sentence list
  • Highlighting moves automatically as videos progress
  • Playback controls work (play, pause, next, previous)
  • Speed control and loop options work
  • Videos transition smoothly (preload next video)

Cross-Screen Integration

  • Sentence state persists from Riječi → Znakopis → Video Rečenica
  • Documents saved in Znakopis appear in Oblak (Cloud)
  • Documents loaded from Oblak can be edited in Znakopis
  • Documents loaded from Oblak can be played in Video Rečenica

Authentication & User Management

  • 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
  • Documents are associated with logged-in user
  • Users can only see their own documents (unless shared)

Cloud Document Management (Oblak)

  • Users can view all their saved documents
  • Documents show metadata (title, creation date, page count)
  • Users can load documents into Znakopis
  • Users can delete documents
  • Users can share documents (generate link)

Additional Features

  • Comments and bug reports can be submitted
  • Help page displays usage documentation
  • Community features work

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

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