1
0
mirror of https://github.com/amir20/dozzle.git synced 2025-12-24 06:28:42 +01:00

feat: localizes distance time (#3874)

This commit is contained in:
Amir Raminfar
2025-05-07 08:37:17 -07:00
committed by GitHub
parent 68ca6d3040
commit 9c70416aec
18 changed files with 83 additions and 74 deletions

View File

@@ -151,6 +151,7 @@ declare global {
const toReactive: typeof import('@vueuse/core')['toReactive'] const toReactive: typeof import('@vueuse/core')['toReactive']
const toRef: typeof import('vue')['toRef'] const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs'] const toRefs: typeof import('vue')['toRefs']
const toRelativeTime: typeof import('./utils/index')['toRelativeTime']
const toValue: typeof import('vue')['toValue'] const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef'] const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
@@ -542,6 +543,7 @@ declare module 'vue' {
readonly toReactive: UnwrapRef<typeof import('@vueuse/core')['toReactive']> readonly toReactive: UnwrapRef<typeof import('@vueuse/core')['toReactive']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']> readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']> readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
readonly toRelativeTime: UnwrapRef<typeof import('./utils/index')['toRelativeTime']>
readonly toValue: UnwrapRef<typeof import('vue')['toValue']> readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']> readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']> readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']>

View File

@@ -105,6 +105,7 @@ declare module 'vue' {
'Ph:stackSimple': typeof import('~icons/ph/stack-simple')['default'] 'Ph:stackSimple': typeof import('~icons/ph/stack-simple')['default']
Popup: typeof import('./components/Popup.vue')['default'] Popup: typeof import('./components/Popup.vue')['default']
RandomColorTag: typeof import('./components/LogViewer/RandomColorTag.vue')['default'] RandomColorTag: typeof import('./components/LogViewer/RandomColorTag.vue')['default']
RelativeTime: typeof import('./components/common/RelativeTime.vue')['default']
'Ri:terminalWindowFill': typeof import('~icons/ri/terminal-window-fill')['default'] 'Ri:terminalWindowFill': typeof import('~icons/ri/terminal-window-fill')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']

View File

@@ -22,7 +22,7 @@
> >
{{ release.name }} {{ release.name }}
</a> </a>
<span class="ml-1 text-xs"><distance-time :date="release.createdAt" /></span> <span class="ml-1 text-xs"><RelativeTime :date="release.createdAt" /></span>
</div> </div>
<div class="text-base-content/80 text-sm"> <div class="text-base-content/80 text-sm">
{{ release.body }} {{ release.body }}
@@ -39,7 +39,7 @@
> >
{{ release.name }} {{ release.name }}
</a> </a>
<span class="ml-1 text-xs"><distance-time :date="release.createdAt" /></span> <span class="ml-1 text-xs"><RelativeTime :date="release.createdAt" /></span>
<Tag class="bg-red ml-auto px-1 py-1 text-xs" v-if="release.latest"> <Tag class="bg-red ml-auto px-1 py-1 text-xs" v-if="release.latest">
{{ $t("releases.latest") }} {{ $t("releases.latest") }}
</Tag> </Tag>

View File

@@ -10,7 +10,7 @@
></div> ></div>
{{ other.name }} {{ other.name }}
<div v-if="other.state === 'running'">running</div> <div v-if="other.state === 'running'">running</div>
<DistanceTime :date="other.created" strict class="text-base-content/70 text-xs" v-else /> <RelativeTime :date="other.finishedAt" class="text-base-content/70 text-xs" v-else />
</router-link> </router-link>
</li> </li>
</ul> </ul>

View File

@@ -1,28 +1,32 @@
<template> <template>
<div> <table class="w-full border-separate border-spacing-x-1">
<span class="font-light capitalize"> STATE </span> <tbody>
<span class="font-semibold uppercase"> {{ container.state }} </span> <tr>
</div> <th class="text-right font-light capitalize">STATE</th>
<div v-if="container.startedAt.getFullYear() > 0"> <td class="font-semibold uppercase">{{ container.state }}</td>
<span class="font-light capitalize"> STARTED </span> </tr>
<span class="font-semibold"> <tr v-if="container.startedAt.getFullYear() > 0">
<DistanceTime :date="container.startedAt" strict /> <th class="text-right font-light capitalize">STARTED</th>
</span> <td class="font-semibold">
</div> <RelativeTime :date="container.startedAt" />
<div v-if="container.state != 'running' && container.finishedAt.getFullYear() > 0"> </td>
<span class="font-light capitalize"> FINISHED </span> </tr>
<span class="font-semibold"> <tr v-if="container.state != 'running' && container.finishedAt.getFullYear() > 0">
<DistanceTime :date="container.finishedAt" strict /> <th class="text-right font-light capitalize">FINISHED</th>
</span> <td class="font-semibold">
</div> <RelativeTime :date="container.finishedAt" />
<div v-if="container.state == 'running'"> </td>
<span class="font-light capitalize"> Load </span> </tr>
<span class="font-semibold"> {{ container.stat.cpu.toFixed(2) }}% </span> <tr v-if="container.state == 'running'">
</div> <th class="text-right font-light capitalize">Load</th>
<div v-if="container.state == 'running'"> <td class="font-semibold">{{ container.stat.cpu.toFixed(2) }}%</td>
<span class="font-light capitalize"> MEM </span> </tr>
<span class="font-semibold"> {{ formatBytes(container.stat.memoryUsage) }} </span> <tr v-if="container.state == 'running'">
</div> <th class="text-right font-light capitalize">MEM</th>
<td class="font-semibold">{{ formatBytes(container.stat.memoryUsage) }}</td>
</tr>
</tbody>
</table>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@@ -75,7 +75,7 @@
<td v-if="isVisible('host')">{{ container.hostLabel }}</td> <td v-if="isVisible('host')">{{ container.hostLabel }}</td>
<td v-if="isVisible('state')">{{ container.state }}</td> <td v-if="isVisible('state')">{{ container.state }}</td>
<td v-if="isVisible('created')"> <td v-if="isVisible('created')">
<distance-time :date="container.created" strict :suffix="false"></distance-time> <RelativeTime :date="container.created" />
</td> </td>
<td v-if="isVisible('cpu')"> <td v-if="isVisible('cpu')">
<div class="flex flex-row items-center gap-1"> <div class="flex flex-row items-center gap-1">

View File

@@ -28,7 +28,7 @@
<div v-if="other.isSwarm">{{ other.swarmId }}</div> <div v-if="other.isSwarm">{{ other.swarmId }}</div>
<div v-else>{{ other.name }}</div> <div v-else>{{ other.name }}</div>
<div v-if="other.state === 'running'">running</div> <div v-if="other.state === 'running'">running</div>
<DistanceTime :date="other.created" strict class="text-base-content/70 text-xs" v-else /> <RelativeTime :date="other.finishedAt" class="text-base-content/70 text-xs" v-else />
</router-link> </router-link>
</li> </li>
</ul> </ul>

View File

@@ -53,7 +53,7 @@
<span data-name v-html="matchedName(result)"></span> <span data-name v-html="matchedName(result)"></span>
</div> </div>
<DistanceTime :date="result.item.created" class="text-xs font-light" /> <RelativeTime :date="result.item.created" class="text-xs font-light" />
<span <span
@click.stop.prevent="addColumn(result.item)" @click.stop.prevent="addColumn(result.item)"
:title="$t('tooltip.pin-column')" :title="$t('tooltip.pin-column')"

View File

@@ -2,7 +2,7 @@
<aside> <aside>
<header class="flex items-center gap-4"> <header class="flex items-center gap-4">
<h1 class="text-2xl max-md:hidden">{{ container.name }}</h1> <h1 class="text-2xl max-md:hidden">{{ container.name }}</h1>
<h2 class="text-sm"><DistanceTime :date="container.created" /></h2> <h2 class="text-sm"><RelativeTime :date="container.created" /></h2>
</header> </header>
<div class="mt-8 flex flex-col gap-2"> <div class="mt-8 flex flex-col gap-2">

View File

@@ -4,7 +4,7 @@
<h1 class="text-lg max-md:hidden"> <h1 class="text-lg max-md:hidden">
<DateTime :date="entry.date" /> <DateTime :date="entry.date" />
</h1> </h1>
<h2 class="text-sm"><DistanceTime :date="entry.date" /> on {{ entry.std }}</h2> <h2 class="text-sm"><RelativeTime :date="entry.date" /> on {{ entry.std }}</h2>
</header> </header>
<div class="mt-8 flex flex-col gap-10"> <div class="mt-8 flex flex-col gap-10">

View File

@@ -4,7 +4,7 @@
<transition name="fade"> <transition name="fade">
<div <div
v-show="show && (delayedShow || globalShow)" v-show="show && (delayedShow || globalShow)"
class="border-base-content/20 bg-base-100 fixed z-50 rounded-sm border p-4 shadow-sm" class="ring-base-content/20 bg-base-100 fixed z-50 rounded-sm p-3 shadow-sm ring"
ref="content" ref="content"
> >
<slot name="content"></slot> <slot name="content"></slot>

View File

@@ -12,7 +12,7 @@
<span> % </span> <span> % </span>
</div> </div>
</div> </div>
<DistanceTime :date="date" class="text-sm whitespace-nowrap" /> <RelativeTime :date="date" class="text-sm whitespace-nowrap" />
</div> </div>
</transition> </transition>
</template> </template>

View File

@@ -3,7 +3,7 @@
<header class="flex items-center gap-4"> <header class="flex items-center gap-4">
<material-symbols:terminal class="size-8" /> <material-symbols:terminal class="size-8" />
<h1 class="text-2xl max-md:hidden">{{ container.name }}</h1> <h1 class="text-2xl max-md:hidden">{{ container.name }}</h1>
<h2 class="text-sm">Started <DistanceTime :date="container.created" /></h2> <h2 class="text-sm">Started <RelativeTime :date="container.created" /></h2>
</header> </header>
<div class="mt-8 flex flex-col gap-2"> <div class="mt-8 flex flex-col gap-2">

View File

@@ -1,30 +0,0 @@
<template>
<time :datetime="date.toISOString()" data-ci-skip>{{ text }}</time>
</template>
<script lang="ts" setup>
import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
import { formatDistanceToNowStrict } from "date-fns/formatDistanceToNowStrict";
const {
date,
strict = false,
suffix = true,
} = defineProps<{
date: Date;
strict?: boolean;
suffix?: boolean;
}>();
const text = ref<string>();
watch($$(date), updateFromNow, { immediate: true });
function updateFromNow() {
const fn = strict ? formatDistanceToNowStrict : formatDistanceToNow;
text.value = fn(date, {
addSuffix: suffix,
});
}
useIntervalFn(updateFromNow, 30_000, { immediateCallback: true });
</script>

View File

@@ -0,0 +1,17 @@
<template>
<time :datetime="date.toISOString()" data-ci-skip>{{ text }}</time>
</template>
<script lang="ts" setup>
const { date } = defineProps<{
date: Date;
}>();
const text = ref<string>();
const updateFromNow = () => {
text.value = toRelativeTime(date, locale.value === "" ? undefined : locale.value);
};
watch(toRef(date), updateFromNow, { immediate: true });
useIntervalFn(updateFromNow, 30_000);
</script>

View File

@@ -109,3 +109,27 @@ export function hashCode(str: string) {
} }
return hash; return hash;
} }
const units: [Intl.RelativeTimeFormatUnit, number][] = [
["year", 31536000],
["month", 2592000],
["week", 604800],
["day", 86400],
["hour", 3600],
["minute", 60],
["second", 1],
];
export function toRelativeTime(date: Date, locale: string | undefined): string {
const diffInSeconds = (date.getTime() - new Date().getTime()) / 1000;
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
for (const [unit, seconds] of units) {
const value = Math.round(diffInSeconds / seconds);
if (Math.abs(value) >= 1) {
return rtf.format(value, unit);
}
}
return rtf.format(0, "second");
}

View File

@@ -55,7 +55,6 @@
"d3-shape": "^3.2.0", "d3-shape": "^3.2.0",
"d3-transition": "^3.0.1", "d3-transition": "^3.0.1",
"daisyui": "5.0.35", "daisyui": "5.0.35",
"date-fns": "^4.1.0",
"entities": "^6.0.0", "entities": "^6.0.0",
"fuse.js": "^7.1.0", "fuse.js": "^7.1.0",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",

8
pnpm-lock.yaml generated
View File

@@ -86,9 +86,6 @@ importers:
daisyui: daisyui:
specifier: 5.0.35 specifier: 5.0.35
version: 5.0.35 version: 5.0.35
date-fns:
specifier: ^4.1.0
version: 4.1.0
entities: entities:
specifier: ^6.0.0 specifier: ^6.0.0
version: 6.0.0 version: 6.0.0
@@ -2364,9 +2361,6 @@ packages:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'} engines: {node: '>=18'}
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
de-indent@1.0.2: de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@@ -6075,8 +6069,6 @@ snapshots:
whatwg-mimetype: 4.0.0 whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0 whatwg-url: 14.2.0
date-fns@4.1.0: {}
de-indent@1.0.2: {} de-indent@1.0.2: {}
debug@4.4.0: debug@4.4.0: