diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..40b2698
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+Dockerfile
+.dockerignore
+node_modules
+npm-debug.log
+README.md
+.env
+.env.local
+.env.production.local
+.env.development.local
+.git
+.gitignore
+.next
+.vscode
+.idea
+*.swp
+*.swo
+.DS_Store
+coverage
+.nyc_output
+*.log
+dist
+build
+.cache
+.turbo
+
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..9d9a292
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,80 @@
+# Multi-stage Dockerfile for Next.js with Prisma
+FROM node:20-alpine AS base
+
+# Install dependencies only when needed
+FROM base AS deps
+# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+# Install dependencies based on the preferred package manager
+COPY package.json package-lock.json* ./
+RUN \
+ if [ -f package-lock.json ]; then npm ci; \
+ else echo "Lockfile not found." && exit 1; \
+ fi
+
+# Rebuild the source code only when needed
+FROM base AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+
+# Set a dummy DATABASE_URL for build time (Prisma needs it to generate client)
+ENV DATABASE_URL="file:/tmp/build.db"
+
+# Generate Prisma client
+RUN npx prisma generate
+
+# Initialize the database schema for build time
+RUN npx prisma migrate deploy || npx prisma db push
+
+# Build the application
+RUN npm run build
+
+# Production image, copy all the files and run next
+FROM base AS runner
+
+# Set timezone to Europe/Paris
+RUN apk add --no-cache tzdata
+RUN ln -snf /usr/share/zoneinfo/Europe/Paris /etc/localtime && echo Europe/Paris > /etc/timezone
+
+WORKDIR /app
+
+ENV NODE_ENV=production
+# Uncomment the following line in case you want to disable telemetry during runtime.
+# ENV NEXT_TELEMETRY_DISABLED 1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+# Copy the public folder
+COPY --from=builder /app/public ./public
+
+# Set the correct permission for prerender cache
+RUN mkdir .next
+RUN chown nextjs:nodejs .next
+
+# Automatically leverage output traces to reduce image size
+# https://nextjs.org/docs/advanced-features/output-file-tracing
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+# Copy Prisma files
+COPY --from=builder /app/prisma ./prisma
+COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
+
+# Create data directory for SQLite
+RUN mkdir -p /app/data && chown nextjs:nodejs /app/data
+
+# Set all ENV vars before switching user
+ENV PORT=3000
+ENV HOSTNAME="0.0.0.0"
+ENV TZ=Europe/Paris
+
+USER nextjs
+
+EXPOSE 3000
+
+# Start the application with database migration
+CMD ["sh", "-c", "npx prisma migrate deploy && node server.js"]
diff --git a/TODO.md b/TODO.md
index 5e2afef..982a52e 100644
--- a/TODO.md
+++ b/TODO.md
@@ -109,8 +109,6 @@
- [x] Auto-création du daily du jour si inexistant
- [x] UX améliorée : édition au clic, focus persistant, input large
- [x] Vue calendar/historique des dailies
-- [ ] Templates de daily personnalisables
-- [ ] Recherche dans l'historique des dailies
### 3.2 Intégration Jira Cloud
- [ ] Créer `services/jira.ts` - Service de connexion à l'API Jira Cloud
diff --git a/components/daily/DailyCalendar.tsx b/components/daily/DailyCalendar.tsx
index 3cbb78d..7d7ac07 100644
--- a/components/daily/DailyCalendar.tsx
+++ b/components/daily/DailyCalendar.tsx
@@ -1,6 +1,6 @@
'use client';
-import React, { useState, useEffect } from 'react';
+import React, { useState } from 'react';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
@@ -10,7 +10,11 @@ interface DailyCalendarProps {
dailyDates: string[]; // Liste des dates qui ont des dailies (format YYYY-MM-DD)
}
-export function DailyCalendar({ currentDate, onDateSelect, dailyDates }: DailyCalendarProps) {
+export function DailyCalendar({
+ currentDate,
+ onDateSelect,
+ dailyDates,
+}: DailyCalendarProps) {
const [viewDate, setViewDate] = useState(new Date(currentDate));
// Formatage des dates pour comparaison (éviter le décalage timezone)
@@ -46,31 +50,32 @@ export function DailyCalendar({ currentDate, onDateSelect, dailyDates }: DailyCa
const getDaysInMonth = () => {
const year = viewDate.getFullYear();
const month = viewDate.getMonth();
-
+
// Premier jour du mois
const firstDay = new Date(year, month, 1);
// Dernier jour du mois
const lastDay = new Date(year, month + 1, 0);
-
+
// Premier lundi de la semaine contenant le premier jour
const startDate = new Date(firstDay);
const dayOfWeek = firstDay.getDay();
const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Lundi = 0
startDate.setDate(firstDay.getDate() - daysToSubtract);
-
+
// Générer toutes les dates du calendrier (6 semaines)
const days = [];
const currentDay = new Date(startDate);
-
- for (let i = 0; i < 42; i++) { // 6 semaines × 7 jours
+
+ for (let i = 0; i < 42; i++) {
+ // 6 semaines × 7 jours
days.push(new Date(currentDay));
currentDay.setDate(currentDay.getDate() + 1);
}
-
+
return { days, firstDay, lastDay };
};
- const { days, firstDay, lastDay } = getDaysInMonth();
+ const { days } = getDaysInMonth();
const handleDateClick = (date: Date) => {
onDateSelect(date);
@@ -96,7 +101,7 @@ export function DailyCalendar({ currentDate, onDateSelect, dailyDates }: DailyCa
const formatMonthYear = () => {
return viewDate.toLocaleDateString('fr-FR', {
month: 'long',
- year: 'numeric'
+ year: 'numeric',
});
};
@@ -114,11 +119,11 @@ export function DailyCalendar({ currentDate, onDateSelect, dailyDates }: DailyCa
>
←
-
+
{formatMonthYear()}
-
+