timesheet-studio Svelte Themes

Timesheet Studio

Georgian-format monthly timesheet generator — interactive calendar, DOCX template filling, desktop app via Tauri

Timesheet Studio

A SvelteKit web application that automates generating Georgian-format monthly timesheets. It computes worked days, vacation days, and public holidays, fills an official DOCX template via XML manipulation, and returns the completed document — ready for submission.

Available as a web app (browser-based) and a desktop app (macOS, Windows, Linux) via Tauri.

Built for organizations operating under Georgian labor regulations that require standardized monthly timesheet forms.

Table of Contents

Features

  • Interactive Calendar UI — Visual month calendar with click-to-toggle vacation selection. Weekends and public holidays are automatically locked.
  • Georgian Holiday Integration — Fetches official Georgian public holidays from two sources (date.nager.at + yell.ge) with automatic fallback and 6-hour caching.
  • DOCX Template Engine — Fills the official timesheet template using XPath-based XML DOM manipulation, preserving the original document formatting.
  • Dual Output Formats — Export as modern .docx or legacy .doc (via LibreOffice CLI conversion).
  • Smart Day Codes — Automatically assigns Georgian-standard codes: 8 (worked), (paid vacation), X (holiday/weekend).
  • Payroll-Ready Metrics — Calculates first-half/second-half hour splits, total worked hours, vacation hours, and weekday holiday counts.
  • Profile Persistence — Employee name, company code, and ID saved to localStorage across sessions.
  • Input Validation — Prevents invalid vacation selections (weekends, holidays) with detailed error messages.
  • Responsive Design — Fully responsive from desktop down to mobile with breakpoints at 1140px, 1024px, 760px, 640px, and 430px.
  • Accessible — Full ARIA labels, semantic HTML, keyboard navigation for all controls.

Quick Start

Prerequisites

  • Node.js v24 LTS (pinned in .nvmrc)
  • LibreOffice (required for template preparation and .doc export)

One-Command Launch

git clone https://github.com/gati3478/timesheet-studio.git
cd timesheet-studio
npm start

npm start handles everything automatically:

  1. Installs dependencies (if node_modules/ missing)
  2. Prepares the DOCX template from .doc source (if not already built)
  3. Checks port availability
  4. Starts the dev server
  5. Opens your browser to http://localhost:5173

Override host/port with environment variables:

HOST=0.0.0.0 PORT=3000 npm start

Manual Setup

If you prefer step-by-step control:

# 1. Install dependencies
npm install

# 2. Convert the source .doc template to .docx (requires LibreOffice)
npm run prepare:template

# 3. Start the dev server
npm run dev

Environment Check

Verify your environment is ready:

npm run doctor
  Timesheet Studio — Environment Check
  ─────────────────────────────────────

  ✓ Node.js 24.x.x
  ✓ npm 11.x.x
  ✓ Dependencies installed
  ✓ DOCX template ready
  ✓ Source template (.doc) present
  ✓ LibreOffice available (DOC export + template prep supported)
  ✓ Port 5173 available

  ─────────────────────────────────────
  Results: 7 passed, 0 warnings, 0 failed

Development

All Commands

Command Description
npm start Full launcher: install, prepare, start, open browser
npm run dev Start Vite dev server on port 5173
npm run dev:open Start dev server and open browser automatically
npm run build Production build
npm run start:prod Run production build (node build)
npm run preview Preview production build
npm run check Type-check with svelte-check
npm run lint Run Prettier + ESLint
npm run format Auto-format all files with Prettier
npm test Run all tests (alias for test:unit)
npm run test:unit Run Vitest unit & integration tests
npm run test:coverage Run unit tests with coverage report
npm run test:e2e Run Playwright end-to-end tests
npm run test:all Run unit + e2e tests sequentially
npm run prepare:template Convert .doc.docx template
npm run doctor Environment health check
npm run clean Remove build artifacts
npm run tauri:build Build desktop app (macOS/Windows/Linux)
npm run tauri:dev Launch Tauri dev window (needs npm run dev first)
npm run bundle:sidecar Bundle SvelteKit server + Node.js for Tauri sidecar

Code Quality

The project uses Prettier for formatting and ESLint for linting with TypeScript and Svelte support.

# Check formatting and lint rules
npm run lint

# Auto-format everything
npm run format

CI runs lint, type checking, and tests on every push and pull request.

Deployment

Production Build

npm run build

The build output goes to build/. SvelteKit uses adapter-node for server-side deployment.

Supported Platforms

Platform Adapter Notes
Node.js adapter-node Default — self-hosted, run node build
Vercel adapter-vercel Swap adapter in svelte.config.js
Netlify adapter-netlify Swap adapter in svelte.config.js
Cloudflare adapter-cloudflare Swap adapter in svelte.config.js

Note: The .doc export requires LibreOffice on the server. DOCX export works everywhere. Serverless/edge adapters (Vercel, Netlify, Cloudflare Workers) need compatibility review — DOC export uses child_process and holiday fetching uses Node.js-specific streaming APIs not available in edge runtimes.

Docker

# DOCX-only (lightweight)
docker compose up app

# With .doc export support (includes LibreOffice)
docker compose --profile full up app-full

The app is available at http://localhost:3000. To use a different host port: PORT=8080 docker compose up app (app then at http://localhost:8080).

For custom builds:

docker build -t timesheet-studio .
docker run -p 3000:3000 timesheet-studio

Preview Locally

Test the production build before deploying:

npm run build
npm run preview

Desktop App

Timesheet Studio is also available as a standalone desktop application powered by Tauri v2. No Node.js or browser required — just download and run.

Download

Pre-built binaries for macOS, Windows, and Linux are available on the Releases page.

Platform Format Size
macOS (Apple Silicon) .dmg ~45 MB
Windows .msi ~40 MB
Linux (Fedora/RHEL) .rpm ~50 MB
Linux (universal) .AppImage ~125 MB

The .AppImage is larger because it bundles a portable runtime and all shared libraries so it runs on any distro. The .rpm defers those to system packages.

Build from Source

Prerequisites: Node.js v24 LTS, Rust toolchain, platform-specific dependencies (Tauri prerequisites).

# One command builds everything: SvelteKit → sidecar bundle → Tauri app
npm run tauri:build

The output is in src-tauri/target/release/bundle/ — a .dmg on macOS, .msi on Windows, or .rpm/.AppImage on Linux.

How It Works

The desktop app uses Tauri's sidecar architecture: Tauri provides a lightweight native window using the OS webview (WebKit on macOS, WebView2 on Windows), while the SvelteKit server runs as a bundled Node.js process inside the app. The webview connects to the server via localhost on a random port. On exit, Tauri gracefully shuts down the server.

┌─────────────────────────────────────┐
│  Tauri App (.app / .exe / .AppImage)│
│                                     │
│  ┌───────────────────────────────┐  │
│  │ Native Webview (OS WebKit)    │  │
│  │ → http://127.0.0.1:<port>    │  │
│  └──────────────┬────────────────┘  │
│                 │                    │
│  ┌──────────────▼────────────────┐  │
│  │ Node.js Sidecar               │  │
│  │ (SvelteKit server, bundled    │  │
│  │  with esbuild into ~3 MB)     │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘

Desktop Development

For working on the desktop app itself:

# Terminal 1: Start the SvelteKit dev server
npm run dev

# Terminal 2: Launch Tauri with hot-reload
npm run tauri:dev

How It Works

┌─────────────────────────────────────────────────────────┐
│                    Browser (Svelte 5)                    │
│                                                         │
│  ┌──────────────┐  ┌────────────────────────────────┐   │
│  │ Control Panel │  │      Interactive Calendar      │   │
│  │              │  │                                │   │
│  │ • Year/Month │  │  Mon Tue Wed Thu Fri Sat Sun   │   │
│  │ • Profile    │  │   1   2   3   4   5   6   7    │   │
│  │ • Format     │  │  [8] [8] [8] [8] [8] [X] [X]  │   │
│  │ • Generate   │  │   8   9  10  11  12  13  14    │   │
│  └──────────────┘  │  [8] [შ] [8] [X] [8] [X] [X]  │   │
│                    └────────────────────────────────┘   │
│                                                         │
│  ┌─────────┐ ┌─────────┐ ┌──────────┐ ┌────────────┐   │
│  │ Worked  │ │Vacation │ │ Holidays │ │Month Split │   │
│  │ 20 days │ │ 1 day   │ │ 2 days   │ │ 88 / 72    │   │
│  └─────────┘ └─────────┘ └──────────┘ └────────────┘   │
└──────────────────────────┬──────────────────────────────┘
                           │ POST /api/timesheet/generate
                           ▼
┌─────────────────────────────────────────────────────────┐
│                  SvelteKit Server                        │
│                                                         │
│  1. Validate request (dates, profile fields)             │
│  2. Fetch holidays (cached, dual-source fallback)        │
│  3. Compute day codes for each day of the month          │
│  4. Load DOCX template → extract XML via JSZip           │
│  5. Fill cells using XPath DOM manipulation              │
│  6. Set Sylfaen font for Georgian text rendering         │
│  7. Apply gray shading to holiday/weekend cells          │
│  8. Repack ZIP → return binary download                  │
│  9. (Optional) Convert to .doc via LibreOffice CLI       │
└─────────────────────────────────────────────────────────┘

Day Code System

Code Meaning Hours Cell Style
8 Worked day 8h Default
Paid vacation (Georgian letter) 8h Blue highlight
X Holiday or weekend 0h Gray shading
(empty) Day beyond month end (e.g., day 30 in Feb)

Template Filling Pipeline

The DOCX template is a ZIP archive containing XML documents. The filling process:

  1. Extract — JSZip unpacks the .docx file
  2. Parse@xmldom/xmldom parses word/document.xml into a DOM tree
  3. Locate — XPath queries find specific table cells by position
  4. Fill — Cell text content is set to day codes, dates, names, and totals
  5. Style — Sylfaen font applied for Georgian text; gray shading for locked cells
  6. Repack — Modified XML is serialized back and the ZIP is rebuilt
  7. Convert — (Optional) LibreOffice CLI converts .docx.doc for legacy format

API Reference

GET /api/holidays?year={yyyy}

Returns Georgian public holidays for a given year. Results are cached for 6 hours. Fetches from date.nager.at (primary) with yell.ge as fallback.

Response:

{
  "year": 2026,
  "entries": [
    { "date": "2026-01-01", "title": "New Year's Day", "isStateOnly": false },
    { "date": "2026-01-07", "title": "Christmas Day", "isStateOnly": false }
  ]
}

GET /api/capabilities

Returns runtime capabilities of the server (e.g., whether LibreOffice is installed for .doc export). The frontend uses this to show or hide format options.

Response:

{
  "docExportAvailable": true
}

POST /api/timesheet/generate

Generates a filled timesheet document from the template.

Request:

{
  "year": 2026,
  "month": 1,
  "companyCode": "123456789",
  "employeeName": "ნინო ბერიძე, პროგრამისტი",
  "employeeId": "12345678901",
  "vacationDates": ["2026-01-15"],
  "outputFormat": "docx"
}

Response: Binary file download with Content-Disposition header.

Field Type Validation
year number Required
month number 1–12
companyCode string 6–12 digits
employeeName string Non-empty, must contain text
employeeId string Exactly 11 digits
vacationDates string[] ISO dates, must be weekdays and non-holidays
outputFormat "docx" | "doc" doc requires LibreOffice

POST /api/system/shutdown

Gracefully shuts down the local server via SIGTERM (used by the npm start launcher). Only available in development mode — returns 404 in production builds. Restricted to localhost clients (127.0.0.1 / ::1).

Project Structure

src/
├── app.html                              # SvelteKit HTML entry point
├── app.css                               # Global styles & design tokens
├── app.d.ts                              # Ambient TypeScript declarations
├── hooks.server.ts                       # Security headers hook
├── routes/
│   ├── +page.svelte                      # Page orchestration & state management
│   ├── +page.server.ts                   # Server-side load (capabilities detection)
│   ├── +layout.svelte                    # Root layout
│   └── api/
│       ├── capabilities/+server.ts       # Runtime capability detection
│       ├── holidays/+server.ts           # Holiday fetching endpoint
│       ├── timesheet/generate/+server.ts # Document generation endpoint
│       └── system/shutdown/+server.ts    # Local server shutdown (dev only)
└── lib/
    ├── constants.ts                      # Shared constants (months, weekdays)
    ├── calendar-types.ts                 # DayItem & CalendarCell types
    ├── content-disposition.ts            # Content-Disposition header parsing
    ├── filename.ts                       # Output filename generation
    ├── profile.ts                        # Profile persistence (localStorage)
    ├── slugify.ts                        # Unicode-safe string slugification
    ├── tauri.ts                          # Tauri webview detection
    ├── components/
    │   ├── MonthPicker.svelte            # Year/month navigation controls
    │   ├── ProfileEditor.svelte          # Employee profile form
    │   ├── StatusMessage.svelte          # Error/success/info banners
    │   ├── SummaryMetrics.svelte         # Worked/vacation/holiday counters
    │   ├── TitleBar.svelte               # Custom window title bar (Tauri)
    │   └── VacationCalendar.svelte       # Interactive vacation day picker
    └── server/
        ├── types.ts                      # Server-side TypeScript types
        ├── capabilities.ts               # LibreOffice / DOC export detection
        ├── parse-payload.ts              # Request validation & length limits
        ├── timesheet.ts                  # Day-code computation logic
        ├── docx.ts                       # DOCX XML template filling
        ├── doc-conversion.ts             # DOCX → DOC via LibreOffice
        ├── holidays.ts                   # Holiday fetching & caching
        ├── georgian-holidays.json        # Static holiday fallback data
        └── template.ts                   # Template buffer loader

scripts/
├── start.mjs                            # Unified launcher (npm start)
├── prepare-template.mjs                 # .doc → .docx template conversion
├── bundle-sidecar.mjs                   # Tauri sidecar bundler (esbuild + Node download)
├── doctor.mjs                           # Environment health check
├── clean.mjs                            # Build artifact cleanup
├── capture-ui-docs.mjs                  # Screenshot capture for documentation
└── take-screenshot.mjs                  # README hero screenshot capture

src-tauri/                               # Tauri desktop app (Rust)
├── src/
│   ├── main.rs                          # Entry point
│   └── lib.rs                           # Sidecar lifecycle (port, spawn, health, cleanup)
├── build.rs                             # Tauri build script
├── tauri.conf.json                      # App config (CSP, resources, sidecar)
├── capabilities/default.json            # Permission scope (shell:allow-spawn only)
├── Cargo.toml                           # Rust dependencies
├── icons/                               # Generated app icons (all platforms)
├── binaries/                            # Node.js sidecar binary (gitignored)
└── resources/                           # Bundled server + assets (gitignored)

static/
├── favicon.svg                           # App icon
└── templates/
    └── timesheet_template.docx           # Compiled DOCX template

tests/
├── helpers/
│   ├── docx-assertions.ts               # DOCX content assertion utilities
│   ├── fixtures.ts                       # Shared test fixtures
│   └── mock-kit-json.ts                  # SvelteKit json() mock for unit tests
├── unit/                                 # Unit tests (one per module)
│   ├── timesheet.test.ts                 # Day code computation
│   ├── holidays.test.ts                  # Holiday parsing & caching
│   ├── holidays-endpoint.test.ts         # Holiday API endpoint
│   ├── filename.test.ts                  # Filename generation
│   ├── parse-payload.test.ts             # Input validation
│   ├── doc-conversion.test.ts            # DOC conversion
│   ├── template.test.ts                  # Template loader
│   ├── template-env.test.ts              # TEMPLATE_DIR env var
│   ├── capabilities.test.ts              # LibreOffice detection
│   ├── capabilities-endpoint.test.ts     # Capabilities API endpoint
│   ├── hooks.test.ts                     # Security headers
│   ├── server-endpoint.test.ts           # Generate endpoint handler
│   ├── shutdown.test.ts                  # Shutdown endpoint
│   ├── page-server-load.test.ts          # Page server load function
│   ├── calendar-types.test.ts            # Calendar type helpers
│   ├── content-disposition.test.ts       # Content-Disposition parsing
│   ├── profile.test.ts                   # Profile persistence
│   └── docx-errors.test.ts               # DOCX error handling
├── integration/
│   ├── docx-fill.test.ts                 # Template filling
│   └── real-template.test.ts             # Full template scenarios
└── e2e/
    ├── helpers.ts                        # Shared e2e test utilities
    ├── app.spec.ts                       # App UI smoke tests
    ├── calendar.spec.ts                  # Calendar interaction
    ├── api.spec.ts                       # API endpoint tests
    ├── api-validation.spec.ts            # API validation
    ├── security.spec.ts                  # Security headers
    ├── accessibility.spec.ts             # WCAG 2.1 AA compliance
    ├── docx-download.spec.ts             # DOCX download flow
    ├── doc-export-ui.spec.ts             # DOC export UI behavior
    ├── doc-format.spec.ts                # DOC format conversion
    ├── profile-persistence.spec.ts       # Profile localStorage
    └── vacation-validation.spec.ts       # Vacation date validation

Tech Stack

Layer Technology
Framework SvelteKit 2 + Svelte 5
Language TypeScript
Build Vite 8
Linting ESLint + Prettier
Document Processing JSZip · @xmldom/xmldom · xpath
Date Handling date-fns
HTML Parsing Cheerio (holiday fallback source)
Testing Vitest (unit/integration) · Playwright (e2e)
Desktop App Tauri 2 (Rust shell + OS native webview)
Sidecar Bundling esbuild (server → single-file ESM bundle)
CI GitHub Actions

License

MIT

Top categories

Loading Svelte Themes