Add i18n support and translations for finnish language
This commit is contained in:
parent
b88e19e311
commit
4b8c8be226
9 changed files with 239 additions and 84 deletions
10
config.toml
10
config.toml
|
@ -5,15 +5,24 @@
|
||||||
# You can place your configuration file in $XDG_CONFIG_HOME/wtc/config.toml,
|
# You can place your configuration file in $XDG_CONFIG_HOME/wtc/config.toml,
|
||||||
# usually ~/.config/wtc/config.toml
|
# usually ~/.config/wtc/config.toml
|
||||||
|
|
||||||
|
# The language of the application.
|
||||||
|
# Currently supported languages are "en", "fi"
|
||||||
|
language = "en"
|
||||||
|
|
||||||
# This section is for default values for inputs
|
# This section is for default values for inputs
|
||||||
[defaults]
|
[defaults]
|
||||||
|
|
||||||
# Leave empty if you don't have an unpaid lunch break
|
# Leave empty if you don't have an unpaid lunch break
|
||||||
# or if you normally log your lunch break hours
|
# or if you normally log your lunch break hours
|
||||||
lunchBreakDuration = "00:30"
|
lunchBreakDuration = "00:30"
|
||||||
|
|
||||||
# Your work day duration
|
# Your work day duration
|
||||||
workDayDuration = "07:30"
|
workDayDuration = "07:30"
|
||||||
|
|
||||||
# The time you start working
|
# The time you start working
|
||||||
|
|
||||||
startTime = "08:00"
|
startTime = "08:00"
|
||||||
|
|
||||||
# The time you stop working. Can either be "now" or a time
|
# The time you stop working. Can either be "now" or a time
|
||||||
stopTime = "now"
|
stopTime = "now"
|
||||||
|
|
||||||
|
@ -25,5 +34,6 @@ workDayDuration = true
|
||||||
startTime = true
|
startTime = true
|
||||||
stopTime = true
|
stopTime = true
|
||||||
logged = true
|
logged = true
|
||||||
|
|
||||||
# It is assumed that you didn't have lunch if this is false
|
# It is assumed that you didn't have lunch if this is false
|
||||||
hadLunch = true
|
hadLunch = true
|
||||||
|
|
|
@ -2,27 +2,11 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { xdgConfig } from 'xdg-basedir';
|
import { xdgConfig } from 'xdg-basedir';
|
||||||
import toml from '@iarna/toml';
|
import toml from '@iarna/toml';
|
||||||
import { Dayjs } from 'dayjs';
|
|
||||||
import { Duration } from 'dayjs/plugin/duration.js';
|
|
||||||
import { parseDuration, parseTimestamp } from './parse.js';
|
import { parseDuration, parseTimestamp } from './parse.js';
|
||||||
|
import WtcConfig from './types/WtcConfig.js';
|
||||||
|
import Language from './types/Language.js';
|
||||||
|
|
||||||
interface Config {
|
interface RawConfig extends Omit<WtcConfig, 'defaults'> {
|
||||||
defaults: {
|
|
||||||
workDayDuration: Duration;
|
|
||||||
lunchBreakDuration: Duration;
|
|
||||||
startTime: Dayjs;
|
|
||||||
stopTime: Dayjs;
|
|
||||||
};
|
|
||||||
askInput: {
|
|
||||||
workDayLength: boolean;
|
|
||||||
startTime: boolean;
|
|
||||||
stopTime: boolean;
|
|
||||||
logged: boolean;
|
|
||||||
hadLunch: boolean;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RawConfig extends Omit<Config, 'defaults'> {
|
|
||||||
defaults: {
|
defaults: {
|
||||||
workDayDuration: string;
|
workDayDuration: string;
|
||||||
lunchBreakDuration: string;
|
lunchBreakDuration: string;
|
||||||
|
@ -32,6 +16,7 @@ interface RawConfig extends Omit<Config, 'defaults'> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultConfig: RawConfig = {
|
const defaultConfig: RawConfig = {
|
||||||
|
language: Language.en,
|
||||||
defaults: {
|
defaults: {
|
||||||
workDayDuration: '07:30',
|
workDayDuration: '07:30',
|
||||||
lunchBreakDuration: '00:30',
|
lunchBreakDuration: '00:30',
|
||||||
|
@ -47,9 +32,9 @@ const defaultConfig: RawConfig = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const getConfig = (): Config => {
|
const getConfig = (): WtcConfig => {
|
||||||
const configDir = xdgConfig || path.join(process.env.HOME ?? './', '.config');
|
const configDir = xdgConfig || path.join(process.env.HOME ?? './', '.config');
|
||||||
let configFilePath = path.join(configDir, 'wct', 'config.toml');
|
let configFilePath = path.join(configDir, 'wtc', 'config.toml');
|
||||||
|
|
||||||
let configData: RawConfig;
|
let configData: RawConfig;
|
||||||
if (fs.existsSync(configFilePath)) {
|
if (fs.existsSync(configFilePath)) {
|
||||||
|
@ -59,6 +44,7 @@ const getConfig = (): Config => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
language: configData.language ?? defaultConfig.language,
|
||||||
defaults: {
|
defaults: {
|
||||||
workDayDuration: parseDuration(
|
workDayDuration: parseDuration(
|
||||||
configData.defaults.workDayDuration ?? defaultConfig.defaults.workDayDuration,
|
configData.defaults.workDayDuration ?? defaultConfig.defaults.workDayDuration,
|
||||||
|
|
|
@ -1,28 +1,44 @@
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { Duration } from 'dayjs/plugin/duration.js';
|
import { Duration } from 'dayjs/plugin/duration.js';
|
||||||
|
import Language from './types/Language';
|
||||||
|
import { MessageKey, message } from './i18n';
|
||||||
|
|
||||||
export const formatTimestamp = (timestamp: Dayjs): string => timestamp.format('YYYY-MM-DD HH:mm');
|
export const formatTimestamp = (timestamp: Dayjs): string => timestamp.format('YYYY-MM-DD HH:mm');
|
||||||
|
|
||||||
export const formatTime = (time: Dayjs): string => time.format('HH:mm');
|
export const formatTime = (time: Dayjs): string => time.format('HH:mm');
|
||||||
|
|
||||||
export const formatDuration = (duration: Duration, short?: boolean): string => {
|
export const formatDuration =
|
||||||
if (duration.hours() === 0 && duration.minutes() === 0) {
|
(language: Language) =>
|
||||||
return 'none';
|
(duration: Duration, short?: boolean): string => {
|
||||||
}
|
if (duration.hours() === 0 && duration.minutes() === 0) {
|
||||||
|
return 'none';
|
||||||
|
}
|
||||||
|
|
||||||
const formatString = short
|
let formatString;
|
||||||
? 'HH:mm'
|
|
||||||
: duration.hours() > 0 && duration.minutes() > 0
|
|
||||||
? `H [hour${duration.hours() > 1 ? 's' : ''} and] m [minute${duration.minutes() > 1 ? 's' : ''}]`
|
|
||||||
: duration.hours() > 0
|
|
||||||
? `H [hour${duration.hours() > 1 ? 's' : ''}]`
|
|
||||||
: `m [minute${duration.minutes() > 1 ? 's' : ''}]`;
|
|
||||||
|
|
||||||
return duration.format(formatString);
|
if (short) {
|
||||||
};
|
formatString = 'HH:mm';
|
||||||
|
} else if (language === Language.fi) {
|
||||||
|
formatString =
|
||||||
|
duration.hours() > 0 && duration.minutes() > 0
|
||||||
|
? `H [tunti${duration.hours() > 1 ? 'a' : ''} ja] m [minuutti${duration.minutes() > 1 ? 'a' : ''}]`
|
||||||
|
: duration.hours() > 0
|
||||||
|
? `H [tunti${duration.hours() > 1 ? 'a' : ''}]`
|
||||||
|
: `m [minutti${duration.minutes() > 1 ? 'a' : ''}]`;
|
||||||
|
} else {
|
||||||
|
formatString =
|
||||||
|
duration.hours() > 0 && duration.minutes() > 0
|
||||||
|
? `H [hour${duration.hours() > 1 ? 's' : ''} and] m [minute${duration.minutes() > 1 ? 's' : ''}]`
|
||||||
|
: duration.hours() > 0
|
||||||
|
? `H [hour${duration.hours() > 1 ? 's' : ''}]`
|
||||||
|
: `m [minute${duration.minutes() > 1 ? 's' : ''}]`;
|
||||||
|
}
|
||||||
|
|
||||||
export const getHoursRoundedStr = (duration: Duration) =>
|
return duration.format(formatString);
|
||||||
`(${getHoursRounded(duration)} as hours rounded to next even 15 minutes)`;
|
};
|
||||||
|
|
||||||
|
export const getHoursRoundedStr = (language: Language) => (duration: Duration) =>
|
||||||
|
`(${getHoursRounded(duration)} ${message(language)(MessageKey.hoursRounded)})`;
|
||||||
|
|
||||||
const getHoursRounded = (duration: Duration) => {
|
const getHoursRounded = (duration: Duration) => {
|
||||||
// Round up to the next multiple of 15
|
// Round up to the next multiple of 15
|
||||||
|
|
120
src/i18n.ts
Normal file
120
src/i18n.ts
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import Language from './types/Language';
|
||||||
|
|
||||||
|
export enum MessageKey {
|
||||||
|
promptWorkDayDuration,
|
||||||
|
promptStartTime,
|
||||||
|
promptStopTime,
|
||||||
|
parseTimeFailed,
|
||||||
|
startTimeBeforeStopTimeError,
|
||||||
|
promptLunchBreak,
|
||||||
|
promptLogged,
|
||||||
|
none,
|
||||||
|
startedWorking,
|
||||||
|
stoppedWorking,
|
||||||
|
workedToday,
|
||||||
|
loggedOver,
|
||||||
|
workedOvertime,
|
||||||
|
workLeft,
|
||||||
|
hoursCalculated,
|
||||||
|
klo,
|
||||||
|
unloggedToday,
|
||||||
|
hoursRounded,
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages: Record<MessageKey, Record<Language, string>> = {
|
||||||
|
[MessageKey.promptWorkDayDuration]: {
|
||||||
|
[Language.en]: 'How long is your work day today, excluding the lunch break? [{0}]: ',
|
||||||
|
[Language.fi]: 'Kuinka pitkä työpäiväsi on tänään, poisluettuna lounastauko? [{0}]: ',
|
||||||
|
},
|
||||||
|
[MessageKey.promptStartTime]: {
|
||||||
|
[Language.en]: 'What time did you start work today? [{0}]: ',
|
||||||
|
[Language.fi]: 'Mihin aikaan aloitit työskentelyn tänään? [{0}]: ',
|
||||||
|
},
|
||||||
|
[MessageKey.promptStopTime]: {
|
||||||
|
[Language.en]: "What time did you stop working? If you didn't stop yet, leave this empty: ",
|
||||||
|
[Language.fi]: 'Mihin aikaan lopetit työskentelyn? Jos et lopettanut vielä, jätä tämä tyhjäksi: ',
|
||||||
|
},
|
||||||
|
[MessageKey.parseTimeFailed]: {
|
||||||
|
[Language.en]: 'Failed to parse time "{0}", using default value "{1}"',
|
||||||
|
[Language.fi]: 'Ajan "{0}" parsiminen epäonnistui, käytetään oletusasetusta "{1}"',
|
||||||
|
},
|
||||||
|
[MessageKey.startTimeBeforeStopTimeError]: {
|
||||||
|
[Language.en]: 'Start time ({0}) needs to be before stop time ({1}). Exiting',
|
||||||
|
[Language.fi]: 'Aloitusaika ({0}) pitää olla ennen lopetusaikaa ({1}). Ohjelma sammuu',
|
||||||
|
},
|
||||||
|
[MessageKey.promptLunchBreak]: {
|
||||||
|
[Language.en]: 'Did you have a lunch break? [y/N]: ',
|
||||||
|
[Language.fi]: 'Piditkö jo lounastauon? [k/E]: ',
|
||||||
|
},
|
||||||
|
[MessageKey.promptLogged]: {
|
||||||
|
[Language.en]: 'How many hours did you log already? [00:00] ',
|
||||||
|
[Language.fi]: 'Kuinka monta tuntia kirjasit jo? [00:00] ',
|
||||||
|
},
|
||||||
|
[MessageKey.none]: {
|
||||||
|
[Language.en]: 'None',
|
||||||
|
[Language.fi]: 'Ei yhtään',
|
||||||
|
},
|
||||||
|
[MessageKey.startedWorking]: {
|
||||||
|
[Language.en]: 'Started working:',
|
||||||
|
[Language.fi]: 'Aloitit työskentelyn:',
|
||||||
|
},
|
||||||
|
[MessageKey.stoppedWorking]: {
|
||||||
|
[Language.en]: 'Stopped working',
|
||||||
|
[Language.fi]: 'Lopetit työskentelyn',
|
||||||
|
},
|
||||||
|
[MessageKey.hoursCalculated]: {
|
||||||
|
[Language.en]: 'Hours calculated',
|
||||||
|
[Language.fi]: 'Tunnit laskettu',
|
||||||
|
},
|
||||||
|
[MessageKey.workedToday]: {
|
||||||
|
[Language.en]: 'Worked today:',
|
||||||
|
[Language.fi]: 'Tänään työskennelty:',
|
||||||
|
},
|
||||||
|
[MessageKey.workedOvertime]: {
|
||||||
|
[Language.en]: 'You worked {0} overtime today',
|
||||||
|
[Language.fi]: 'Olet tehnyt {0} ylitöitä tänään',
|
||||||
|
},
|
||||||
|
[MessageKey.loggedOver]: {
|
||||||
|
[Language.en]: 'You have logged {0} more than you worked today!',
|
||||||
|
[Language.fi]: 'Olet kirjannut {0} enemmän kuin olet työskennellyt!',
|
||||||
|
},
|
||||||
|
[MessageKey.workLeft]: {
|
||||||
|
[Language.en]: 'You still have to work {0} more today',
|
||||||
|
[Language.fi]: 'Sinun pitää työskennellä tänään vielä {0} lisää',
|
||||||
|
},
|
||||||
|
[MessageKey.klo]: {
|
||||||
|
[Language.en]: 'at',
|
||||||
|
[Language.fi]: 'klo',
|
||||||
|
},
|
||||||
|
[MessageKey.unloggedToday]: {
|
||||||
|
[Language.en]: 'Unlogged today:',
|
||||||
|
[Language.fi]: 'Kirjaamattomia tänään:',
|
||||||
|
},
|
||||||
|
[MessageKey.hoursRounded]: {
|
||||||
|
[Language.en]: 'as hours rounded to next even 15 minutes',
|
||||||
|
[Language.fi]: 'tunteina pyöristettynä seuraavaan 15 minuuttiin',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a function to fetch messages for a given language
|
||||||
|
* @param language The language to get the messages for
|
||||||
|
*/
|
||||||
|
export const message =
|
||||||
|
(language: Language) =>
|
||||||
|
/**
|
||||||
|
* Get a message for a fiven key
|
||||||
|
* @param key The key of the message
|
||||||
|
*/
|
||||||
|
(key: keyof typeof messages, ...params: string[]) => {
|
||||||
|
let result = messages[key][language];
|
||||||
|
if (!result) {
|
||||||
|
throw `Unknown language: ${language}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace parameters in the template
|
||||||
|
for (let i = 0; i < params.length; i++) {
|
||||||
|
result = result.replace(new RegExp(`\\{${i}\\}`, 'g'), params[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
52
src/input.ts
52
src/input.ts
|
@ -7,13 +7,17 @@ import { formatDuration, formatTime } from './format';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { WtcPromptResult } from './types/WtcPromptResult';
|
import { WtcPromptResult } from './types/WtcPromptResult';
|
||||||
import duration from 'dayjs/plugin/duration.js';
|
import duration from 'dayjs/plugin/duration.js';
|
||||||
|
import WtcConfig from './types/WtcConfig';
|
||||||
|
import { MessageKey, message } from './i18n';
|
||||||
|
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
|
|
||||||
const { error } = console;
|
const { error } = console;
|
||||||
|
|
||||||
const input = async (): Promise<WtcPromptResult> => {
|
const input = async (config: WtcConfig): Promise<WtcPromptResult> => {
|
||||||
const { defaults, askInput } = getConfig();
|
const msg = message(config.language);
|
||||||
|
const fmtDuration = formatDuration(config.language);
|
||||||
|
const { defaults, askInput } = config;
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout,
|
output: process.stdout,
|
||||||
|
@ -29,20 +33,14 @@ const input = async (): Promise<WtcPromptResult> => {
|
||||||
|
|
||||||
if (askInput.workDayLength) {
|
if (askInput.workDayLength) {
|
||||||
const durationAnswer = await rl.question(
|
const durationAnswer = await rl.question(
|
||||||
`How long is your work day today, excluding the lunch break? [${formatDuration(
|
msg(MessageKey.promptWorkDayDuration, fmtDuration(defaults.workDayDuration, true)),
|
||||||
defaults.workDayDuration,
|
|
||||||
true,
|
|
||||||
)}] `,
|
|
||||||
);
|
);
|
||||||
if (durationAnswer !== '') {
|
if (durationAnswer !== '') {
|
||||||
workDayDuration = parseDuration(durationAnswer);
|
workDayDuration = parseDuration(durationAnswer);
|
||||||
if (workDayDuration.asMinutes() <= 0) {
|
if (workDayDuration.asMinutes() <= 0) {
|
||||||
error(
|
error(
|
||||||
chalk.red(
|
chalk.red(
|
||||||
`Failed to parse ${durationAnswer} to duration, using default work day duration ${formatDuration(
|
msg(MessageKey.parseTimeFailed, durationAnswer, fmtDuration(defaults.workDayDuration)),
|
||||||
defaults.workDayDuration,
|
|
||||||
true,
|
|
||||||
)}`,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
workDayDuration = undefined;
|
workDayDuration = undefined;
|
||||||
|
@ -55,19 +53,11 @@ const input = async (): Promise<WtcPromptResult> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (askInput.startTime) {
|
if (askInput.startTime) {
|
||||||
const startTimeAnswer = await rl.question(
|
const startTimeAnswer = await rl.question(msg(MessageKey.promptStartTime, formatTime(defaults.startTime)));
|
||||||
`What time did you start work today? [${formatTime(defaults.startTime)}] `,
|
|
||||||
);
|
|
||||||
if (startTimeAnswer !== '') {
|
if (startTimeAnswer !== '') {
|
||||||
startedAt = parseTimestamp(startTimeAnswer);
|
startedAt = parseTimestamp(startTimeAnswer);
|
||||||
if (!startedAt.isValid()) {
|
if (!startedAt.isValid()) {
|
||||||
error(
|
error(chalk.red(msg(MessageKey.parseTimeFailed, startTimeAnswer, formatTime(defaults.startTime))));
|
||||||
chalk.red(
|
|
||||||
`Failed to parse ${startTimeAnswer} to time, using default start time ${formatTime(
|
|
||||||
defaults.startTime,
|
|
||||||
)}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,16 +67,13 @@ const input = async (): Promise<WtcPromptResult> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (askInput.stopTime) {
|
if (askInput.stopTime) {
|
||||||
const stoppedAnswer = await rl.question(
|
const stoppedAnswer = await rl.question(msg(MessageKey.promptStopTime, formatTime(defaults.stopTime)));
|
||||||
`What time did you stop working? [${formatTime(defaults.stopTime)}] `,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (stoppedAnswer !== '') {
|
if (stoppedAnswer !== '') {
|
||||||
stoppedWorking = true;
|
stoppedWorking = true;
|
||||||
stoppedAt = parseTimestamp(stoppedAnswer);
|
stoppedAt = parseTimestamp(stoppedAnswer);
|
||||||
if (!stoppedAt.isValid()) {
|
if (!stoppedAt.isValid()) {
|
||||||
error(`Failed to parse ${stoppedAnswer} to time, using current time`);
|
error(chalk.red(msg(MessageKey.parseTimeFailed, stoppedAnswer, formatTime(defaults.stopTime))));
|
||||||
stoppedAt = dayjs();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,26 +84,25 @@ const input = async (): Promise<WtcPromptResult> => {
|
||||||
|
|
||||||
if (stoppedAt.isSame(startedAt) || stoppedAt.isBefore(startedAt)) {
|
if (stoppedAt.isSame(startedAt) || stoppedAt.isBefore(startedAt)) {
|
||||||
error(
|
error(
|
||||||
chalk.red(
|
chalk.red(msg(MessageKey.startTimeBeforeStopTimeError, formatTime(startedAt), formatTime(stoppedAt))),
|
||||||
`Start time (${formatTime(startedAt)}) needs to be before stop time (${formatTime(
|
|
||||||
stoppedAt,
|
|
||||||
)}). Exiting`,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let worked = dayjs.duration(stoppedAt.diff(startedAt));
|
let worked = dayjs.duration(stoppedAt.diff(startedAt));
|
||||||
|
|
||||||
const hadLunch =
|
let hadLunch = false;
|
||||||
askInput.hadLunch && (await rl.question('Did you have a lunch break? [Y/n] ')).toLowerCase() !== 'n';
|
if (askInput.hadLunch) {
|
||||||
|
const lunchAnswer = (await rl.question(msg(MessageKey.promptLunchBreak))).toLowerCase();
|
||||||
|
hadLunch = lunchAnswer === 'y' || lunchAnswer === 'k';
|
||||||
|
}
|
||||||
|
|
||||||
if (hadLunch) {
|
if (hadLunch) {
|
||||||
worked = worked.subtract(defaults.lunchBreakDuration);
|
worked = worked.subtract(defaults.lunchBreakDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate unlogged time
|
// Calculate unlogged time
|
||||||
let loggedAnswer = await rl.question('How many hours did you log already? [00:00] ');
|
let loggedAnswer = await rl.question(msg(MessageKey.promptLogged));
|
||||||
if (loggedAnswer === '') {
|
if (loggedAnswer === '') {
|
||||||
loggedAnswer = '00:00';
|
loggedAnswer = '00:00';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,40 @@
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { formatDuration, formatTimestamp, getHoursRoundedStr } from './format';
|
import { formatDuration, formatTimestamp, getHoursRoundedStr } from './format';
|
||||||
import { WtcPromptResult } from './types/WtcPromptResult';
|
import { WtcPromptResult } from './types/WtcPromptResult';
|
||||||
|
import { MessageKey, message } from './i18n.js';
|
||||||
|
import WtcConfig from './types/WtcConfig';
|
||||||
|
|
||||||
const { log } = console;
|
const { log } = console;
|
||||||
|
|
||||||
const output = (result: WtcPromptResult) => {
|
const output = (result: WtcPromptResult, config: WtcConfig) => {
|
||||||
|
const msg = message(config.language);
|
||||||
|
const fmtDuration = formatDuration(config.language);
|
||||||
|
const hoursRounded = getHoursRoundedStr(config.language);
|
||||||
const { startedAt, stoppedAt, stoppedWorking, worked, unLogged, workLeft, workedOverTime } = result;
|
const { startedAt, stoppedAt, stoppedWorking, worked, unLogged, workLeft, workedOverTime } = result;
|
||||||
log();
|
log();
|
||||||
log('Started working at:', formatTimestamp(startedAt));
|
log(msg(MessageKey.startedWorking), formatTimestamp(startedAt));
|
||||||
log((stoppedWorking ? 'Stopped working' : 'Hours calculated') + ' at:', formatTimestamp(stoppedAt));
|
log(
|
||||||
log('Worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked)));
|
(stoppedWorking ? msg(MessageKey.stoppedWorking) : msg(MessageKey.hoursCalculated)) +
|
||||||
|
` ${msg(MessageKey.klo)}:`,
|
||||||
|
formatTimestamp(stoppedAt),
|
||||||
|
);
|
||||||
|
log(msg(MessageKey.workedToday), chalk.green(fmtDuration(worked)), chalk.yellow(hoursRounded(worked)));
|
||||||
|
|
||||||
if (unLogged.asMinutes() == 0) {
|
const unLoggedMinutes = unLogged.asMinutes();
|
||||||
log('Unlogged today:', chalk.green('none'));
|
if (unLoggedMinutes >= 0) {
|
||||||
} else if (unLogged.asMinutes() > 0) {
|
|
||||||
log('Unlogged today:', chalk.red(formatDuration(unLogged)), chalk.yellow(getHoursRoundedStr(unLogged)));
|
|
||||||
} else if (unLogged.asMinutes() < 0) {
|
|
||||||
log(
|
log(
|
||||||
chalk.red(`You have logged ${formatDuration(unLogged)} more than you worked today!`),
|
msg(MessageKey.unloggedToday),
|
||||||
chalk.yellow(getHoursRoundedStr(unLogged)),
|
unLoggedMinutes === 0 ? chalk.green(msg(MessageKey.none)) : chalk.red(fmtDuration(unLogged)),
|
||||||
|
chalk.yellow(hoursRounded(unLogged)),
|
||||||
);
|
);
|
||||||
|
} else if (unLoggedMinutes < 0) {
|
||||||
|
log(chalk.red(msg(MessageKey.loggedOver, fmtDuration(unLogged))), chalk.yellow(hoursRounded(unLogged)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workLeft.asMinutes() > 0) {
|
if (workLeft.asMinutes() > 0) {
|
||||||
log('You still have to work', chalk.green(formatDuration(workLeft)), 'more today');
|
log(msg(MessageKey.workLeft, chalk.green(fmtDuration(workLeft))));
|
||||||
} else if (workedOverTime) {
|
} else if (workedOverTime) {
|
||||||
log('You worked', chalk.green(formatDuration(workedOverTime), 'overtime today'));
|
log(msg(MessageKey.workedOvertime, chalk.green(fmtDuration(workedOverTime))));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
6
src/types/Language.ts
Normal file
6
src/types/Language.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
enum Language {
|
||||||
|
en = 'en',
|
||||||
|
fi = 'fi',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Language;
|
20
src/types/WtcConfig.ts
Normal file
20
src/types/WtcConfig.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { Dayjs } from 'dayjs';
|
||||||
|
import { Duration } from 'dayjs/plugin/duration.js';
|
||||||
|
import Language from './Language.js';
|
||||||
|
|
||||||
|
export default interface WtcConfig {
|
||||||
|
language: Language,
|
||||||
|
defaults: {
|
||||||
|
workDayDuration: Duration;
|
||||||
|
lunchBreakDuration: Duration;
|
||||||
|
startTime: Dayjs;
|
||||||
|
stopTime: Dayjs;
|
||||||
|
};
|
||||||
|
askInput: {
|
||||||
|
workDayLength: boolean;
|
||||||
|
startTime: boolean;
|
||||||
|
stopTime: boolean;
|
||||||
|
logged: boolean;
|
||||||
|
hadLunch: boolean;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
|
import getConfig from './config.js';
|
||||||
import input from './input.js';
|
import input from './input.js';
|
||||||
import output from './output.js';
|
import output from './output.js';
|
||||||
|
|
||||||
const ui = async () => {
|
const ui = async () => {
|
||||||
const result = await input();
|
const config = getConfig();
|
||||||
output(result);
|
const result = await input(config);
|
||||||
|
output(result, config);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ui;
|
export default ui;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue