rename(taktik): rebrand to Times

Rename taktik → times across the entire app: package names (@taktik →
@times), appId, localStorage keys, export filenames, type names
(TaktikSettings → TimesSettings), monorepo scripts, shared-branding,
mana-auth trustedOrigins, docker-compose, and documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-30 15:44:18 +02:00
parent 1eb370eaaa
commit c33339b0cf
92 changed files with 970 additions and 1263 deletions

View file

@ -1,10 +1,10 @@
---
title: 'Taktik: Production Readiness Audit'
title: 'Times: Production Readiness Audit'
description: 'Zeiterfassung mit Live-Timer, Projekten, Kunden, Reports, CSV-Export, Templates und Abrechnungsraten - local-first mit umfassender Dokumentation und solider Testabdeckung'
date: 2026-03-30
app: 'taktik'
app: 'times'
author: 'Claude Code'
tags: ['audit', 'taktik', 'production-readiness', 'beta']
tags: ['audit', 'times', 'production-readiness', 'beta']
score: 55
scores:
backend: 5
@ -39,7 +39,7 @@ stats:
## Zusammenfassung
Taktik ist eine **vollwertige Zeiterfassung** mit Live-Timer, Projekt-/Kunden-Management, Reports mit Charts, CSV-Export und konfigurierbaren Abrechnungsraten. Local-first mit 6 Dexie-Collections, 4 Testdateien und umfassender CLAUDE.md. Feature-komplett für den Produktiveinsatz.
Times ist eine **vollwertige Zeiterfassung** mit Live-Timer, Projekt-/Kunden-Management, Reports mit Charts, CSV-Export und konfigurierbaren Abrechnungsraten. Local-first mit 6 Dexie-Collections, 4 Testdateien und umfassender CLAUDE.md. Feature-komplett für den Produktiveinsatz.
## Backend (5/100)

View file

@ -1,14 +0,0 @@
{
"name": "taktik",
"version": "1.0.0",
"private": true,
"description": "Taktik - Zeiterfassung & Timetracking",
"scripts": {
"dev": "pnpm --filter @taktik/web dev",
"dev:web": "pnpm --filter @taktik/web dev"
},
"devDependencies": {
"typescript": "^5.9.3"
},
"packageManager": "pnpm@9.15.0"
}

View file

@ -1,4 +1,4 @@
# Taktik
# Times
Zeiterfassung & Timetracking - Dein Arbeitsrhythmus, messbar gemacht.
@ -6,7 +6,7 @@ Zeiterfassung & Timetracking - Dein Arbeitsrhythmus, messbar gemacht.
## Project Overview
Taktik 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.
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
@ -24,16 +24,16 @@ Taktik is a professional time tracking app with timer, manual entry, projects, c
```bash
# From monorepo root
pnpm dev:taktik:web # Start web app on port 5197
pnpm dev:taktik:full # Start with auth + sync server
pnpm dev:times:web # Start web app on port 5197
pnpm dev:times:full # Start with auth + sync server
# Tests
pnpm --filter @taktik/web test # Run all tests
pnpm --filter @taktik/web test:unit # Run in watch mode
pnpm --filter @times/web test # Run all tests
pnpm --filter @times/web test:unit # Run in watch mode
# Type checking
pnpm --filter @taktik/web type-check
pnpm --filter @taktik/shared type-check
pnpm --filter @times/web type-check
pnpm --filter @times/shared type-check
```
## Key Features
@ -104,7 +104,7 @@ pnpm --filter @taktik/shared type-check
## Project Structure
```
apps/taktik/
apps/times/
├── apps/
│ └── web/ # SvelteKit web client (port 5197)
│ ├── src/
@ -160,7 +160,7 @@ apps/taktik/
│ │ └── version.ts
│ └── static/
├── packages/
│ └── shared/ # @taktik/shared
│ └── shared/ # @times/shared
│ └── src/
│ ├── types/index.ts # All TypeScript types
│ ├── constants/index.ts # Currencies, colors, defaults

View file

@ -9,15 +9,15 @@ ARG PUBLIC_MANA_CORE_AUTH_URL=http://mana-core-auth:3001
ENV PUBLIC_MANA_CORE_AUTH_URL=$PUBLIC_MANA_CORE_AUTH_URL
# Copy app-specific packages
COPY apps/taktik/packages/shared ./apps/taktik/packages/shared
COPY apps/taktik/apps/web ./apps/taktik/apps/web
COPY apps/times/packages/shared ./apps/times/packages/shared
COPY apps/times/apps/web ./apps/times/apps/web
# Install app-specific dependencies
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --no-frozen-lockfile --ignore-scripts
# Build the web app
WORKDIR /app/apps/taktik/apps/web
WORKDIR /app/apps/times/apps/web
RUN pnpm exec svelte-kit sync
RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm build
@ -25,17 +25,17 @@ RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm build
FROM node:20-alpine AS production
# Keep same directory structure as builder so pnpm symlinks resolve correctly
WORKDIR /app/apps/taktik/apps/web
WORKDIR /app/apps/times/apps/web
# Copy the pnpm store that symlinks point to (at /app/node_modules/.pnpm)
COPY --from=builder /app/node_modules/.pnpm /app/node_modules/.pnpm
# Copy the app's node_modules (contains symlinks to the pnpm store)
COPY --from=builder /app/apps/taktik/apps/web/node_modules ./node_modules
COPY --from=builder /app/apps/times/apps/web/node_modules ./node_modules
# Copy built application
COPY --from=builder /app/apps/taktik/apps/web/build ./build
COPY --from=builder /app/apps/taktik/apps/web/package.json ./
COPY --from=builder /app/apps/times/apps/web/build ./build
COPY --from=builder /app/apps/times/apps/web/package.json ./
# Expose port
EXPOSE 5027

View file

@ -1,5 +1,5 @@
{
"name": "@taktik/web",
"name": "@times/web",
"version": "1.0.0",
"private": true,
"scripts": {
@ -44,7 +44,7 @@
"@manacore/shared-types": "workspace:*",
"@manacore/shared-ui": "workspace:*",
"@manacore/shared-utils": "workspace:*",
"@taktik/shared": "workspace:*",
"@times/shared": "workspace:*",
"date-fns": "^4.1.0",
"svelte-i18n": "^4.0.1"
},

View file

@ -9,7 +9,7 @@
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="apple-touch-icon" href="%sveltekit.assets%/apple-touch-icon.png" />
<title>Taktik</title>
<title>Times</title>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">

View file

@ -2,7 +2,7 @@
import { getContext } from 'svelte';
import { _ } from 'svelte-i18n';
import { timeEntryCollection } from '$lib/data/local-store';
import type { Project, Client } from '@taktik/shared';
import type { Project, Client } from '@times/shared';
let {
visible = false,

View file

@ -3,7 +3,7 @@
import { _ } from 'svelte-i18n';
import { timeEntryCollection } from '$lib/data/local-store';
import { formatDurationCompact } from '$lib/data/queries';
import type { TimeEntry, Project, Client } from '@taktik/shared';
import type { TimeEntry, Project, Client } from '@times/shared';
import ConfirmDialog from './ConfirmDialog.svelte';
let {

View file

@ -2,7 +2,7 @@
import { _ } from 'svelte-i18n';
import EntryItem from './EntryItem.svelte';
import { groupEntriesByDate, getTotalDuration, formatDurationCompact } from '$lib/data/queries';
import type { TimeEntry } from '@taktik/shared';
import type { TimeEntry } from '@times/shared';
let { entries }: { entries: TimeEntry[] } = $props();

View file

@ -2,7 +2,7 @@
import { getContext } from 'svelte';
import { _ } from 'svelte-i18n';
import { timerStore } from '$lib/stores/timer.svelte';
import type { TimeEntry, Project } from '@taktik/shared';
import type { TimeEntry, Project } from '@times/shared';
const allTimeEntries = getContext<{ value: TimeEntry[] }>('timeEntries');
const allProjects = getContext<{ value: Project[] }>('projects');

View file

@ -3,7 +3,7 @@
import { _ } from 'svelte-i18n';
import { timerStore } from '$lib/stores/timer.svelte';
import { formatDuration } from '$lib/data/queries';
import type { Project, Client } from '@taktik/shared';
import type { Project, Client } from '@times/shared';
const allProjects = getContext<{ value: Project[] }>('projects');
const allClients = getContext<{ value: Client[] }>('clients');

View file

@ -3,7 +3,7 @@
import { _ } from 'svelte-i18n';
import { timerStore } from '$lib/stores/timer.svelte';
import { formatDuration } from '$lib/data/queries';
import type { Project } from '@taktik/shared';
import type { Project } from '@times/shared';
const allProjects = getContext<{ value: Project[] }>('projects');

View file

@ -1,5 +1,5 @@
/**
* Guest seed data for the Taktik app.
* Guest seed data for the Times app.
*
* Provides demo clients, projects, and time entries for the guest experience.
*/

View file

@ -1,5 +1,5 @@
/**
* Taktik Local-First Data Layer
* Times Local-First Data Layer
*
* IndexedDB (Dexie.js) with sync support for time tracking.
* Clients, projects, time entries, tags, templates, and settings.
@ -13,7 +13,7 @@ import {
guestTags,
guestSettings,
} from './guest-seed';
import type { BillingRate, ProjectVisibility, EntrySourceRef } from '@taktik/shared';
import type { BillingRate, ProjectVisibility, EntrySourceRef } from '@times/shared';
// ─── Types ──────────────────────────────────────────────────
@ -97,8 +97,8 @@ export interface LocalSettings extends BaseRecord {
const SYNC_SERVER_URL = import.meta.env.PUBLIC_SYNC_SERVER_URL || 'http://localhost:3050';
export const taktikStore = createLocalStore({
appId: 'taktik',
export const timesStore = createLocalStore({
appId: 'times',
collections: [
{
name: 'clients',
@ -145,9 +145,9 @@ export const taktikStore = createLocalStore({
});
// Typed collection accessors
export const clientCollection = taktikStore.collection<LocalClient>('clients');
export const projectCollection = taktikStore.collection<LocalProject>('projects');
export const timeEntryCollection = taktikStore.collection<LocalTimeEntry>('timeEntries');
export const tagCollection = taktikStore.collection<LocalTag>('tags');
export const templateCollection = taktikStore.collection<LocalTemplate>('templates');
export const settingsCollection = taktikStore.collection<LocalSettings>('settings');
export const clientCollection = timesStore.collection<LocalClient>('clients');
export const projectCollection = timesStore.collection<LocalProject>('projects');
export const timeEntryCollection = timesStore.collection<LocalTimeEntry>('timeEntries');
export const tagCollection = timesStore.collection<LocalTag>('tags');
export const templateCollection = timesStore.collection<LocalTemplate>('templates');
export const settingsCollection = timesStore.collection<LocalSettings>('settings');

View file

@ -17,7 +17,7 @@ import {
getClientById,
getProjectsByClient,
} from './queries';
import type { TimeEntry, Project, Client } from '@taktik/shared';
import type { TimeEntry, Project, Client } from '@times/shared';
// ─── Test Factories ──────────────────────────────────────

View file

@ -1,5 +1,5 @@
/**
* Reactive Queries & Pure Helpers for Taktik
* Reactive Queries & Pure Helpers for Times
*
* Uses Dexie liveQuery to automatically re-render when IndexedDB changes
* (local writes, sync updates, other tabs).
@ -26,10 +26,10 @@ import type {
TimeEntry,
Tag,
EntryTemplate,
TaktikSettings,
TimesSettings,
FilterCriteria,
SortOption,
} from '@taktik/shared';
} from '@times/shared';
// ─── Type Converters ───────────────────────────────────────
@ -118,7 +118,7 @@ export function toTemplate(local: LocalTemplate): EntryTemplate {
};
}
export function toSettings(local: LocalSettings): TaktikSettings {
export function toSettings(local: LocalSettings): TimesSettings {
return {
id: local.id,
defaultBillingRate: local.defaultBillingRate ?? undefined,
@ -178,7 +178,7 @@ export function useSettings() {
const locals = await settingsCollection.getAll();
return locals.length > 0 ? toSettings(locals[0]) : null;
},
null as TaktikSettings | null
null as TimesSettings | null
);
}

View file

@ -5,11 +5,11 @@ import type {
TimeEntry,
Tag,
EntryTemplate,
TaktikSettings,
TimesSettings,
BillingRate,
FilterCriteria,
SortOption,
} from '@taktik/shared';
} from '@times/shared';
describe('Shared Types', () => {
it('BillingRate has correct shape', () => {

View file

@ -11,7 +11,7 @@ register('en', () => import('./locales/en.json'));
function getInitialLocale(): SupportedLocale {
if (browser) {
const stored = localStorage.getItem('taktik_locale');
const stored = localStorage.getItem('times_locale');
if (stored && supportedLocales.includes(stored as SupportedLocale)) {
return stored as SupportedLocale;
}
@ -31,7 +31,7 @@ init({
export function setLocale(newLocale: SupportedLocale) {
locale.set(newLocale);
if (browser) {
localStorage.setItem('taktik_locale', newLocale);
localStorage.setItem('times_locale', newLocale);
}
}

View file

@ -1,6 +1,6 @@
{
"app": {
"name": "Taktik",
"name": "Times",
"loading": "Laden...",
"tagline": "Dein Arbeitsrhythmus, messbar gemacht."
},

View file

@ -1,6 +1,6 @@
{
"app": {
"name": "Taktik",
"name": "Times",
"loading": "Loading...",
"tagline": "Your work rhythm, made measurable."
},

View file

@ -1,6 +1,6 @@
import { createThemeStore } from '@manacore/shared-theme';
export const theme = createThemeStore({
appId: 'taktik',
appId: 'times',
defaultVariant: 'ocean',
});

View file

@ -12,7 +12,7 @@ function getAuthUrl(): string {
}
export const userSettings = createUserSettingsStore({
appId: 'taktik',
appId: 'times',
authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(),
});

View file

@ -1,9 +1,9 @@
import { browser } from '$app/environment';
import type { ViewMode, SortOption, FilterCriteria, SavedFilter } from '@taktik/shared';
import type { ViewMode, SortOption, FilterCriteria, SavedFilter } from '@times/shared';
const VIEW_KEY = 'taktik_view_mode';
const SORT_KEY = 'taktik_sort';
const FILTERS_KEY = 'taktik_saved_filters';
const VIEW_KEY = 'times_view_mode';
const SORT_KEY = 'times_sort';
const FILTERS_KEY = 'times_saved_filters';
function load<T>(key: string, fallback: T): T {
if (!browser) return fallback;

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import type { TimeEntry, Project, Client } from '@taktik/shared';
import type { TimeEntry, Project, Client } from '@times/shared';
// We test the CSV generation logic without triggering DOM download.
// This mirrors the core logic from export.ts.

View file

@ -2,7 +2,7 @@
* CSV Export utility for time entries
*/
import type { TimeEntry, Project, Client } from '@taktik/shared';
import type { TimeEntry, Project, Client } from '@times/shared';
export function exportEntriesToCSV(
entries: TimeEntry[],
@ -55,7 +55,7 @@ export function exportEntriesToCSV(
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `taktik-export-${new Date().toISOString().split('T')[0]}.csv`;
a.download = `times-export-${new Date().toISOString().split('T')[0]}.csv`;
a.click();
URL.revokeObjectURL(url);
}

View file

@ -4,7 +4,7 @@
* Applies rounding based on user settings (increment + method).
*/
import type { RoundingMethod } from '@taktik/shared';
import type { RoundingMethod } from '@times/shared';
/**
* Round a duration in seconds based on settings.

View file

@ -13,7 +13,7 @@
import { getPillAppItems } from '@manacore/shared-branding';
import { AuthGate, GuestWelcomeModal } from '@manacore/shared-auth-ui';
import { shouldShowGuestWelcome } from '@manacore/shared-auth-ui';
import { taktikStore } from '$lib/data/local-store';
import { timesStore } from '$lib/data/local-store';
import {
useAllClients,
useAllProjects,
@ -46,17 +46,17 @@
setContext('settings', settings);
async function handleAuthReady() {
await taktikStore.initialize();
await timesStore.initialize();
if (authStore.isAuthenticated) {
taktikStore.startSync(() => authStore.getValidToken());
timesStore.startSync(() => authStore.getValidToken());
}
viewStore.initialize();
await timerStore.initialize();
initialized = true;
if (!authStore.isAuthenticated && shouldShowGuestWelcome('taktik')) {
if (!authStore.isAuthenticated && shouldShowGuestWelcome('times')) {
showGuestWelcome = true;
}
}
@ -105,7 +105,7 @@
/>
</svg>
</div>
<span class="text-lg font-bold text-[hsl(var(--foreground))]">Taktik</span>
<span class="text-lg font-bold text-[hsl(var(--foreground))]">Times</span>
</a>
<!-- Nav Items -->
@ -203,7 +203,7 @@
</button>
<GuestWelcomeModal
appId="taktik"
appId="times"
visible={showGuestWelcome}
onClose={() => (showGuestWelcome = false)}
onLogin={() => goto('/login')}

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte';
import { _ } from 'svelte-i18n';
import type { TimeEntry, Project, Client } from '@taktik/shared';
import type { TimeEntry, Project, Client } from '@times/shared';
import {
getEntriesByDate,
getTotalDuration,
@ -29,7 +29,7 @@
</script>
<svelte:head>
<title>Timer | Taktik</title>
<title>Timer | Times</title>
</svelte:head>
<div class="space-y-6">

View file

@ -3,8 +3,8 @@
import { _ } from 'svelte-i18n';
import { clientCollection } from '$lib/data/local-store';
import { getTotalDuration, formatDurationCompact } from '$lib/data/queries';
import type { Client, Project, TimeEntry } from '@taktik/shared';
import { PROJECT_COLORS } from '@taktik/shared/constants';
import type { Client, Project, TimeEntry } from '@times/shared';
import { PROJECT_COLORS } from '@times/shared/constants';
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
const allClients = getContext<{ value: Client[] }>('clients');
@ -101,7 +101,7 @@
</script>
<svelte:head>
<title>{$_('nav.clients')} | Taktik</title>
<title>{$_('nav.clients')} | Times</title>
</svelte:head>
<div class="space-y-6">

View file

@ -10,7 +10,7 @@
formatDurationDecimal,
} from '$lib/data/queries';
import EntryList from '$lib/components/EntryList.svelte';
import type { Project, Client, TimeEntry } from '@taktik/shared';
import type { Project, Client, TimeEntry } from '@times/shared';
const allClients = getContext<{ value: Client[] }>('clients');
const allProjects = getContext<{ value: Project[] }>('projects');
@ -42,7 +42,7 @@
</script>
<svelte:head>
<title>{client?.name || 'Kunde'} | Taktik</title>
<title>{client?.name || 'Kunde'} | Times</title>
</svelte:head>
{#if !client}

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte';
import { _ } from 'svelte-i18n';
import type { TimeEntry } from '@taktik/shared';
import type { TimeEntry } from '@times/shared';
import {
getFilteredEntries,
getSortedEntries,
@ -56,7 +56,7 @@
</script>
<svelte:head>
<title>{$_('nav.entries')} | Taktik</title>
<title>{$_('nav.entries')} | Times</title>
</svelte:head>
<div class="space-y-6">

View file

@ -3,7 +3,7 @@
</script>
<svelte:head>
<title>Feedback | Taktik</title>
<title>Feedback | Times</title>
</svelte:head>
<div>

View file

@ -3,7 +3,7 @@
</script>
<svelte:head>
<title>Hilfe | Taktik</title>
<title>Hilfe | Times</title>
</svelte:head>
<div>

View file

@ -3,7 +3,7 @@
</script>
<svelte:head>
<title>Mana | Taktik</title>
<title>Mana | Times</title>
</svelte:head>
<div>

View file

@ -3,7 +3,7 @@
</script>
<svelte:head>
<title>Profil | Taktik</title>
<title>Profil | Times</title>
</svelte:head>
<div>

View file

@ -3,8 +3,8 @@
import { _ } from 'svelte-i18n';
import { projectCollection } from '$lib/data/local-store';
import { getTotalDuration, formatDurationCompact } from '$lib/data/queries';
import type { Project, Client, TimeEntry } from '@taktik/shared';
import { PROJECT_COLORS } from '@taktik/shared/constants';
import type { Project, Client, TimeEntry } from '@times/shared';
import { PROJECT_COLORS } from '@times/shared/constants';
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
const allProjects = getContext<{ value: Project[] }>('projects');
@ -109,7 +109,7 @@
</script>
<svelte:head>
<title>{$_('nav.projects')} | Taktik</title>
<title>{$_('nav.projects')} | Times</title>
</svelte:head>
<div class="space-y-6">

View file

@ -11,8 +11,8 @@
formatDurationDecimal,
} from '$lib/data/queries';
import EntryList from '$lib/components/EntryList.svelte';
import type { Project, Client, TimeEntry } from '@taktik/shared';
import { PROJECT_COLORS } from '@taktik/shared/constants';
import type { Project, Client, TimeEntry } from '@times/shared';
import { PROJECT_COLORS } from '@times/shared/constants';
const allProjects = getContext<{ value: Project[] }>('projects');
const allClients = getContext<{ value: Client[] }>('clients');
@ -82,7 +82,7 @@
</script>
<svelte:head>
<title>{project?.name || 'Projekt'} | Taktik</title>
<title>{project?.name || 'Projekt'} | Times</title>
</svelte:head>
{#if !project}

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte';
import { _ } from 'svelte-i18n';
import type { TimeEntry, Project, Client } from '@taktik/shared';
import type { TimeEntry, Project, Client } from '@times/shared';
import { exportEntriesToCSV } from '$lib/utils/export';
import {
getTotalDuration,
@ -81,7 +81,7 @@
</script>
<svelte:head>
<title>{$_('nav.reports')} | Taktik</title>
<title>{$_('nav.reports')} | Times</title>
</svelte:head>
<div class="space-y-6">

View file

@ -2,10 +2,10 @@
import { getContext } from 'svelte';
import { _ } from 'svelte-i18n';
import { settingsCollection } from '$lib/data/local-store';
import type { TaktikSettings } from '@taktik/shared';
import { CURRENCIES, ROUNDING_INCREMENTS } from '@taktik/shared/constants';
import type { TimesSettings } from '@times/shared';
import { CURRENCIES, ROUNDING_INCREMENTS } from '@times/shared/constants';
const settings = getContext<{ value: TaktikSettings | null }>('settings');
const settings = getContext<{ value: TimesSettings | null }>('settings');
// Local edit state, synced from settings
let workingHoursPerDay = $state(8);
@ -49,7 +49,7 @@
</script>
<svelte:head>
<title>{$_('settings.title')} | Taktik</title>
<title>{$_('settings.title')} | Times</title>
</svelte:head>
<div class="space-y-6">

View file

@ -3,7 +3,7 @@
import { _ } from 'svelte-i18n';
import { templateCollection, timeEntryCollection } from '$lib/data/local-store';
import { timerStore } from '$lib/stores/timer.svelte';
import type { EntryTemplate, Project, Client } from '@taktik/shared';
import type { EntryTemplate, Project, Client } from '@times/shared';
const allTemplates = getContext<{ value: EntryTemplate[] }>('templates');
const allProjects = getContext<{ value: Project[] }>('projects');
@ -64,7 +64,7 @@
</script>
<svelte:head>
<title>{$_('nav.templates')} | Taktik</title>
<title>{$_('nav.templates')} | Times</title>
</svelte:head>
<div class="space-y-6">

View file

@ -3,7 +3,7 @@
</script>
<svelte:head>
<title>Themes | Taktik</title>
<title>Themes | Times</title>
</svelte:head>
<div>

View file

@ -96,7 +96,7 @@
</script>
<svelte:head>
<title>{showRegister ? $_('auth.register') : $_('auth.login')} | Taktik</title>
<title>{showRegister ? $_('auth.register') : $_('auth.login')} | Times</title>
</svelte:head>
<div class="flex min-h-screen items-center justify-center bg-[hsl(var(--background))] p-4">
@ -115,7 +115,7 @@
/>
</svg>
</div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Taktik</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Times</h1>
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))]">Zeiterfassung</p>
</div>
@ -256,7 +256,7 @@
<!-- App switcher -->
<div class="mt-6 flex flex-wrap justify-center gap-2">
{#each getPillAppItems() as app}
{#if app.id !== 'taktik'}
{#if app.id !== 'times'}
<a
href={app.url}
class="rounded-full border border-[hsl(var(--border))] px-3 py-1 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--accent-foreground))]"

View file

@ -5,6 +5,6 @@ export const GET: RequestHandler = async () => {
return json({
status: 'ok',
timestamp: new Date().toISOString(),
service: 'taktik-web',
service: 'times-web',
});
};

View file

@ -3,7 +3,7 @@
</script>
<svelte:head>
<title>Offline | Taktik</title>
<title>Offline | Times</title>
</svelte:head>
<div class="flex min-h-screen items-center justify-center bg-[hsl(var(--background))] p-4">

View file

@ -11,8 +11,8 @@ export default defineConfig({
SvelteKitPWA({
registerType: 'autoUpdate',
manifest: {
name: 'Taktik',
short_name: 'Taktik',
name: 'Times',
short_name: 'Times',
description: 'Zeiterfassung & Timetracking',
theme_color: '#f59e0b',
background_color: '#0f172a',

14
apps/times/package.json Normal file
View file

@ -0,0 +1,14 @@
{
"name": "times",
"version": "1.0.0",
"private": true,
"description": "Times - Zeiterfassung & Timetracking",
"scripts": {
"dev": "pnpm --filter @times/web dev",
"dev:web": "pnpm --filter @times/web dev"
},
"devDependencies": {
"typescript": "^5.9.3"
},
"packageManager": "pnpm@9.15.0"
}

View file

@ -1,5 +1,5 @@
{
"name": "@taktik/shared",
"name": "@times/shared",
"version": "1.0.0",
"private": true,
"type": "module",

View file

@ -110,7 +110,7 @@ export interface EntryTemplate {
export type RoundingMethod = 'none' | 'up' | 'down' | 'nearest';
export interface TaktikSettings {
export interface TimesSettings {
id: string;
defaultBillingRate?: BillingRate;
workingHoursPerDay: number;