Compare commits
2 Commits
34f44a1781
...
31c156d157
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31c156d157 | ||
|
|
b63362bbaa |
78
README.md
78
README.md
@@ -67,65 +67,12 @@ Die Anwendung berechnet automatisch die Pausenzeiten gemäß deutschem Arbeitsze
|
|||||||
- `DELETE /api/entries/:id` - Eintrag löschen
|
- `DELETE /api/entries/:id` - Eintrag löschen
|
||||||
- `GET /api/export?from=YYYY-MM-DD&to=YYYY-MM-DD` - Einträge als CSV exportieren
|
- `GET /api/export?from=YYYY-MM-DD&to=YYYY-MM-DD` - Einträge als CSV exportieren
|
||||||
|
|
||||||
## Installation & Ausführung
|
## Lokale Ausführung
|
||||||
|
|
||||||
### Repository klonen
|
### Voraussetzungen
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://gitea.fx-se.de/maggot/timetracker.git
|
|
||||||
cd timetracker
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 1: Mit Docker Compose (Empfohlen)
|
|
||||||
|
|
||||||
**Voraussetzungen:**
|
|
||||||
- Docker und Docker Compose installiert
|
|
||||||
|
|
||||||
**Starten:**
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
**Logs ansehen:**
|
|
||||||
```bash
|
|
||||||
docker-compose logs -f
|
|
||||||
```
|
|
||||||
|
|
||||||
**Stoppen:**
|
|
||||||
```bash
|
|
||||||
docker-compose down
|
|
||||||
```
|
|
||||||
|
|
||||||
**Stoppen und Daten löschen:**
|
|
||||||
```bash
|
|
||||||
docker-compose down -v
|
|
||||||
```
|
|
||||||
|
|
||||||
Die Anwendung läuft auf:
|
|
||||||
```
|
|
||||||
http://localhost:3000
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Mit Docker (manuell)
|
|
||||||
|
|
||||||
**Docker-Image erstellen:**
|
|
||||||
```bash
|
|
||||||
docker build -t zeiterfassung .
|
|
||||||
```
|
|
||||||
|
|
||||||
**Container starten:**
|
|
||||||
```bash
|
|
||||||
docker run -p 3000:3000 -v $(pwd)/db:/app/db zeiterfassung
|
|
||||||
```
|
|
||||||
|
|
||||||
Das `-v` Flag bindet das Datenbankverzeichnis ein, um Daten zwischen Container-Neustarts zu erhalten.
|
|
||||||
|
|
||||||
### Option 3: Lokale Ausführung (ohne Docker)
|
|
||||||
|
|
||||||
**Voraussetzungen:**
|
|
||||||
- Node.js 18+ installiert
|
- Node.js 18+ installiert
|
||||||
|
|
||||||
**Installation:**
|
### Installation
|
||||||
|
|
||||||
1. Abhängigkeiten installieren:
|
1. Abhängigkeiten installieren:
|
||||||
```bash
|
```bash
|
||||||
@@ -142,6 +89,25 @@ npm start
|
|||||||
http://localhost:3000
|
http://localhost:3000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Ausführung mit Docker
|
||||||
|
|
||||||
|
### Docker-Image erstellen:
|
||||||
|
```bash
|
||||||
|
docker build -t zeiterfassung .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container starten:
|
||||||
|
```bash
|
||||||
|
docker run -p 3000:3000 -v $(pwd)/db:/app/db zeiterfassung
|
||||||
|
```
|
||||||
|
|
||||||
|
Das `-v` Flag bindet das Datenbankverzeichnis ein, um Daten zwischen Container-Neustarts zu erhalten.
|
||||||
|
|
||||||
|
### Anwendung aufrufen:
|
||||||
|
```
|
||||||
|
http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
## CSV-Export-Format
|
## CSV-Export-Format
|
||||||
|
|
||||||
Die exportierte CSV-Datei enthält folgende Spalten:
|
Die exportierte CSV-Datei enthält folgende Spalten:
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
timetracker:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: timetracker-app
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
volumes:
|
|
||||||
# Persistent volume for SQLite database
|
|
||||||
- timetracker-data:/app/db
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=production
|
|
||||||
- PORT=3000
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 3
|
|
||||||
start_period: 5s
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
timetracker-data:
|
|
||||||
driver: local
|
|
||||||
@@ -1462,9 +1462,7 @@ async function bulkSetLocation(location) {
|
|||||||
for (const id of selectedEntries) {
|
for (const id of selectedEntries) {
|
||||||
const entry = entries.find(e => e.id === id);
|
const entry = entries.find(e => e.id === id);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
// Convert date from YYYY-MM-DD to DD.MM.YYYY for updateEntry
|
const success = await updateEntry(id, entry.date, entry.startTime, entry.endTime, entry.pauseMinutes, location);
|
||||||
const formattedDate = formatDateDisplay(entry.date);
|
|
||||||
const success = await updateEntry(id, formattedDate, entry.startTime, entry.endTime, entry.pauseMinutes, location);
|
|
||||||
if (success) {
|
if (success) {
|
||||||
updated++;
|
updated++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,20 +308,20 @@
|
|||||||
<!-- Entries Table -->
|
<!-- Entries Table -->
|
||||||
<div class="bg-gray-800 rounded-lg shadow-md overflow-hidden border border-gray-700">
|
<div class="bg-gray-800 rounded-lg shadow-md overflow-hidden border border-gray-700">
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="w-full">
|
<table class="w-full table-fixed">
|
||||||
<thead class="bg-gray-700 border-b border-gray-600">
|
<thead class="bg-gray-700 border-b border-gray-600">
|
||||||
<tr>
|
<tr>
|
||||||
<th id="checkboxHeader" class="hidden px-2 py-3 text-center text-xs font-medium text-gray-400 uppercase tracking-wider">
|
<th id="checkboxHeader" class="hidden w-12 px-2 py-3 text-center text-xs font-medium text-gray-400 uppercase tracking-wider">
|
||||||
<input type="checkbox" id="masterCheckbox" class="w-5 h-5 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500" title="Alle auswählen/abwählen">
|
<input type="checkbox" id="masterCheckbox" class="w-5 h-5 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500" title="Alle auswählen/abwählen">
|
||||||
</th>
|
</th>
|
||||||
<th class="px-2 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Tag</th>
|
<th class="w-12 px-2 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Tag</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Datum</th>
|
<th class="w-28 px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Datum</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Start</th>
|
<th class="w-20 px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Start</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Ende</th>
|
<th class="w-20 px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Ende</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Pause (Min)</th>
|
<th class="w-28 px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Pause (Min)</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Netto (Std)</th>
|
<th class="w-28 px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Netto (Std)</th>
|
||||||
<th class="px-6 py-3 text-center text-xs font-medium text-gray-400 uppercase tracking-wider">Ort</th>
|
<th class="w-24 px-6 py-3 text-center text-xs font-medium text-gray-400 uppercase tracking-wider">Ort</th>
|
||||||
<th class="px-6 py-3 text-center text-xs font-medium text-gray-400 uppercase tracking-wider">Action</th>
|
<th class="w-24 px-6 py-3 text-center text-xs font-medium text-gray-400 uppercase tracking-wider">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="entriesTableBody" class="bg-gray-800 divide-y divide-gray-700">
|
<tbody id="entriesTableBody" class="bg-gray-800 divide-y divide-gray-700">
|
||||||
|
|||||||
Reference in New Issue
Block a user