browsertrix/frontend/src/utils/executionTimeFormatter.ts
sua yoo d4334d42bc
feat: Enable self-service user access to billing portal (#1908)
Resolves https://github.com/webrecorder/browsertrix/issues/1875
Follows https://github.com/webrecorder/browsertrix/pull/1914

### Changes

- When billing is enabled, adds billing tab to org settings that
displays billing information if applicable
- Handles external link to manage plan
- Refactors org quota type to always be present
- Refactors org settings into `TailwindComponent`
2024-07-10 17:11:01 -07:00

132 lines
3.8 KiB
TypeScript

import { html, nothing } from "lit";
import { getLocale } from "./localization";
/**
* Returns either `nothing`, or hours-minutes-seconds wrapped in parens.
* Biases towards minutes:
* - When the time is exactly on an hour boundary, just shows hours
* - e.g. `3h`
* - When the time isn't on an hour boundary but is on a minute boundary, just shows hours (if applicable) and minutes
* - e.g. `3h 2m` or `32m`
* - When the time is less than a minute, shows minutes and seconds
* - e.g. `0m 43s`
*/
export function humanizeSeconds(
seconds: number,
locale?: string,
displaySeconds = false,
unitDisplay: "narrow" | "short" | "long" = "narrow",
) {
if (seconds < 0) {
throw new Error("humanizeSeconds in unimplemented for negative times");
}
const hours = Math.floor(seconds / 3600);
seconds -= hours * 3600;
// If displaying seconds, round minutes down, otherwise round up
const minutes = displaySeconds
? Math.floor(seconds / 60)
: Math.ceil(seconds / 60);
seconds -= minutes * 60;
const hourFormatter = new Intl.NumberFormat(locale, {
style: "unit",
unit: "hour",
unitDisplay: unitDisplay,
});
const minuteFormatter = new Intl.NumberFormat(locale, {
style: "unit",
unit: "minute",
unitDisplay: unitDisplay,
});
const secondFormatter = new Intl.NumberFormat(locale, {
style: "unit",
unit: "second",
unitDisplay: unitDisplay,
});
return [
hours !== 0 && hourFormatter.format(hours),
(minutes !== 0 || seconds !== 0 || (!displaySeconds && hours === 0)) &&
minuteFormatter.format(minutes),
displaySeconds &&
(seconds !== 0 || (hours === 0 && minutes === 0)) &&
secondFormatter.format(seconds),
]
.filter(Boolean)
.join(" ");
}
/**
* Formats execution seconds, either just as minutes (when `style` is `"short"`), or as minutes and hours-minutes-seconds (when `style` is undefined or `"full"`)
* @example humanizeExecutionSeconds(1_234_567_890)
* // <span title="20,576,132 minutes">21M minutes</span> (342,935h 31m 30s)
*
* @example humanizeExecutionSeconds(1_234_567_890, "short")
* // <span title="20,576,132 minutes (342,935h 31m 30s)">21M minutes</span>
*/
export const humanizeExecutionSeconds = (
seconds: number,
options?: {
/**
* When this is "long", the time in hours is also displayed
* @default "long"
*/
style?: "short" | "long";
/**
* @default false
*/
displaySeconds?: boolean;
/**
* @default "up"
*/
round?: "up" | "down";
},
) => {
const {
style = "long",
displaySeconds = false,
round = "up",
} = options || {};
const locale = getLocale();
const minutes =
round === "down" ? Math.floor(seconds / 60) : Math.ceil(seconds / 60);
const compactMinuteFormatter = new Intl.NumberFormat(locale, {
notation: "compact",
style: "unit",
unit: "minute",
unitDisplay: style,
});
const longMinuteFormatter = new Intl.NumberFormat(locale, {
style: "unit",
unit: "minute",
unitDisplay: "long",
maximumFractionDigits: 0,
});
const details = humanizeSeconds(seconds, locale, displaySeconds);
// if the time is less than an hour and lines up exactly on the minute, don't render the details.
const detailsRelevant = displaySeconds
? seconds % 60 !== 0
: Math.floor(seconds / 60) === 0 && seconds % 60 !== 0;
const formattedDetails =
detailsRelevant || seconds > 3600 ? `\u00a0(${details})` : nothing;
switch (style) {
case "long":
return html`<span title="${longMinuteFormatter.format(minutes)}">
${compactMinuteFormatter.format(minutes)}</span
>${formattedDetails}`;
case "short":
return html`<span
title="${longMinuteFormatter.format(minutes)}${formattedDetails}"
>${compactMinuteFormatter.format(minutes)}</span
>`;
}
};