diff --git a/app/Http/Controllers/Settings/GeneralController.php b/app/Http/Controllers/Settings/GeneralController.php index 10a9c75..e6d56ea 100644 --- a/app/Http/Controllers/Settings/GeneralController.php +++ b/app/Http/Controllers/Settings/GeneralController.php @@ -10,6 +10,7 @@ use App\Http\Requests\UpdateGeneralSettingsRequest; use App\Http\Requests\UpdateLocaleRequest; use App\Jobs\CalculateWeekBalance; +use App\Services\TimeFormatService; use App\Settings\GeneralSettings; use App\Settings\ProjectSettings; use DateTimeZone; @@ -38,6 +39,7 @@ public function edit(GeneralSettings $settings) 'timezones' => DateTimeZone::listIdentifiers(), 'timezone' => $settings->timezone, 'defaultOverview' => $settings->default_overview, + 'timeDisplayFormat' => $settings->time_display_format ?? TimeFormatService::CLOCK, ]); } @@ -53,6 +55,7 @@ public function update(UpdateGeneralSettingsRequest $request, GeneralSettings $s $settings->appActivityTracking = $data['appActivityTracking']; $settings->timezone = $data['timezone']; $settings->default_overview = $data['default_overview'] ?? 'week'; + $settings->time_display_format = $data['time_display_format'] ?? TimeFormatService::CLOCK; if ($data['theme'] !== $settings->theme ?? SystemThemesEnum::SYSTEM->value) { $settings->theme = $data['theme']; diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 525dd8e..288c9d3 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -4,6 +4,7 @@ namespace App\Http\Middleware; +use App\Services\TimeFormatService; use App\Services\TimestampService; use App\Settings\GeneralSettings; use Illuminate\Http\Request; @@ -42,6 +43,7 @@ public function share(Request $request): array 'js_locale' => str_replace('_', '-', $settings->locale ?? config('app.fallback_locale')), 'locale' => $settings->locale ?? config('app.fallback_locale'), 'timezone' => $settings->timezone ?? config('app.timezone'), + 'time_display_format' => $settings->time_display_format ?? TimeFormatService::CLOCK, 'app_version' => config('nativephp.version'), 'date' => now()->format('Y-m-d'), 'recording' => (bool) TimestampService::getCurrentType(), diff --git a/app/Http/Requests/UpdateGeneralSettingsRequest.php b/app/Http/Requests/UpdateGeneralSettingsRequest.php index 71cc80d..d8876e5 100644 --- a/app/Http/Requests/UpdateGeneralSettingsRequest.php +++ b/app/Http/Requests/UpdateGeneralSettingsRequest.php @@ -4,6 +4,7 @@ namespace App\Http\Requests; +use App\Services\TimeFormatService; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; @@ -35,6 +36,7 @@ public function rules(): array 'appActivityTracking' => ['required', 'boolean'], 'timezone' => ['required', 'string', 'timezone'], 'default_overview' => ['required', Rule::in(['day', 'week', 'month', 'year'])], + 'time_display_format' => ['required', Rule::in([TimeFormatService::CLOCK, TimeFormatService::DECIMAL])], ]; } } diff --git a/app/Jobs/MenubarRefresh.php b/app/Jobs/MenubarRefresh.php index 0fbc384..3ca1976 100644 --- a/app/Jobs/MenubarRefresh.php +++ b/app/Jobs/MenubarRefresh.php @@ -6,8 +6,10 @@ use App\Enums\TimestampTypeEnum; use App\Services\LocaleService; +use App\Services\TimeFormatService; use App\Services\TimestampService; use App\Services\TrayIconService; +use App\Settings\GeneralSettings; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Queue\Queueable; use Native\Desktop\Facades\MenuBar; @@ -23,6 +25,7 @@ public function handle(): void { try { new LocaleService; + $settings = resolve(GeneralSettings::class); $currentType = TimestampService::getCurrentType(); if ($currentType === TimestampTypeEnum::WORK) { @@ -39,8 +42,10 @@ public function handle(): void return; } - MenuBar::tooltip(gmdate('G:i', (int) $time)); - MenuBar::label(gmdate('G:i', (int) $time)); + $formattedTime = TimeFormatService::formatDuration($time, $settings->time_display_format ?? TimeFormatService::CLOCK); + + MenuBar::tooltip($formattedTime); + MenuBar::label($formattedTime); } catch (\Throwable) { return; } diff --git a/app/Services/TimeFormatService.php b/app/Services/TimeFormatService.php new file mode 100644 index 0000000..9850a5c --- /dev/null +++ b/app/Services/TimeFormatService.php @@ -0,0 +1,52 @@ += 3600 ? 'h' : 'min'; + } + + public static function formatDurationWithUnit(int|float $seconds, string $format = self::CLOCK): string + { + $unit = self::unitTranslationKey($seconds, $format); + + if (app()->bound('translator')) { + $unit = trans('app.'.$unit); + } + + $formattedDuration = self::formatDuration($seconds, $format); + + if ($format === self::CLOCK && abs($seconds) < 3600) { + $minutes = (int) floor(abs($seconds) / 60); + $formattedDuration = ($seconds < 0 ? '-' : '').$minutes; + } + + return sprintf( + '%s %s', + $formattedDuration, + $unit + ); + } +} diff --git a/app/Settings/GeneralSettings.php b/app/Settings/GeneralSettings.php index 59d6dff..937dd72 100644 --- a/app/Settings/GeneralSettings.php +++ b/app/Settings/GeneralSettings.php @@ -34,6 +34,8 @@ class GeneralSettings extends Settings public string $default_overview = 'week'; + public string $time_display_format = 'clock'; + public static function group(): string { return 'general'; diff --git a/database/settings/2026_04_11_195700_add_time_display_format_to_general_settings.php b/database/settings/2026_04_11_195700_add_time_display_format_to_general_settings.php new file mode 100644 index 0000000..0720f67 --- /dev/null +++ b/database/settings/2026_04_11_195700_add_time_display_format_to_general_settings.php @@ -0,0 +1,18 @@ +migrator->add('general.time_display_format', 'clock'); + } + + public function down(): void + { + $this->migrator->delete('general.time_display_format'); + } +}; diff --git a/lang/da/app.php b/lang/da/app.php index 3a204a3..422d523 100644 --- a/lang/da/app.php +++ b/lang/da/app.php @@ -54,9 +54,11 @@ 'chinese' => 'Kinesisk', 'choose how fractional days are rounded.' => 'Vælg, hvordan brøkdelsdage rundes.', 'choose the appearance of the application.' => 'Vælg applikationens udseende.', + 'choose how durations are shown' => 'Vælg, hvordan varigheder vises.', 'choose whether to show vacation steps' => 'Vælg, om ferietrin skal vises.', 'choose your mode' => 'Vælg din tilstand.', 'click "export" at the top of the table and choose "save as csv".' => 'Klik på "Eksporter" øverst i tabellen og vælg "Gem som CSV".', + 'clock (7:30)' => 'Klokkeslæt (7:30)', 'close' => 'Luk', 'color' => 'Farve', 'confirm' => 'Bekræft', @@ -89,6 +91,7 @@ 'day' => 'Dag', 'day equivalent' => 'Dagsækvivalent', 'days' => 'Dage', + 'decimal hours (7.50)' => 'Decimaltimer (7.50)', 'default annual entitlement' => 'Standard årlig tildeling', 'default overview' => 'Standardoversigt', 'default: :value days' => 'Standard: :value dage', @@ -341,6 +344,7 @@ 'this shortcut is not supported.' => 'Denne genvej understøttes ikke.', 'time' => 'Tid', 'time balance' => 'Tidssaldo', + 'time display' => 'Tidsvisning', 'time span' => 'Tidsperiode', 'timezone' => 'Tidszone', 'to import data from clockify, you must first export it. Simply follow the step-by-step instructions below.' => 'For at importere data fra Clockify skal du først eksportere dem. Følg blot de trinvise instruktioner nedenfor.', diff --git a/lang/de/app.php b/lang/de/app.php index 9fd000e..4487cd4 100644 --- a/lang/de/app.php +++ b/lang/de/app.php @@ -54,9 +54,11 @@ 'chinese' => 'Chinesisch', 'choose how fractional days are rounded.' => 'Legt fest, in welchen Abstufungen Urlaubstage gerundet werden.', 'choose the appearance of the application.' => 'Wähle das Erscheinungsbild der Anwendung.', + 'choose how durations are shown' => 'Wähle, wie Zeitdauern angezeigt werden.', 'choose whether to show vacation steps' => 'Wähle, ob wir Urlaubsschritte zeigen sollen.', 'choose your mode' => 'Wähle deinen Modus.', 'click "export" at the top of the table and choose "save as csv".' => 'Klicken Sie oben in der Tabelle auf „Export“ und wählen Sie „Als CSV speichern“.', + 'clock (7:30)' => 'Uhrzeit (7:30)', 'close' => 'Schließen', 'color' => 'Farbe', 'confirm' => 'Bestätigen', @@ -89,6 +91,7 @@ 'day' => 'Tag', 'day equivalent' => 'Tagesäquivalent', 'days' => 'Tage', + 'decimal hours (7.50)' => 'Dezimalstunden (7.50)', 'default annual entitlement' => 'Standardjahreskontingent', 'default overview' => 'Standard-Übersicht', 'default: :value days' => 'Standardwert: :value Tage', @@ -341,6 +344,7 @@ 'this shortcut is not supported.' => 'Diese Tastenkombination wird nicht unterstützt.', 'time' => 'Uhrzeit', 'time balance' => 'Arbeitszeitbilanz', + 'time display' => 'Zeitanzeige', 'time span' => 'Zeitspanne', 'timezone' => 'Zeitzone', 'to import data from clockify, you must first export it. Simply follow the step-by-step instructions below.' => 'Um Daten aus Clockify zu importieren, müssen Sie diese zunächst exportieren. Folgen Sie einfach der nachfolgenden Schritt-für-Schritt-Anleitung.', diff --git a/lang/en/app.php b/lang/en/app.php index c46efd6..2e9c640 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -54,9 +54,11 @@ 'chinese' => 'Chinese', 'choose how fractional days are rounded.' => 'Choose how fractional days are rounded.', 'choose the appearance of the application.' => 'Choose the appearance of the application.', + 'choose how durations are shown' => 'Choose how durations are shown.', 'choose whether to show vacation steps' => 'Choose whether to show vacation steps.', 'choose your mode' => 'Choose your mode.', 'click "export" at the top of the table and choose "save as csv".' => 'Click "Export" at the top of the table and choose "Save as CSV".', + 'clock (7:30)' => 'Clock (7:30)', 'close' => 'Close', 'color' => 'Color', 'confirm' => 'Confirm', @@ -89,6 +91,7 @@ 'day' => 'Day', 'day equivalent' => 'Day equivalent', 'days' => 'Days', + 'decimal hours (7.50)' => 'Decimal hours (7.50)', 'default annual entitlement' => 'Default annual entitlement', 'default overview' => 'Default overview', 'default: :value days' => 'Default: :value days', @@ -341,6 +344,7 @@ 'this shortcut is not supported.' => 'This shortcut is not supported.', 'time' => 'Time', 'time balance' => 'Time Balance', + 'time display' => 'Time display', 'time span' => 'Time span', 'timezone' => 'Timezone', 'to import data from clockify, you must first export it. Simply follow the step-by-step instructions below.' => 'To import data from Clockify, you must first export it. Simply follow the step-by-step instructions below.', diff --git a/lang/fr/app.php b/lang/fr/app.php index 7f48c87..d10bfdb 100644 --- a/lang/fr/app.php +++ b/lang/fr/app.php @@ -54,9 +54,11 @@ 'chinese' => 'Chinois', 'choose how fractional days are rounded.' => 'Détermine comment les fractions de journée sont arrondies.', 'choose the appearance of the application.' => 'Choisissez l\'apparence de l\'application.', + 'choose how durations are shown' => 'Choisissez comment les durées sont affichées.', 'choose whether to show vacation steps' => 'Choisissez si nous devons afficher les étapes de congés.', 'choose your mode' => 'Choisissez votre mode.', 'click "export" at the top of the table and choose "save as csv".' => 'Cliquez sur "Exporter" en haut du tableau et choisissez "Enregistrer en CSV".', + 'clock (7:30)' => 'Horloge (7:30)', 'close' => 'Fermer', 'color' => 'Couleur', 'confirm' => 'Confirmer', @@ -89,6 +91,7 @@ 'day' => 'Jour', 'day equivalent' => 'Équivalent jour', 'days' => 'Jours', + 'decimal hours (7.50)' => 'Heures décimales (7.50)', 'default annual entitlement' => 'Quota annuel par défaut', 'default overview' => 'Vue par défaut', 'default: :value days' => 'Valeur par défaut : :value jours', @@ -341,6 +344,7 @@ 'this shortcut is not supported.' => 'Ce raccourci n’est pas pris en charge.', 'time' => 'Temps', 'time balance' => 'Solde du temps', + 'time display' => 'Affichage du temps', 'time span' => 'Plage horaire', 'timezone' => 'Fuseau horaire', 'to import data from clockify, you must first export it. Simply follow the step-by-step instructions below.' => 'Pour importer des données depuis Clockify, vous devez d\'abord les exporter. Suivez simplement les instructions ci-dessous.', diff --git a/lang/it/app.php b/lang/it/app.php index 89e4feb..67fcd9f 100644 --- a/lang/it/app.php +++ b/lang/it/app.php @@ -54,9 +54,11 @@ 'chinese' => 'Cinese', 'choose how fractional days are rounded.' => 'Scegli come arrotondare i giorni frazionari.', 'choose the appearance of the application.' => 'Scegli l\'aspetto dell\'applicazione.', + 'choose how durations are shown' => 'Scegli come vengono mostrate le durate.', 'choose whether to show vacation steps' => 'Scegli se mostrare i passaggi per le ferie.', 'choose your mode' => 'Scegli la tua modalità.', 'click "export" at the top of the table and choose "save as csv".' => 'Clicca "Esporta" in cima alla tabella e scegli "Salva come CSV".', + 'clock (7:30)' => 'Orario (7:30)', 'close' => 'Chiudi', 'color' => 'Colore', 'confirm' => 'Conferma', @@ -89,6 +91,7 @@ 'day' => 'Giorno', 'day equivalent' => 'Equivalente giorno', 'days' => 'Giorni', + 'decimal hours (7.50)' => 'Ore decimali (7.50)', 'default annual entitlement' => 'Maturazione annuale predefinita', 'default overview' => 'Panoramica predefinita', 'default: :value days' => 'Predefinito: :value giorni', @@ -341,6 +344,7 @@ 'this shortcut is not supported.' => 'Questa scorciatoia non è supportata.', 'time' => 'Tempo', 'time balance' => 'Saldo ore', + 'time display' => 'Formato orario', 'time span' => 'Intervallo di tempo', 'timezone' => 'Fuso orario', 'to import data from clockify, you must first export it. Simply follow the step-by-step instructions below.' => 'Per importare dati da Clockify, devi prima esportarli. Segui semplicemente le istruzioni passo-passo qui sotto.', diff --git a/lang/pt_BR/app.php b/lang/pt_BR/app.php index d907a76..215b826 100644 --- a/lang/pt_BR/app.php +++ b/lang/pt_BR/app.php @@ -54,9 +54,11 @@ 'chinese' => 'Chinês', 'choose how fractional days are rounded.' => 'Escolha como os dias fracionados são arredondados.', 'choose the appearance of the application.' => 'Escolha a aparência do aplicativo.', + 'choose how durations are shown' => 'Escolha como as durações são exibidas.', 'choose whether to show vacation steps' => 'Escolha se deseja mostrar as etapas de férias.', 'choose your mode' => 'Escolha seu modo.', 'click "export" at the top of the table and choose "save as csv".' => 'Clique em "Exportar" no topo da tabela e escolha "Salvar como CSV".', + 'clock (7:30)' => 'Relógio (7:30)', 'close' => 'Fechar', 'color' => 'Cor', 'confirm' => 'Confirmar', @@ -89,6 +91,7 @@ 'day' => 'Dia', 'day equivalent' => 'Equivalente em dias', 'days' => 'Dias', + 'decimal hours (7.50)' => 'Horas decimais (7.50)', 'default annual entitlement' => 'Direito anual padrão', 'default overview' => 'Visão geral padrão', 'default: :value days' => 'Padrão: :value dias', @@ -341,6 +344,7 @@ 'this shortcut is not supported.' => 'Este atalho não é suportado.', 'time' => 'Tempo', 'time balance' => 'Saldo de Horas', + 'time display' => 'Formato de tempo', 'time span' => 'Período de tempo', 'timezone' => 'Fuso Horário', 'to import data from clockify, you must first export it. Simply follow the step-by-step instructions below.' => 'Para importar dados do Clockify, você deve primeiro exportá-los. Basta seguir as instruções passo a passo abaixo.', diff --git a/lang/zh_CN/app.php b/lang/zh_CN/app.php index 59bdb4a..201fd5f 100644 --- a/lang/zh_CN/app.php +++ b/lang/zh_CN/app.php @@ -54,9 +54,11 @@ 'chinese' => '中文', 'choose how fractional days are rounded.' => '控制休假日的小数步长。', 'choose the appearance of the application.' => '选择应用程序的外观。', + 'choose how durations are shown' => '选择时长的显示方式。', 'choose whether to show vacation steps' => '选择是否显示休假步骤。', 'choose your mode' => '选择你的模式。', 'click "export" at the top of the table and choose "save as csv".' => '点击表格上方的“导出”,然后选择“另存为 CSV”。', + 'clock (7:30)' => '时钟格式 (7:30)', 'close' => '关闭', 'color' => '颜色', 'confirm' => '确认', @@ -89,6 +91,7 @@ 'day' => '天', 'day equivalent' => '折算天数', 'days' => '天', + 'decimal hours (7.50)' => '十进制小时 (7.50)', 'default annual entitlement' => '默认年假配额', 'default overview' => '默认概览', 'default: :value days' => '默认值::value 天', @@ -341,6 +344,7 @@ 'this shortcut is not supported.' => '该快捷键不受支持。', 'time' => '时间', 'time balance' => '时间结余', + 'time display' => '时间显示', 'time span' => '时间段', 'timezone' => '时区', 'to import data from clockify, you must first export it. Simply follow the step-by-step instructions below.' => '若要从 Clockify 导入数据,您必须先导出数据。只需遵循以下逐步指示即可。', diff --git a/resources/js/Components/TimestampTypeBadge.vue b/resources/js/Components/TimestampTypeBadge.vue index ebfdcd4..9997ece 100644 --- a/resources/js/Components/TimestampTypeBadge.vue +++ b/resources/js/Components/TimestampTypeBadge.vue @@ -7,7 +7,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger } from '@/Components/ui/dropdown-menu' -import { secToFormat } from '@/lib/utils' +import { formatDurationWithUnit } from '@/lib/utils' import { GetTimeProjectDetails } from '@/types' import { Link } from '@inertiajs/vue3' import { @@ -84,7 +84,7 @@ const badgeDetails = { const { title: badgeTitle, icon: badgeIcon, color: badgeColor } = badgeDetails[props.type] || badgeDetails.default -const durationLabel = computed(() => secToFormat(props.duration ?? 0, true, true, true)) +const durationLabel = computed(() => formatDurationWithUnit(props.duration ?? 0))