mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-17 01:39:39 +02:00
Create entry-parser.ts with duration extraction (2h, 30min, 1h30m), time range parsing (9-12, 14:00-16:30), project (@), tags (#), billable ($), and date recognition. Multi-entry splitting via semicolons with context inheritance. Integrate quick-input bar into EntryForm — type "Meeting 2h @Client $; Review 1h" and press Enter to create multiple entries at once. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
229 lines
9.2 KiB
Markdown
229 lines
9.2 KiB
Markdown
# Times
|
|
|
|
Zeiterfassung & Timetracking - Dein Arbeitsrhythmus, messbar gemacht.
|
|
|
|
**Web App Port:** 5197
|
|
|
|
## Project Overview
|
|
|
|
Times is a professional time tracking app with timer, manual entry, projects, clients, reports, templates, and guild (team) integration. Built local-first for offline capability and instant UI.
|
|
|
|
### Tech Stack
|
|
|
|
| Layer | Technology |
|
|
|-------|------------|
|
|
| Frontend | SvelteKit 2, Svelte 5 (runes), Tailwind CSS 4 |
|
|
| Data | @manacore/local-store (Dexie.js + mana-sync) |
|
|
| Auth | @manacore/shared-auth + AuthGate (guest mode supported) |
|
|
| Icons | @manacore/shared-icons (Phosphor) |
|
|
| PWA | @vite-pwa/sveltekit + Workbox |
|
|
| i18n | svelte-i18n (de, en) |
|
|
| Testing | Vitest |
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# From monorepo root
|
|
pnpm dev:times:web # Start web app on port 5197
|
|
pnpm dev:times:full # Start with auth + sync server
|
|
|
|
# Tests
|
|
pnpm --filter @times/web test # Run all tests
|
|
pnpm --filter @times/web test:unit # Run in watch mode
|
|
|
|
# Type checking
|
|
pnpm --filter @times/web type-check
|
|
pnpm --filter @times/shared type-check
|
|
```
|
|
|
|
## Key Features
|
|
|
|
### Timer
|
|
- Start/stop with one click, live HH:MM:SS counter
|
|
- Persists in IndexedDB (survives page reload/crash)
|
|
- Auto-save every 10 seconds
|
|
- Compact indicator in navbar when running (visible on all pages)
|
|
- Quick Start from recent entries or templates
|
|
|
|
### Time Entries
|
|
- **Quick Input (NL)**: Type `"Meeting 2h @Projekt $; Review 1h; Mails 30min"` → creates 3 entries
|
|
- Manual entry with quick-duration buttons (15m, 30m, 1h, 1.5h, 2h, 4h)
|
|
- Inline-expand editing (click to expand, auto-save on change)
|
|
- Day grouping with totals
|
|
- Filter by week/month/all
|
|
- CSV export (semicolon-delimited, UTF-8 BOM for Excel)
|
|
|
|
### Quick Input Syntax
|
|
|
|
The EntryForm includes a NL quick-input bar (press Enter to create):
|
|
|
|
```
|
|
"Meeting 2h @ClientX #team $"
|
|
→ description: Meeting, duration: 2h, project: ClientX, tags: [team], billable: true
|
|
|
|
"9-12 Workshop @Schulung; 13-15 Nachbereitung; Mails 30min"
|
|
→ 3 entries with time ranges and context inheritance
|
|
```
|
|
|
|
Recognized patterns:
|
|
- **Duration**: `30min`, `2h`, `1.5h`, `1h30m`, `1.5 Stunden`
|
|
- **Time Range**: `9-12`, `14:00-16:30` (auto-calculates duration)
|
|
- **Project**: `@ProjectName`
|
|
- **Tags**: `#tag1 #tag2`
|
|
- **Billable**: `$`, `billable`, `abrechenbar`
|
|
- **Date**: `heute`, `morgen`, `gestern`, `montag`
|
|
- **Multi-Entry**: Split with `;` or `danach`/`dann` (inherits date + project)
|
|
|
|
### Projects
|
|
- Color-coded project cards with budget progress bars
|
|
- Client assignment with inherited billing rates
|
|
- Billable/non-billable toggle
|
|
- Archive/unarchive, inline CRUD
|
|
|
|
### Clients
|
|
- Billing rates (per hour/day) with currency selection
|
|
- Short codes for quick reference
|
|
- Project and hours rollup
|
|
|
|
### Reports
|
|
- Stats: total hours, billable hours, avg/day, entry count
|
|
- Billable vs non-billable breakdown bar
|
|
- Hours by project (horizontal bar chart)
|
|
- Hours by day (vertical bar chart, last 7 days)
|
|
- Week/month toggle
|
|
- CSV export
|
|
|
|
### Templates
|
|
- Save frequent entries as reusable templates
|
|
- One-click timer start from template
|
|
- Sorted by usage count
|
|
|
|
### Settings
|
|
- Working hours/day, working days/week
|
|
- Week start (Monday/Sunday)
|
|
- Rounding increment (0/1/5/6/10/15 min) and method (none/up/down/nearest)
|
|
- Default billing rate with currency (EUR/CHF/USD/GBP)
|
|
- Timer reminder and auto-stop configuration
|
|
|
|
### Keyboard Shortcuts
|
|
| Key | Action |
|
|
|-----|--------|
|
|
| `s` | Start/Stop timer |
|
|
| `n` | New manual entry |
|
|
| `Escape` | Close modal / blur input |
|
|
|
|
## Data Collections
|
|
|
|
| Collection | Purpose | Key Indexes |
|
|
|------------|---------|-------------|
|
|
| clients | Customer management | order, isArchived, shortCode |
|
|
| projects | Project tracking | clientId, isArchived, isBillable, guildId |
|
|
| timeEntries | Core time records | projectId, date, isRunning, [date+projectId] |
|
|
| tags | Entry categorization | name, order |
|
|
| templates | Quick-start templates | usageCount, lastUsedAt |
|
|
| settings | App configuration | (single record) |
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
apps/times/
|
|
├── apps/
|
|
│ └── web/ # SvelteKit web client (port 5197)
|
|
│ ├── src/
|
|
│ │ ├── routes/
|
|
│ │ │ ├── (auth)/ # Login/register flow
|
|
│ │ │ │ └── login/
|
|
│ │ │ ├── (app)/ # Authenticated app
|
|
│ │ │ │ ├── +layout.svelte # AuthGate, PillNav, TimerIndicator, contexts
|
|
│ │ │ │ ├── +page.svelte # Timer home page
|
|
│ │ │ │ ├── entries/ # Time entry list
|
|
│ │ │ │ ├── projects/ # Project management
|
|
│ │ │ │ ├── clients/ # Client management
|
|
│ │ │ │ ├── reports/ # Dashboard & charts
|
|
│ │ │ │ ├── templates/ # Entry templates
|
|
│ │ │ │ ├── settings/ # App configuration
|
|
│ │ │ │ ├── mana/ # Credits & subscription
|
|
│ │ │ │ ├── feedback/ # Feedback form
|
|
│ │ │ │ ├── profile/ # User profile
|
|
│ │ │ │ ├── themes/ # Theme selection
|
|
│ │ │ │ └── help/ # Help & docs
|
|
│ │ │ ├── +layout.svelte # Root layout (i18n, theme, auth init)
|
|
│ │ │ ├── +layout.ts # SSR disabled
|
|
│ │ │ ├── +error.svelte # Error page
|
|
│ │ │ ├── health/+server.ts # Health check
|
|
│ │ │ └── offline/ # Offline fallback
|
|
│ │ └── lib/
|
|
│ │ ├── data/
|
|
│ │ │ ├── local-store.ts # 6 collections + typed accessors
|
|
│ │ │ ├── queries.ts # Live queries + pure helpers
|
|
│ │ │ ├── queries.test.ts # Unit tests
|
|
│ │ │ └── guest-seed.ts # Demo data (2 clients, 3 projects, 5 entries)
|
|
│ │ ├── stores/
|
|
│ │ │ ├── auth.svelte.ts # Mana auth factory
|
|
│ │ │ ├── timer.svelte.ts # Timer start/stop/resume/auto-save
|
|
│ │ │ ├── view.svelte.ts # View mode, filters, sort
|
|
│ │ │ ├── theme.ts # Theme store (ocean default)
|
|
│ │ │ ├── navigation.ts # Nav collapse state
|
|
│ │ │ └── user-settings.svelte.ts
|
|
│ │ ├── components/
|
|
│ │ │ ├── TimerCard.svelte # Main timer widget
|
|
│ │ │ ├── TimerIndicator.svelte # Compact navbar indicator
|
|
│ │ │ ├── EntryItem.svelte # Inline-expandable entry
|
|
│ │ │ ├── EntryList.svelte # Day-grouped entry list
|
|
│ │ │ ├── EntryForm.svelte # Manual entry modal
|
|
│ │ │ ├── QuickStart.svelte # Recent entry pills
|
|
│ │ │ └── KeyboardShortcuts.svelte
|
|
│ │ ├── utils/
|
|
│ │ │ ├── export.ts # CSV export
|
|
│ │ │ └── export.test.ts # Export tests
|
|
│ │ ├── i18n/
|
|
│ │ │ ├── index.ts # svelte-i18n setup
|
|
│ │ │ └── locales/ # de.json, en.json
|
|
│ │ └── version.ts
|
|
│ └── static/
|
|
├── packages/
|
|
│ └── shared/ # @times/shared
|
|
│ └── src/
|
|
│ ├── types/index.ts # All TypeScript types
|
|
│ ├── constants/index.ts # Currencies, colors, defaults
|
|
│ └── index.ts
|
|
├── CLAUDE.md
|
|
└── package.json
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Timer Flow
|
|
```
|
|
User clicks Start → timerStore.start() → Insert timeEntry (isRunning=true) → IndexedDB
|
|
→ Start 1s tick interval (UI counter)
|
|
→ Start 10s auto-save interval
|
|
|
|
User clicks Stop → timerStore.stop() → Update timeEntry (isRunning=false, endTime, duration)
|
|
→ Stop intervals
|
|
→ Entry appears in today's list
|
|
```
|
|
|
|
### Data Flow (Local-First)
|
|
```
|
|
Guest: App → IndexedDB (Dexie.js) → UI (no sync)
|
|
Logged in: App → IndexedDB → UI → SyncEngine → mana-sync → PostgreSQL
|
|
← WebSocket push ←
|
|
```
|
|
|
|
### Context Providers (set in app layout)
|
|
All data is provided via Svelte context from `(app)/+layout.svelte`:
|
|
- `clients` - Live query of all clients
|
|
- `projects` - Live query of all projects
|
|
- `timeEntries` - Live query of all time entries
|
|
- `tags` - Live query of all tags
|
|
- `templates` - Live query of all templates
|
|
- `settings` - Live query of settings (single record)
|
|
|
|
## Gilden Integration (Planned v2)
|
|
|
|
- Projects with `visibility: 'guild'` + `guildId` are shared with team
|
|
- Time entries inherit visibility from project
|
|
- Team dashboard: hours per member, budget tracking
|
|
- Manager vs member views
|
|
- Credit consumption from guild pool for AI/PDF features
|