close button and CSV import parsing for Clockify/Harvest

Close button did nothing when "close to tray" was disabled - the
onCloseRequested handler lacked an explicit destroy call for the
non-tray path.

Clockify CSV import threw RangeError because locale-dependent date
formats (MM/DD/YYYY, DD.MM.YYYY, 12h time) were passed straight
to the Date constructor. Added flexible date/time parsers that
handle all Clockify export variants without relying on Date parsing.

Added dedicated Clockify mapper that prefers Duration (decimal)
column and a new Harvest CSV importer (date + decimal hours, no
start/end times).

Bump version to 1.0.1.
This commit is contained in:
2026-02-21 14:56:53 +02:00
parent 7486cbdbb0
commit b1a0c03988
6 changed files with 162 additions and 28 deletions
+7 -2
View File
@@ -1541,7 +1541,7 @@ import AppSelect from '../components/AppSelect.vue'
import AppShortcutRecorder from '../components/AppShortcutRecorder.vue'
import AppTimePicker from '../components/AppTimePicker.vue'
import { LOCALES, getCurrencies, getCurrencySymbol } from '../utils/locale'
import { parseCSV, mapTogglCSV, mapGenericCSV, type ImportEntry } from '../utils/import'
import { parseCSV, mapTogglCSV, mapClockifyCSV, mapHarvestCSV, mapGenericCSV, type ImportEntry } from '../utils/import'
import { TIMER_FONTS, loadGoogleFont, loadAndApplyTimerFont } from '../utils/fonts'
import { UI_FONTS, loadUIFont } from '../utils/uiFonts'
import { useFocusTrap } from '../utils/focusTrap'
@@ -1982,6 +1982,7 @@ const isImporting = ref(false)
const importFormats = [
{ label: 'Toggl CSV', value: 'toggl' },
{ label: 'Clockify CSV', value: 'clockify' },
{ label: 'Harvest CSV', value: 'harvest' },
{ label: 'Generic CSV', value: 'generic' },
{ label: 'ZeroClock JSON', value: 'json' },
]
@@ -2379,8 +2380,12 @@ async function executeImport() {
const rows = parseCSV(importFileContent.value)
let entries: ImportEntry[]
if (importFormat.value === 'toggl' || importFormat.value === 'clockify') {
if (importFormat.value === 'toggl') {
entries = mapTogglCSV(rows)
} else if (importFormat.value === 'clockify') {
entries = mapClockifyCSV(rows)
} else if (importFormat.value === 'harvest') {
entries = mapHarvestCSV(rows)
} else {
entries = mapGenericCSV(rows, { project: 0, description: 1, start_time: 2, duration: 3, client: -1, task: -1 })
}