mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-14 03:02:15 +01:00
add support for interval / duration format in frontend views
This commit is contained in:
41
resources/js/packages/ui/src/utils/number.ts
Normal file
41
resources/js/packages/ui/src/utils/number.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
export type NumberFormat =
|
||||
| 'point-comma'
|
||||
| 'comma-point'
|
||||
| 'space-comma'
|
||||
| 'space-point'
|
||||
| 'apostrophe-point';
|
||||
|
||||
/**
|
||||
* Formats a number according to the specified format
|
||||
* @param value - The number to format
|
||||
* @param format - The format to use
|
||||
* @returns The formatted number as a string
|
||||
*/
|
||||
export function formatNumber(value: number, format?: string): string {
|
||||
// Convert to fixed 2 decimal places first
|
||||
const parts = value.toFixed(2).split('.');
|
||||
const wholePart = parts[0];
|
||||
const decimalPart = parts[1];
|
||||
|
||||
// Format the whole number part based on the format
|
||||
let formattedWhole: string;
|
||||
switch (format) {
|
||||
case 'point-comma':
|
||||
formattedWhole = wholePart.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
return `${formattedWhole},${decimalPart}`;
|
||||
case 'comma-point':
|
||||
formattedWhole = wholePart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return `${formattedWhole}.${decimalPart}`;
|
||||
case 'space-comma':
|
||||
formattedWhole = wholePart.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||||
return `${formattedWhole},${decimalPart}`;
|
||||
case 'space-point':
|
||||
formattedWhole = wholePart.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||||
return `${formattedWhole}.${decimalPart}`;
|
||||
case 'apostrophe-point':
|
||||
formattedWhole = wholePart.replace(/\B(?=(\d{3})+(?!\d))/g, "'");
|
||||
return `${formattedWhole}.${decimalPart}`;
|
||||
default:
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,34 @@ import isYesterday from 'dayjs/plugin/isYesterday';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||
import parse from 'parse-duration';
|
||||
|
||||
import { getUserTimezone, getWeekStart } from './settings';
|
||||
import updateLocale from 'dayjs/plugin/updateLocale';
|
||||
import { computed } from 'vue';
|
||||
import { formatNumber } from './number';
|
||||
|
||||
export type CurrencyFormat =
|
||||
| 'iso-code-before-with-space'
|
||||
| 'iso-code-after-with-space'
|
||||
| 'symbol-before'
|
||||
| 'symbol-after'
|
||||
| 'symbol-before-with-space'
|
||||
| 'symbol-after-with-space';
|
||||
export type DateFormat =
|
||||
| 'point-separated-d-m-yyyy'
|
||||
| 'slash-separated-mm-dd-yyyy'
|
||||
| 'slash-separated-dd-mm-yyyy'
|
||||
| 'hyphen-separated-dd-mm-yyyy'
|
||||
| 'hyphen-separated-mm-dd-yyyy'
|
||||
| 'hyphen-separated-yyyy-mm-dd';
|
||||
export type TimeFormat = '12-hours' | '24-hours';
|
||||
export type IntervalFormat =
|
||||
| 'decimal'
|
||||
| 'hours-minutes'
|
||||
| 'hours-minutes-colon-separated'
|
||||
| 'hours-minutes-seconds-colon-separated';
|
||||
export type TimeInputUnit = 'minutes' | 'hours';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(isToday);
|
||||
@@ -40,11 +64,28 @@ export const firstDayIndex = computed(() => {
|
||||
return apiDayOrder.indexOf(getWeekStart());
|
||||
});
|
||||
|
||||
export function formatHumanReadableDuration(duration: number): string {
|
||||
export function formatHumanReadableDuration(
|
||||
duration: number,
|
||||
intervalFormat?: string,
|
||||
numberFormat?: string
|
||||
): string {
|
||||
const dayJsDuration = dayjs.duration(duration, 's');
|
||||
const hours = Math.floor(dayJsDuration.asHours());
|
||||
const minutes = dayJsDuration.minutes();
|
||||
return `${hours}h ${minutes.toString().padStart(2, '0')}min`;
|
||||
const seconds = dayJsDuration.seconds();
|
||||
|
||||
switch (intervalFormat) {
|
||||
case 'decimal':
|
||||
return formatNumber(dayJsDuration.asHours(), numberFormat) + ' h';
|
||||
case 'hours-minutes':
|
||||
return `${hours}h ${minutes.toString().padStart(2, '0')}min`;
|
||||
case 'hours-minutes-colon-separated':
|
||||
return `${hours}:${minutes.toString().padStart(2, '0')}`;
|
||||
case 'hours-minutes-seconds-colon-separated':
|
||||
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
default:
|
||||
return `${hours}h ${minutes.toString().padStart(2, '0')}min`;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatDuration(duration: number): string {
|
||||
@@ -131,3 +172,44 @@ export function formatStartEnd(start: string, end: string | null) {
|
||||
return `${formatTime(start)} - ...`;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseTimeInput(
|
||||
input: string,
|
||||
defaultUnit: TimeInputUnit = 'minutes'
|
||||
): {
|
||||
seconds: number | null;
|
||||
isHHMM: boolean;
|
||||
} {
|
||||
// Check if input is a decimal number (hours)
|
||||
const decimalRegex = /^-?\d+[.,]\d+$/;
|
||||
if (decimalRegex.test(input)) {
|
||||
const hours = parseFloat(input.replace(',', '.'));
|
||||
return { seconds: Math.round(hours * 3600), isHHMM: false };
|
||||
}
|
||||
|
||||
// Check if input is just a number (minutes or hours based on defaultUnit)
|
||||
if (/^-?\d+$/.test(input)) {
|
||||
const value = parseInt(input);
|
||||
const seconds = defaultUnit === 'minutes' ? value * 60 : value * 3600;
|
||||
return { seconds, isHHMM: false };
|
||||
}
|
||||
|
||||
// Check if input is in HH:MM format
|
||||
const HHMMtimeRegex = /^([0-9]{1,2}):([0-5]?[0-9])$/;
|
||||
if (HHMMtimeRegex.test(input)) {
|
||||
const match = input.match(HHMMtimeRegex);
|
||||
if (match) {
|
||||
const hours = parseInt(match[1]);
|
||||
const minutes = parseInt(match[2]);
|
||||
return { seconds: (hours * 60 + minutes) * 60, isHHMM: true };
|
||||
}
|
||||
}
|
||||
|
||||
// Try to parse natural language like "1h 30m"
|
||||
const parsedDuration = parse(input, 's');
|
||||
if (parsedDuration && parsedDuration > 0) {
|
||||
return { seconds: parsedDuration, isHHMM: false };
|
||||
}
|
||||
|
||||
return { seconds: null, isHHMM: false };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user