diff --git a/DOCKER_GIT_DEPLOYMENT.md b/DOCKER_GIT_DEPLOYMENT.md
new file mode 100644
index 0000000..b7e1ac6
--- /dev/null
+++ b/DOCKER_GIT_DEPLOYMENT.md
@@ -0,0 +1,165 @@
+# Docker Git Deployment Guide
+
+This project now supports deploying directly from a Git repository using Docker. This is useful for automated deployments and CI/CD pipelines.
+
+## File Structure
+
+- `Dockerfile` - Production dockerfile that supports Git deployment
+- `Dockerfile.dev` - Development dockerfile
+- `docker-compose.yml` - Development environment
+- `docker-compose.prod.yml` - Production environment with Git support
+- `deploy.sh` / `deploy.bat` - Deployment scripts
+
+## Deployment Options
+
+### 1. Deploy from Local Files (Default)
+
+```bash
+# Development
+docker-compose up
+
+# Production
+docker-compose -f docker-compose.prod.yml up --build
+```
+
+### 2. Deploy from Git Repository
+
+#### Using Environment Variables
+
+Create a `.env` file with:
+```env
+GIT_REPO_URL=https://github.com/yourusername/your-repo.git
+GIT_BRANCH=main
+GIT_COMMIT=abc123 # Optional: specific commit hash
+```
+
+Then run:
+```bash
+docker-compose -f docker-compose.prod.yml up --build
+```
+
+#### Using Build Arguments
+
+```bash
+docker build \
+ --build-arg GIT_REPO_URL=https://github.com/yourusername/your-repo.git \
+ --build-arg GIT_BRANCH=main \
+ --build-arg GIT_COMMIT=abc123 \
+ -t your-app .
+```
+
+#### Using Deployment Scripts
+
+```bash
+# Linux/Mac
+./deploy.sh https://github.com/yourusername/your-repo.git main abc123
+
+# Windows
+deploy.bat https://github.com/yourusername/your-repo.git main abc123
+```
+
+## Private Repositories
+
+For private repositories, you have several options:
+
+### 1. SSH Keys (Recommended for development)
+```bash
+# Build with SSH URL
+docker build --build-arg GIT_REPO_URL=git@github.com:yourusername/your-repo.git .
+```
+
+### 2. Personal Access Token
+```bash
+# Build with token in URL
+docker build --build-arg GIT_REPO_URL=https://token@github.com/yourusername/your-repo.git .
+```
+
+### 3. Docker Secrets (Recommended for production)
+```yaml
+# In docker-compose.prod.yml
+services:
+ app:
+ build:
+ context: .
+ args:
+ - GIT_REPO_URL=https://github.com/yourusername/your-repo.git
+ secrets:
+ - git_token
+secrets:
+ git_token:
+ file: ./git_token.txt
+```
+
+## CI/CD Integration
+
+### GitHub Actions Example
+
+```yaml
+name: Deploy
+on:
+ push:
+ branches: [main]
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Deploy to server
+ run: |
+ docker build \
+ --build-arg GIT_REPO_URL=${{ github.repository }} \
+ --build-arg GIT_COMMIT=${{ github.sha }} \
+ -t my-app .
+ docker run -d -p 3000:3000 my-app
+```
+
+### Docker Compose in CI/CD
+
+```bash
+# Set environment variables in your CI/CD system
+export GIT_REPO_URL="https://github.com/yourusername/your-repo.git"
+export GIT_BRANCH="main"
+export GIT_COMMIT="$CI_COMMIT_SHA"
+
+# Deploy
+docker-compose -f docker-compose.prod.yml up --build -d
+```
+
+## Build Process
+
+When `GIT_REPO_URL` is provided:
+1. Git repository is cloned into the container
+2. If `GIT_COMMIT` is specified, checkout that specific commit
+3. Install dependencies from the repository's package.json
+4. Build the application
+5. Start the production server
+
+When `GIT_REPO_URL` is not provided:
+1. Copy local files into the container
+2. Install dependencies
+3. Build the application
+4. Start the production server
+
+## Environment Variables
+
+- `GIT_REPO_URL` - Git repository URL (HTTPS or SSH)
+- `GIT_BRANCH` - Git branch to checkout (default: main)
+- `GIT_COMMIT` - Specific commit hash to checkout (optional)
+- `NODE_ENV` - Node.js environment (development/production)
+
+## Troubleshooting
+
+### Git Authentication Issues
+- Ensure your Git credentials are properly configured
+- For HTTPS, use personal access tokens instead of passwords
+- For SSH, ensure SSH keys are properly mounted or available
+
+### Build Failures
+- Check if the repository URL is accessible
+- Verify the branch name exists
+- Ensure the commit hash is valid
+- Check Docker build logs for specific errors
+
+### Permission Issues
+- Ensure the Docker daemon has network access
+- For private repositories, verify authentication tokens/keys
diff --git a/Dockerfile b/Dockerfile
index 9b1bb84..ade93f6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,20 +1,39 @@
# Use Node.js 22.11.0 as the base image
FROM node:22.11.0
+# Install git
+RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
+
# Set the working directory
WORKDIR /app
-# Copy package.json and package-lock.json (if any)
+# If building from a git repository, clone it
+# This will be used when the build context doesn't include source files
+ARG GIT_REPO_URL
+ARG GIT_BRANCH=main
+ARG GIT_COMMIT
+
+# If GIT_REPO_URL is provided, clone the repo; otherwise copy local files
+RUN if [ -n "$GIT_REPO_URL" ]; then \
+ git clone --branch ${GIT_BRANCH} ${GIT_REPO_URL} . && \
+ if [ -n "$GIT_COMMIT" ]; then git checkout ${GIT_COMMIT}; fi; \
+ fi
+
+# Copy package.json and package-lock.json (if not cloned from git)
COPY package*.json ./
# Install dependencies
RUN npm install
-# Copy the rest of the app
+# Copy the rest of the app (if not cloned from git)
+RUN if [ -z "$GIT_REPO_URL" ]; then echo "Copying local files..."; fi
COPY . .
+# Build the application for production
+RUN npm run build
+
# Expose the default Next.js port
EXPOSE 3000
-# Start the dev server
-CMD ["npm", "run", "dev"]
+# Start the production server
+CMD ["npm", "start"]
diff --git a/Dockerfile.dev b/Dockerfile.dev
new file mode 100644
index 0000000..92abef7
--- /dev/null
+++ b/Dockerfile.dev
@@ -0,0 +1,23 @@
+# Use Node.js 22.11.0 as the base image
+FROM node:22.11.0
+
+# Install git for development
+RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
+
+# Set the working directory
+WORKDIR /app
+
+# Copy package.json and package-lock.json (if any)
+COPY package*.json ./
+
+# Install dependencies
+RUN npm install
+
+# Copy the rest of the app
+COPY . .
+
+# Expose the default Next.js port
+EXPOSE 3000
+
+# Start the dev server
+CMD ["npm", "run", "dev"]
diff --git a/deploy.bat b/deploy.bat
new file mode 100644
index 0000000..b54d44b
--- /dev/null
+++ b/deploy.bat
@@ -0,0 +1,29 @@
+@echo off
+REM Production deployment script for Windows
+REM Usage: deploy.bat [git_repo_url] [branch] [commit_hash]
+
+set GIT_REPO_URL=%1
+set GIT_BRANCH=%2
+if "%GIT_BRANCH%"=="" set GIT_BRANCH=main
+set GIT_COMMIT=%3
+
+if "%GIT_REPO_URL%"=="" (
+ echo Building from local files...
+ docker-compose -f docker-compose.prod.yml build
+) else (
+ echo Building from git repository: %GIT_REPO_URL%
+ echo Branch: %GIT_BRANCH%
+ if not "%GIT_COMMIT%"=="" echo Commit: %GIT_COMMIT%
+
+ set GIT_REPO_URL=%GIT_REPO_URL%
+ set GIT_BRANCH=%GIT_BRANCH%
+ set GIT_COMMIT=%GIT_COMMIT%
+ docker-compose -f docker-compose.prod.yml build
+)
+
+echo Starting production deployment...
+docker-compose -f docker-compose.prod.yml down
+docker-compose -f docker-compose.prod.yml up -d
+
+echo Deployment completed successfully!
+echo Application is running at http://localhost:3000
diff --git a/deploy.sh b/deploy.sh
new file mode 100644
index 0000000..92521c1
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Production deployment script
+# Usage: ./deploy.sh [git_repo_url] [branch] [commit_hash]
+
+set -e
+
+# Default values
+GIT_REPO_URL=${1:-""}
+GIT_BRANCH=${2:-"main"}
+GIT_COMMIT=${3:-""}
+
+if [ -z "$GIT_REPO_URL" ]; then
+ echo "Building from local files..."
+ docker-compose -f docker-compose.prod.yml build
+else
+ echo "Building from git repository: $GIT_REPO_URL"
+ echo "Branch: $GIT_BRANCH"
+ if [ -n "$GIT_COMMIT" ]; then
+ echo "Commit: $GIT_COMMIT"
+ fi
+
+ GIT_REPO_URL=$GIT_REPO_URL GIT_BRANCH=$GIT_BRANCH GIT_COMMIT=$GIT_COMMIT \
+ docker-compose -f docker-compose.prod.yml build
+fi
+
+echo "Starting production deployment..."
+docker-compose -f docker-compose.prod.yml down
+docker-compose -f docker-compose.prod.yml up -d
+
+echo "Deployment completed successfully!"
+echo "Application is running at http://localhost:3000"
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
new file mode 100644
index 0000000..b7d3e3d
--- /dev/null
+++ b/docker-compose.prod.yml
@@ -0,0 +1,18 @@
+version: "3.9"
+
+services:
+ app:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ args:
+ - GIT_REPO_URL=${GIT_REPO_URL}
+ - GIT_BRANCH=${GIT_BRANCH:-main}
+ - GIT_COMMIT=${GIT_COMMIT}
+ ports:
+ - "3000:3000"
+ volumes:
+ - ./data:/app/data
+ environment:
+ - NODE_ENV=production
+ restart: unless-stopped
diff --git a/docker-compose.yml b/docker-compose.yml
index 395da32..771a657 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,7 +2,9 @@ version: "3.9"
services:
app:
- build: .
+ build:
+ context: .
+ dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
diff --git a/src/app/page.js b/src/app/page.js
index 72f7be6..e69de29 100644
--- a/src/app/page.js
+++ b/src/app/page.js
@@ -1,1085 +0,0 @@
-"use client";
-
-import { useEffect, useState } from "react";
-import { useSession } from "next-auth/react";
-import Link from "next/link";
-import { Card, CardHeader, CardContent } from "@/components/ui/Card";
-import Button from "@/components/ui/Button";
-import Badge from "@/components/ui/Badge";
-import TaskStatusDropdownSimple from "@/components/TaskStatusDropdownSimple";
-import PageContainer from "@/components/ui/PageContainer";
-import PageHeader from "@/components/ui/PageHeader";
-import { LoadingState } from "@/components/ui/States";
-import { useTranslation } from "@/lib/i18n";
-import {
- differenceInCalendarDays,
- parseISO,
- formatDistanceToNow,
- isAfter,
- isBefore,
- addDays,
- startOfWeek,
- endOfWeek,
- format,
-} from "date-fns";
-import { formatDate } from "@/lib/utils";
-import TaskStatusChart from "@/components/ui/TaskStatusChart";
-
-export default function Home() {
- const { data: session, status } = useSession();
- const { t } = useTranslation();
- const [stats, setStats] = useState({
- totalProjects: 0,
- activeProjects: 0,
- completedProjects: 0,
- overdueProjects: 0,
- totalContracts: 0,
- activeContracts: 0,
- pendingTasks: 0,
- inProgressTasks: 0,
- completedTasks: 0,
- overdueTasks: 0,
- projectsThisWeek: 0,
- tasksThisWeek: 0,
- completionRate: 0,
- });
- const [recentProjects, setRecentProjects] = useState([]);
- const [recentTasks, setRecentTasks] = useState([]);
- const [upcomingDeadlines, setUpcomingDeadlines] = useState([]);
- const [contracts, setContracts] = useState([]);
- const [tasksByStatus, setTasksByStatus] = useState([]);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- // Only fetch data if user is authenticated
- if (!session) {
- setLoading(false);
- return;
- }
-
- const fetchDashboardData = async () => {
- try {
- // Fetch all data concurrently
- const [projectsRes, tasksRes, contractsRes] = await Promise.all([
- fetch("/api/projects"),
- fetch("/api/all-project-tasks"),
- fetch("/api/contracts"),
- ]);
-
- const projects = await projectsRes.json();
- const tasks = await tasksRes.json();
- const contractsData = await contractsRes.json();
-
- const now = new Date();
-
- // Calculate project statistics
- const activeProjects = projects.filter(
- (p) => p.finish_date && isAfter(parseISO(p.finish_date), now)
- );
- const completedProjects = projects.filter(
- (p) => p.project_status === "fulfilled"
- );
- const overdueProjects = projects.filter(
- (p) =>
- p.finish_date &&
- isBefore(parseISO(p.finish_date), now) &&
- p.project_status !== "fulfilled"
- );
-
- // Calculate contract statistics
- const activeContracts = contractsData.filter(
- (c) => !c.finish_date || isAfter(parseISO(c.finish_date), now)
- );
-
- // Calculate task statistics
- const pendingTasks = tasks.filter((t) => t.status === "pending");
- const inProgressTasks = tasks.filter((t) => t.status === "in_progress");
- const completedTasks = tasks.filter((t) => t.status === "completed");
-
- // Calculate overdue tasks
- const overdueTasks = tasks.filter((t) => {
- if (t.status === "completed" || t.status === "cancelled")
- return false;
- try {
- const addedDate = t.date_added.includes("T")
- ? parseISO(t.date_added)
- : new Date(t.date_added + "T00:00:00");
- const daysElapsed = differenceInCalendarDays(now, addedDate);
- const maxWaitDays = t.max_wait_days || 0;
- return daysElapsed > maxWaitDays;
- } catch {
- return false;
- }
- });
-
- // Get upcoming deadlines (next 14 days)
- const deadlines = [
- ...projects
- .filter((p) => p.finish_date)
- .map((p) => ({
- type: "project",
- id: p.project_id,
- name: p.project_name,
- date: p.finish_date,
- status: p.project_status,
- city: p.city,
- })),
- ...contractsData
- .filter((c) => c.finish_date)
- .map((c) => ({
- type: "contract",
- id: c.contract_id,
- name: c.contract_name,
- date: c.finish_date,
- customer: c.customer,
- })),
- ]
- .filter((item) => {
- const itemDate = parseISO(item.date);
- return (
- isAfter(itemDate, now) && isBefore(itemDate, addDays(now, 14))
- );
- })
- .sort((a, b) => new Date(a.date) - new Date(b.date))
- .slice(0, 5);
-
- // Calculate weekly statistics
- const weekStart = startOfWeek(now);
- const weekEnd = endOfWeek(now);
-
- const projectsThisWeek = projects.filter((p) => {
- if (!p.date_created) return false;
- const projectDate = parseISO(p.date_created);
- return projectDate >= weekStart && projectDate <= weekEnd;
- }).length;
-
- const tasksThisWeek = tasks.filter((t) => {
- if (!t.date_added) return false;
- const taskDate = t.date_added.includes("T")
- ? parseISO(t.date_added)
- : new Date(t.date_added + "T00:00:00");
- return taskDate >= weekStart && taskDate <= weekEnd;
- }).length;
-
- const totalTasks = tasks.length;
- const completionRate =
- totalTasks > 0
- ? Math.round((completedTasks.length / totalTasks) * 100)
- : 0;
-
- // Group tasks by status for charts
- const tasksByStatusData = [
- {
- status: "pending",
- count: pendingTasks.length,
- color: "bg-yellow-500",
- },
- {
- status: "in_progress",
- count: inProgressTasks.length,
- color: "bg-blue-500",
- },
- {
- status: "completed",
- count: completedTasks.length,
- color: "bg-green-500",
- },
- {
- status: "overdue",
- count: overdueTasks.length,
- color: "bg-red-500",
- },
- ];
-
- setStats({
- totalProjects: projects.length,
- activeProjects: activeProjects.length,
- completedProjects: completedProjects.length,
- overdueProjects: overdueProjects.length,
- totalContracts: contractsData.length,
- activeContracts: activeContracts.length,
- pendingTasks: pendingTasks.length,
- inProgressTasks: inProgressTasks.length,
- completedTasks: completedTasks.length,
- overdueTasks: overdueTasks.length,
- projectsThisWeek,
- tasksThisWeek,
- completionRate,
- });
-
- setRecentProjects(projects.slice(0, 5));
- setRecentTasks(tasks.slice(0, 8));
- setUpcomingDeadlines(deadlines);
- setContracts(contractsData);
- setTasksByStatus(tasksByStatusData);
- } catch (error) {
- console.error("Failed to fetch dashboard data:", error);
- } finally {
- setLoading(false);
- }
- };
-
- fetchDashboardData();
- }, [session]);
-
- const getProjectStatusColor = (status) => {
- switch (status) {
- case "fulfilled":
- return "text-green-600";
- case "in_progress_design":
- return "text-blue-600";
- case "in_progress_construction":
- return "text-orange-600";
- case "registered":
- return "text-gray-600";
- default:
- return "text-gray-600";
- }
- };
-
- const getTaskPriorityVariant = (priority) => {
- switch (priority) {
- case "high":
- return "danger";
- case "normal":
- return "secondary";
- case "low":
- return "success";
- default:
- return "secondary";
- }
- };
-
- const getDeadlineVariant = (days) => {
- if (days <= 2) return "danger";
- if (days <= 7) return "warning";
- return "primary";
- };
-
- if (loading) {
- return (
-
- Zaloguj się, aby uzyskać dostęp do systemu zarządzania projektami.
-
- {t('dashboard.totalProjects')}
-
- {stats.totalProjects}
-
- {stats.activeProjects} {t('dashboard.activeProjects').toLowerCase()}
-
- {t('dashboard.totalContracts')}
-
- {stats.totalContracts}
-
- {stats.activeContracts} {t('dashboard.activeContracts').toLowerCase()}
-
- {t('dashboard.pendingTasks')}
-
- {stats.pendingTasks}
- Oczekują rozpoczęcia
- {t('dashboard.inProgressTasks')}
-
- {stats.inProgressTasks}
- Aktywne zadania
- {t('dashboard.completedTasks')}
-
- {stats.completedTasks}
- Zadania wykonane
- Overdue
- 0 ? "text-red-600" : "text-gray-900"
- }`}
- >
- {stats.overdueTasks}
-
- {stats.overdueTasks > 0 ? "Need attention!" : "All on track"}
-
- New Projects
- Added this week
- New Tasks
- Created this week No upcoming deadlines
- All projects on track!
-
- {deadline.type === "project"
- ? deadline.city
- : deadline.customer}{" "}
- • {formatDate(deadline.date)}
- No projects yet.
- {project.city}
-
- Due: {formatDate(project.finish_date)}
- No tasks yet.
- {(() => {
- try {
- if (!task.date_added) return "No date";
- const taskDate = task.date_added.includes("T")
- ? parseISO(task.date_added)
- : new Date(task.date_added + "T00:00:00");
-
- if (isNaN(taskDate.getTime()))
- return "Invalid date";
-
- return formatDistanceToNow(taskDate, {
- addSuffix: true,
- });
- } catch (error) {
- return "Invalid date";
- }
- })()}
- Start a new project Add a new contract Create reusable template Monitor all tasks
- Witamy w Panelu Zarządzania Projektami
-
-
- Task Distribution
-
- This Week
-
- Quick Overview
-
-
- Upcoming Deadlines
-
-
- Recent Projects
-
-
-
-
-
- Recent Tasks
-
-
-
-
-
- {task.task_name}
-
-
- New Project
-
-
- New Contract
-
-
- Task Template
-
-
- Task Dashboard
-
-
- Coordinates: {coords.lat.toFixed(6)}, {coords.lng.toFixed(6)} + {coords.lat.toFixed(6)}, {coords.lng.toFixed(6)}