From b88e19e3119802d65d9239749a04bbebd302f81b Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Wed, 22 Nov 2023 22:13:00 +0200 Subject: [PATCH] Split code more into modules --- src/input.ts | 148 ++++++++++++++++++++++++++++++++ src/main.ts | 160 +---------------------------------- src/output.ts | 32 +++++++ src/types/WtcPromptResult.ts | 14 +++ src/ui.ts | 9 ++ 5 files changed, 206 insertions(+), 157 deletions(-) create mode 100644 src/input.ts create mode 100644 src/output.ts create mode 100644 src/types/WtcPromptResult.ts create mode 100644 src/ui.ts diff --git a/src/input.ts b/src/input.ts new file mode 100644 index 0000000..4b250f1 --- /dev/null +++ b/src/input.ts @@ -0,0 +1,148 @@ +import chalk from 'chalk'; +import { Duration } from 'dayjs/plugin/duration'; +import getConfig from './config'; +import { parseDuration, parseTimestamp } from './parse'; +import * as readline from 'readline/promises'; +import { formatDuration, formatTime } from './format'; +import dayjs, { Dayjs } from 'dayjs'; +import { WtcPromptResult } from './types/WtcPromptResult'; +import duration from 'dayjs/plugin/duration.js'; + +dayjs.extend(duration); + +const { error } = console; + +const input = async (): Promise => { + const { defaults, askInput } = getConfig(); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + let startedAt: Dayjs | undefined = undefined; + let stoppedAt: Dayjs | undefined = undefined; + let stoppedWorking = false; + + try { + // Get work day duration + let workDayDuration: Duration | undefined = undefined; + + if (askInput.workDayLength) { + const durationAnswer = await rl.question( + `How long is your work day today, excluding the lunch break? [${formatDuration( + defaults.workDayDuration, + true, + )}] `, + ); + if (durationAnswer !== '') { + workDayDuration = parseDuration(durationAnswer); + if (workDayDuration.asMinutes() <= 0) { + error( + chalk.red( + `Failed to parse ${durationAnswer} to duration, using default work day duration ${formatDuration( + defaults.workDayDuration, + true, + )}`, + ), + ); + workDayDuration = undefined; + } + } + } + + if (!workDayDuration) { + workDayDuration = defaults.workDayDuration; + } + + if (askInput.startTime) { + const startTimeAnswer = await rl.question( + `What time did you start work today? [${formatTime(defaults.startTime)}] `, + ); + if (startTimeAnswer !== '') { + startedAt = parseTimestamp(startTimeAnswer); + if (!startedAt.isValid()) { + error( + chalk.red( + `Failed to parse ${startTimeAnswer} to time, using default start time ${formatTime( + defaults.startTime, + )}`, + ), + ); + } + } + } + + if (!startedAt?.isValid()) { + startedAt = defaults.startTime; + } + + if (askInput.stopTime) { + const stoppedAnswer = await rl.question( + `What time did you stop working? [${formatTime(defaults.stopTime)}] `, + ); + + if (stoppedAnswer !== '') { + stoppedWorking = true; + stoppedAt = parseTimestamp(stoppedAnswer); + if (!stoppedAt.isValid()) { + error(`Failed to parse ${stoppedAnswer} to time, using current time`); + stoppedAt = dayjs(); + } + } + } + + if (!stoppedAt) { + stoppedAt = defaults.stopTime; + } + + if (stoppedAt.isSame(startedAt) || stoppedAt.isBefore(startedAt)) { + error( + chalk.red( + `Start time (${formatTime(startedAt)}) needs to be before stop time (${formatTime( + stoppedAt, + )}). Exiting`, + ), + ); + process.exit(1); + } + + let worked = dayjs.duration(stoppedAt.diff(startedAt)); + + const hadLunch = + askInput.hadLunch && (await rl.question('Did you have a lunch break? [Y/n] ')).toLowerCase() !== 'n'; + + if (hadLunch) { + worked = worked.subtract(defaults.lunchBreakDuration); + } + + // Calculate unlogged time + let loggedAnswer = await rl.question('How many hours did you log already? [00:00] '); + if (loggedAnswer === '') { + loggedAnswer = '00:00'; + } + const logged = parseDuration(loggedAnswer); + const unLogged = worked.subtract(logged); + const workLeft = workDayDuration.subtract(worked); + let workLeftMinutes = workLeft.asMinutes(); + let workedOverTime: Duration | undefined; + + if (workLeftMinutes < 0) { + workedOverTime = dayjs.duration(Math.round(workLeftMinutes * -1), 'minutes'); + } + + return { + logged, + unLogged, + startedAt, + stoppedAt, + stoppedWorking, + hadLunch, + worked, + workLeft, + }; + } finally { + rl.close(); + } +}; + +export default input; diff --git a/src/main.ts b/src/main.ts index 2181c0b..f244ada 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,163 +1,9 @@ -import chalk from 'chalk'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; -import dayjs, { Dayjs } from 'dayjs'; -import * as readline from 'readline/promises'; -import { formatDuration, formatTime, formatTimestamp, getHoursRoundedStr } from './format.js'; -import duration, { Duration } from 'dayjs/plugin/duration.js'; -import { parseDuration, parseTimestamp } from './parse.js'; -import getConfig from './config.js'; - -dayjs.extend(duration); - -const { log, error } = console; - -const ui = async () => { - const { defaults, askInput } = getConfig(); - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - let startedAt: Dayjs | undefined = undefined; - let stoppedAt: Dayjs | undefined = undefined; - let stoppedWorking = false; - - try { - // Get work day duration - let workDayDuration: Duration | undefined = undefined; - - if (askInput.workDayLength) { - const durationAnswer = await rl.question( - `How long is your work day today, excluding the lunch break? [${formatDuration( - defaults.workDayDuration, - true, - )}] `, - ); - if (durationAnswer !== '') { - workDayDuration = parseDuration(durationAnswer); - if (workDayDuration.asMinutes() <= 0) { - error( - chalk.red( - `Failed to parse ${durationAnswer} to duration, using default work day duration ${formatDuration( - defaults.workDayDuration, - true, - )}`, - ), - ); - workDayDuration = undefined; - } - } - } - - if (!workDayDuration) { - workDayDuration = defaults.workDayDuration; - } - - if (askInput.startTime) { - const startTimeAnswer = await rl.question( - `What time did you start work today? [${formatTime(defaults.startTime)}] `, - ); - if (startTimeAnswer !== '') { - startedAt = parseTimestamp(startTimeAnswer); - if (!startedAt.isValid()) { - error( - chalk.red( - `Failed to parse ${startTimeAnswer} to time, using default start time ${formatTime( - defaults.startTime, - )}`, - ), - ); - } - } - } - - if (!startedAt?.isValid()) { - startedAt = defaults.startTime; - } - - if (askInput.stopTime) { - const stoppedAnswer = await rl.question( - `What time did you stop working? [${formatTime(defaults.stopTime)}] `, - ); - - if (stoppedAnswer !== '') { - stoppedWorking = true; - stoppedAt = parseTimestamp(stoppedAnswer); - if (!stoppedAt.isValid()) { - error(`Failed to parse ${stoppedAnswer} to time, using current time`); - stoppedAt = dayjs(); - } - } - } - - if (!stoppedAt) { - stoppedAt = defaults.stopTime; - } - - if (stoppedAt.isSame(startedAt) || stoppedAt.isBefore(startedAt)) { - error( - chalk.red( - `Start time (${formatTime(startedAt)}) needs to be before stop time (${formatTime( - stoppedAt, - )}). Exiting`, - ), - ); - process.exit(1); - } - - let worked = dayjs.duration(stoppedAt.diff(startedAt)); - - const hadLunch = - askInput.hadLunch && (await rl.question('Did you have a lunch break? [Y/n] ')).toLowerCase() !== 'n'; - - if (hadLunch) { - worked = worked.subtract(defaults.lunchBreakDuration); - } - - // Calculate unlogged time - let loggedAnswer = await rl.question('How many hours did you log already? [00:00] '); - if (loggedAnswer === '') { - loggedAnswer = '00:00'; - } - const logged = parseDuration(loggedAnswer); - const unLogged = worked.subtract(logged); - - // Log result - log(); - log('Started working at:', formatTimestamp(startedAt)); - log((stoppedWorking ? 'Stopped working' : 'Hours calculated') + ' at:', formatTimestamp(stoppedAt)); - log('Worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked))); - - if (unLogged.asMinutes() == 0) { - log('Unlogged today:', chalk.green('none')); - } else if (unLogged.asMinutes() > 0) { - log('Unlogged today:', chalk.red(formatDuration(unLogged)), chalk.yellow(getHoursRoundedStr(unLogged))); - } else if (unLogged.asMinutes() < 0) { - log( - chalk.red(`You have logged ${formatDuration(unLogged)} more than you worked today!`), - chalk.yellow(getHoursRoundedStr(unLogged)), - ); - } - - const workLeft = workDayDuration.subtract(worked); - const workLeftMinutes = workLeft.asMinutes(); - if (workLeftMinutes > 0) { - log('You still have to work', chalk.green(formatDuration(workLeft)), 'more today'); - } else if (workLeft.asMinutes() < 0) { - log( - 'You worked', - chalk.green( - formatDuration(dayjs.duration(Math.round(workLeftMinutes * -1), 'minutes')), - 'overtime today', - ), - ); - } - } finally { - rl.close(); - } -}; +import ui from './ui.js'; +// Process args. Yargs will exit if it detects help or version yargs(hideBin(process.argv)).usage('Work time calculator').alias('help', 'h').alias('version', 'v').argv; +// Run UI if help or version is not prompted ui(); diff --git a/src/output.ts b/src/output.ts new file mode 100644 index 0000000..a45fbdc --- /dev/null +++ b/src/output.ts @@ -0,0 +1,32 @@ +import chalk from 'chalk'; +import { formatDuration, formatTimestamp, getHoursRoundedStr } from './format'; +import { WtcPromptResult } from './types/WtcPromptResult'; + +const { log } = console; + +const output = (result: WtcPromptResult) => { + const { startedAt, stoppedAt, stoppedWorking, worked, unLogged, workLeft, workedOverTime } = result; + log(); + log('Started working at:', formatTimestamp(startedAt)); + log((stoppedWorking ? 'Stopped working' : 'Hours calculated') + ' at:', formatTimestamp(stoppedAt)); + log('Worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked))); + + if (unLogged.asMinutes() == 0) { + log('Unlogged today:', chalk.green('none')); + } else if (unLogged.asMinutes() > 0) { + log('Unlogged today:', chalk.red(formatDuration(unLogged)), chalk.yellow(getHoursRoundedStr(unLogged))); + } else if (unLogged.asMinutes() < 0) { + log( + chalk.red(`You have logged ${formatDuration(unLogged)} more than you worked today!`), + chalk.yellow(getHoursRoundedStr(unLogged)), + ); + } + + if (workLeft.asMinutes() > 0) { + log('You still have to work', chalk.green(formatDuration(workLeft)), 'more today'); + } else if (workedOverTime) { + log('You worked', chalk.green(formatDuration(workedOverTime), 'overtime today')); + } +}; + +export default output; diff --git a/src/types/WtcPromptResult.ts b/src/types/WtcPromptResult.ts new file mode 100644 index 0000000..ed3ee0f --- /dev/null +++ b/src/types/WtcPromptResult.ts @@ -0,0 +1,14 @@ +import { Dayjs } from 'dayjs'; +import { Duration } from 'dayjs/plugin/duration'; + +export interface WtcPromptResult { + startedAt: Dayjs; + stoppedAt: Dayjs; + stoppedWorking: boolean; + logged: Duration; + unLogged: Duration; + hadLunch: boolean; + worked: Duration; + workLeft: Duration; + workedOverTime?: Duration; +} diff --git a/src/ui.ts b/src/ui.ts new file mode 100644 index 0000000..1cc4bc5 --- /dev/null +++ b/src/ui.ts @@ -0,0 +1,9 @@ +import input from './input.js'; +import output from './output.js'; + +const ui = async () => { + const result = await input(); + output(result); +}; + +export default ui;