feat: Logs now have a calendar view
This commit is contained in:
parent
e96270046e
commit
2ffb8b0d81
|
|
@ -17,10 +17,10 @@
|
||||||
"@xterm/addon-fit": "^0.11.0",
|
"@xterm/addon-fit": "^0.11.0",
|
||||||
"@xterm/xterm": "^6.0.0",
|
"@xterm/xterm": "^6.0.0",
|
||||||
"iconify-icon": "^3.0.2",
|
"iconify-icon": "^3.0.2",
|
||||||
"picocrank": "^1.12.1",
|
"picocrank": "^1.12.5",
|
||||||
"standard": "^17.1.2",
|
"standard": "^17.1.2",
|
||||||
"unplugin-vue-components": "^30.0.0",
|
"unplugin-vue-components": "^30.0.0",
|
||||||
"vite": "^7.3.0",
|
"vite": "^7.3.1",
|
||||||
"vue-i18n": "^11.2.8",
|
"vue-i18n": "^11.2.8",
|
||||||
"vue-router": "^4.6.4"
|
"vue-router": "^4.6.4"
|
||||||
},
|
},
|
||||||
|
|
@ -2961,9 +2961,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/femtocrank": {
|
"node_modules/femtocrank": {
|
||||||
"version": "2.4.12",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/femtocrank/-/femtocrank-2.4.12.tgz",
|
"resolved": "https://registry.npmjs.org/femtocrank/-/femtocrank-2.5.0.tgz",
|
||||||
"integrity": "sha512-X2a4WVG1ADGjQcULUyH2FJ4njJNZobfP+iPO1MpAEtWRyVEDxps6dmktbOb3igoyCxNObF0T0uKwUC7zV21C0A==",
|
"integrity": "sha512-plV1HNS/fUzohWJ349kuCBZ3TCfXz7V4F/sY2lVbVWtGXUV+aHxLG6IddAMEf64k2LJ8j0KVrj+nIIKepFaKvg==",
|
||||||
"license": "AGPL-3.0"
|
"license": "AGPL-3.0"
|
||||||
},
|
},
|
||||||
"node_modules/file-entry-cache": {
|
"node_modules/file-entry-cache": {
|
||||||
|
|
@ -4607,17 +4607,17 @@
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picocrank": {
|
"node_modules/picocrank": {
|
||||||
"version": "1.12.1",
|
"version": "1.12.5",
|
||||||
"resolved": "https://registry.npmjs.org/picocrank/-/picocrank-1.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocrank/-/picocrank-1.12.5.tgz",
|
||||||
"integrity": "sha512-2qcIcveWQkkA2Wyo+KQdZANTbjb/9ydzinbpNN/1U/4x0BBUjyHhWoK5lNAx/KDVNl6ZM3xGo3eMb5/n6xWoVA==",
|
"integrity": "sha512-z0EP/I56cFGzvXV4EAEpkczYDYkdHGtRfHQA+k7rbrBEHMO1fi7qW8VbDj7/2eqeG6IbNqWRIG1IexRQWZj7bQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hugeicons/core-free-icons": "^3.1.0",
|
"@hugeicons/core-free-icons": "^3.1.1",
|
||||||
"@hugeicons/vue": "^1.0.4",
|
"@hugeicons/vue": "^1.0.4",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
"femtocrank": "^2.4.12",
|
"femtocrank": "^2.5.0",
|
||||||
"unplugin-vue-components": "^30.0.0",
|
"unplugin-vue-components": "^30.0.0",
|
||||||
"vite": "^7.3.0",
|
"vite": "^7.3.1",
|
||||||
"vue": "^3.5.26",
|
"vue": "^3.5.26",
|
||||||
"vue-router": "^4.6.4"
|
"vue-router": "^4.6.4"
|
||||||
}
|
}
|
||||||
|
|
@ -6122,9 +6122,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@
|
||||||
"@xterm/addon-fit": "^0.11.0",
|
"@xterm/addon-fit": "^0.11.0",
|
||||||
"@xterm/xterm": "^6.0.0",
|
"@xterm/xterm": "^6.0.0",
|
||||||
"iconify-icon": "^3.0.2",
|
"iconify-icon": "^3.0.2",
|
||||||
"picocrank": "^1.12.1",
|
"picocrank": "^1.12.5",
|
||||||
"standard": "^17.1.2",
|
"standard": "^17.1.2",
|
||||||
"unplugin-vue-components": "^30.0.0",
|
"unplugin-vue-components": "^30.0.0",
|
||||||
"vite": "^7.3.0",
|
"vite": "^7.3.1",
|
||||||
"vue-i18n": "^11.2.8",
|
"vue-i18n": "^11.2.8",
|
||||||
"vue-router": "^4.6.4"
|
"vue-router": "^4.6.4"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,18 @@ const routes = [
|
||||||
icon: LeftToRightListDashIcon
|
icon: LeftToRightListDashIcon
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/logs/calendar',
|
||||||
|
name: 'LogsCalendar',
|
||||||
|
component: () => import('./views/LogsCalendarView.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'Logs Calendar',
|
||||||
|
breadcrumb: [
|
||||||
|
{ name: "Logs", href: "/logs" },
|
||||||
|
{ name: "Calendar" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/entities',
|
path: '/entities',
|
||||||
name: 'Entities',
|
name: 'Entities',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
<template>
|
||||||
|
<Section :title="t('logs.calendar-title')" :padding="false">
|
||||||
|
<template #toolbar>
|
||||||
|
<router-link to="/logs" class="button neutral">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8l8 8l1.41-1.41L7.83 13H20z"/>
|
||||||
|
</svg>
|
||||||
|
{{ t('logs.back-to-list') }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="padding">
|
||||||
|
<Calendar
|
||||||
|
:events="calendarEvents"
|
||||||
|
:loading="loading"
|
||||||
|
:error="error"
|
||||||
|
:current-month="currentMonthIndex"
|
||||||
|
:current-year="currentYear"
|
||||||
|
@event-click="handleEventClick"
|
||||||
|
@date-click="handleDayClick"
|
||||||
|
@month-change="handleMonthChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import Calendar from 'picocrank/vue/components/Calendar.vue'
|
||||||
|
import Section from 'picocrank/vue/components/Section.vue'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const logs = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref(null)
|
||||||
|
const currentMonthIndex = ref(new Date().getMonth())
|
||||||
|
const currentYear = ref(new Date().getFullYear())
|
||||||
|
|
||||||
|
// Convert logs to calendar events format
|
||||||
|
const calendarEvents = computed(() => {
|
||||||
|
return logs.value
|
||||||
|
.filter(log => {
|
||||||
|
// Only include logs with valid start dates
|
||||||
|
if (!log.datetimeStarted) return false
|
||||||
|
const startDate = new Date(log.datetimeStarted)
|
||||||
|
return !isNaN(startDate.getTime())
|
||||||
|
})
|
||||||
|
.map(log => {
|
||||||
|
const startDate = new Date(log.datetimeStarted)
|
||||||
|
let endDate = log.datetimeFinished ? new Date(log.datetimeFinished) : null
|
||||||
|
|
||||||
|
// Validate end date
|
||||||
|
if (endDate && isNaN(endDate.getTime())) {
|
||||||
|
endDate = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: log.executionTrackingId,
|
||||||
|
title: log.actionTitle || 'Untitled Action',
|
||||||
|
date: startDate,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
actionIcon: log.actionIcon,
|
||||||
|
user: log.user,
|
||||||
|
tags: log.tags,
|
||||||
|
logEntry: log
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
async function fetchLogs() {
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch a large number of logs to populate the calendar
|
||||||
|
// We'll fetch more than a single page to get better calendar coverage
|
||||||
|
const args = {
|
||||||
|
"startOffset": BigInt(0),
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await window.client.getLogs(args)
|
||||||
|
logs.value = response.logs || []
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to fetch logs:', err)
|
||||||
|
error.value = 'Failed to load logs'
|
||||||
|
window.showBigError('fetch-logs-calendar', 'getting logs for calendar', err, false)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEventClick(event) {
|
||||||
|
// Navigate to the execution view when clicking on a calendar event
|
||||||
|
if (event.id) {
|
||||||
|
router.push(`/logs/${event.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDayClick(date) {
|
||||||
|
// Navigate to logs list filtered by the selected date
|
||||||
|
// Format date as YYYY-MM-DD for the query parameter
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
const dateString = `${year}-${month}-${day}`
|
||||||
|
router.push(`/logs?date=${dateString}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMonthChange(month, year) {
|
||||||
|
currentMonthIndex.value = month
|
||||||
|
currentYear.value = year
|
||||||
|
// Optionally fetch logs for the new month if needed
|
||||||
|
// For now, we'll keep all logs loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchLogs()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.padding {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<Section :title="t('logs.title')" :padding="false">
|
<Section :title="t('logs.title')" :padding="false">
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
|
<router-link to="/logs/calendar" class="button neutral">
|
||||||
|
Calendar
|
||||||
|
</router-link>
|
||||||
<label class="input-with-icons">
|
<label class="input-with-icons">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
|
|
@ -21,7 +24,20 @@
|
||||||
<table class="logs-table">
|
<table class="logs-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ t('logs.timestamp') }}</th>
|
<th>
|
||||||
|
<div class="timestamp-header">
|
||||||
|
<span>{{ t('logs.timestamp') }}</span>
|
||||||
|
<span v-if="selectedDate" class="date-filter-indicator">
|
||||||
|
<span class="date-filter-text">{{ formatDateFilter(selectedDate) }}</span>
|
||||||
|
<button :title="t('logs.clear-date-filter')" @click="clearDateFilter" class="clear-date-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
<th>{{ t('logs.action') }}</th>
|
<th>{{ t('logs.action') }}</th>
|
||||||
<th>{{ t('logs.metadata') }}</th>
|
<th>{{ t('logs.metadata') }}</th>
|
||||||
<th>{{ t('logs.status') }}</th>
|
<th>{{ t('logs.status') }}</th>
|
||||||
|
|
@ -56,7 +72,14 @@
|
||||||
@page-size-change="handlePageSizeChange" itemTitle="execution logs" />
|
@page-size-change="handlePageSizeChange" itemTitle="execution logs" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-show="logs.length === 0" class="empty-state">
|
<div v-show="selectedDate && filteredLogs.length === 0" class="empty-state">
|
||||||
|
<p>No logs found for {{ formatDateFilter(selectedDate) }}.</p>
|
||||||
|
<button @click="clearDateFilter" class="button neutral">
|
||||||
|
Clear date filter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="logs.length === 0 && !selectedDate" class="empty-state">
|
||||||
<p>{{ t('logs.no-logs-to-display') }}</p>
|
<p>{{ t('logs.no-logs-to-display') }}</p>
|
||||||
<router-link to="/">{{ t('return-to-index') }}</router-link>
|
<router-link to="/">{{ t('return-to-index') }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -64,27 +87,57 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import Pagination from 'picocrank/vue/components/Pagination.vue'
|
import Pagination from 'picocrank/vue/components/Pagination.vue'
|
||||||
import Section from 'picocrank/vue/components/Section.vue'
|
import Section from 'picocrank/vue/components/Section.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ActionStatusDisplay from '../components/ActionStatusDisplay.vue'
|
import ActionStatusDisplay from '../components/ActionStatusDisplay.vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const logs = ref([])
|
const logs = ref([])
|
||||||
const searchText = ref('')
|
const searchText = ref('')
|
||||||
const pageSize = ref(10)
|
const pageSize = ref(10)
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const totalCount = ref(0)
|
const totalCount = ref(0)
|
||||||
|
const selectedDate = ref(null)
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// Read date query parameter from route
|
||||||
|
function updateDateFromRoute() {
|
||||||
|
const dateParam = route.query.date
|
||||||
|
if (dateParam) {
|
||||||
|
selectedDate.value = dateParam
|
||||||
|
} else {
|
||||||
|
selectedDate.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for route changes to update date filter
|
||||||
|
watch(() => route.query.date, () => {
|
||||||
|
updateDateFromRoute()
|
||||||
|
})
|
||||||
|
|
||||||
const filteredLogs = computed(() => {
|
const filteredLogs = computed(() => {
|
||||||
let result = logs.value
|
let result = logs.value
|
||||||
|
|
||||||
|
// Filter by selected date if present
|
||||||
|
if (selectedDate.value) {
|
||||||
|
result = result.filter(log => {
|
||||||
|
if (!log.datetimeStarted) return false
|
||||||
|
const logDate = new Date(log.datetimeStarted)
|
||||||
|
const logDateString = `${logDate.getFullYear()}-${String(logDate.getMonth() + 1).padStart(2, '0')}-${String(logDate.getDate()).padStart(2, '0')}`
|
||||||
|
return logDateString === selectedDate.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (searchText.value) {
|
if (searchText.value) {
|
||||||
const searchLower = searchText.value.toLowerCase()
|
const searchLower = searchText.value.toLowerCase()
|
||||||
result = logs.value.filter(log =>
|
result = result.filter(log =>
|
||||||
log.actionTitle.toLowerCase().includes(searchLower)
|
log.actionTitle.toLowerCase().includes(searchLower)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -123,6 +176,24 @@ function clearSearch() {
|
||||||
searchText.value = ''
|
searchText.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearDateFilter() {
|
||||||
|
selectedDate.value = null
|
||||||
|
// Remove date query parameter from URL
|
||||||
|
const query = { ...route.query }
|
||||||
|
delete query.date
|
||||||
|
router.push({ path: route.path, query })
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateFilter(dateString) {
|
||||||
|
// Format YYYY-MM-DD to a short format (e.g., "Jan 15, 2024")
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString + 'T00:00:00')
|
||||||
|
return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })
|
||||||
|
} catch (err) {
|
||||||
|
return dateString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function formatTimestamp(timestamp) {
|
function formatTimestamp(timestamp) {
|
||||||
if (!timestamp) return 'Unknown'
|
if (!timestamp) return 'Unknown'
|
||||||
try {
|
try {
|
||||||
|
|
@ -144,6 +215,7 @@ function handlePageSizeChange(newPageSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
updateDateFromRoute()
|
||||||
fetchLogs()
|
fetchLogs()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -227,4 +299,47 @@ onMounted(() => {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
.timestamp-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-filter-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: normal;
|
||||||
|
color: var(--text-secondary, #666);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-filter-text {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp-header .clear-date-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.125rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp-header .clear-date-button:hover {
|
||||||
|
opacity: 1;
|
||||||
|
background: var(--hover-background, rgba(0, 0, 0, 0.05));
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp-header .clear-date-button svg {
|
||||||
|
width: 0.75rem;
|
||||||
|
height: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@
|
||||||
"language-dialog.title": "Sprache auswählen",
|
"language-dialog.title": "Sprache auswählen",
|
||||||
"login-button": "Login",
|
"login-button": "Login",
|
||||||
"logs.action": "Aktion",
|
"logs.action": "Aktion",
|
||||||
|
"logs.back-to-list": "Zurück zur Liste",
|
||||||
"logs.blocked": "Blockiert",
|
"logs.blocked": "Blockiert",
|
||||||
|
"logs.calendar-title": "Protokoll-Kalender",
|
||||||
"logs.clear-filter": "Suchfilter löschen",
|
"logs.clear-filter": "Suchfilter löschen",
|
||||||
"logs.completed": "Abgeschlossen",
|
"logs.completed": "Abgeschlossen",
|
||||||
"logs.exit-code": "Ausführungscode",
|
"logs.exit-code": "Ausführungscode",
|
||||||
|
|
@ -73,7 +75,9 @@
|
||||||
"language-dialog.title": "Select Language",
|
"language-dialog.title": "Select Language",
|
||||||
"login-button": "Login",
|
"login-button": "Login",
|
||||||
"logs.action": "Action",
|
"logs.action": "Action",
|
||||||
|
"logs.back-to-list": "Back to List",
|
||||||
"logs.blocked": "Blocked",
|
"logs.blocked": "Blocked",
|
||||||
|
"logs.calendar-title": "Logs Calendar",
|
||||||
"logs.clear-filter": "Clear search filter",
|
"logs.clear-filter": "Clear search filter",
|
||||||
"logs.completed": "Completed",
|
"logs.completed": "Completed",
|
||||||
"logs.exit-code": "Exit code",
|
"logs.exit-code": "Exit code",
|
||||||
|
|
@ -119,7 +123,9 @@
|
||||||
"language-dialog.title": "Seleccionar idioma",
|
"language-dialog.title": "Seleccionar idioma",
|
||||||
"login-button": "Iniciar sesión",
|
"login-button": "Iniciar sesión",
|
||||||
"logs.action": "Acción",
|
"logs.action": "Acción",
|
||||||
|
"logs.back-to-list": "Volver a la Lista",
|
||||||
"logs.blocked": "Bloqueado",
|
"logs.blocked": "Bloqueado",
|
||||||
|
"logs.calendar-title": "Calendario de Registros",
|
||||||
"logs.clear-filter": "Limpiar filtro de búsqueda",
|
"logs.clear-filter": "Limpiar filtro de búsqueda",
|
||||||
"logs.completed": "Completado",
|
"logs.completed": "Completado",
|
||||||
"logs.exit-code": "Código de salida",
|
"logs.exit-code": "Código de salida",
|
||||||
|
|
@ -165,7 +171,9 @@
|
||||||
"language-dialog.title": "Seleziona lingua",
|
"language-dialog.title": "Seleziona lingua",
|
||||||
"login-button": "Login",
|
"login-button": "Login",
|
||||||
"logs.action": "Azione",
|
"logs.action": "Azione",
|
||||||
|
"logs.back-to-list": "Torna all'Elenco",
|
||||||
"logs.blocked": "Bloccato",
|
"logs.blocked": "Bloccato",
|
||||||
|
"logs.calendar-title": "Calendario dei Registri",
|
||||||
"logs.clear-filter": "Cancella filtro di ricerca",
|
"logs.clear-filter": "Cancella filtro di ricerca",
|
||||||
"logs.completed": "Completato",
|
"logs.completed": "Completato",
|
||||||
"logs.exit-code": "Codice di uscita",
|
"logs.exit-code": "Codice di uscita",
|
||||||
|
|
@ -211,7 +219,9 @@
|
||||||
"language-dialog.title": "选择语言",
|
"language-dialog.title": "选择语言",
|
||||||
"login-button": "登录",
|
"login-button": "登录",
|
||||||
"logs.action": "动作",
|
"logs.action": "动作",
|
||||||
|
"logs.back-to-list": "返回列表",
|
||||||
"logs.blocked": "阻塞",
|
"logs.blocked": "阻塞",
|
||||||
|
"logs.calendar-title": "日志日历",
|
||||||
"logs.clear-filter": "清除搜索筛选器",
|
"logs.clear-filter": "清除搜索筛选器",
|
||||||
"logs.completed": "完成",
|
"logs.completed": "完成",
|
||||||
"logs.exit-code": "退出代码",
|
"logs.exit-code": "退出代码",
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ translations:
|
||||||
logs.exit-code: Ausführungscode
|
logs.exit-code: Ausführungscode
|
||||||
logs.completed: Abgeschlossen
|
logs.completed: Abgeschlossen
|
||||||
logs.clear-filter: Suchfilter löschen
|
logs.clear-filter: Suchfilter löschen
|
||||||
|
logs.calendar-title: Protokoll-Kalender
|
||||||
|
logs.back-to-list: Zurück zur Liste
|
||||||
diagnostics.get-support: Unterstützung erhalten
|
diagnostics.get-support: Unterstützung erhalten
|
||||||
diagnostics.get-support-description: Wenn Sie Probleme mit OliveTin haben und eine Support-Anfrage stellen möchten, wäre es sehr hilfreich, einen sosreport von dieser Seite einzufügen.
|
diagnostics.get-support-description: Wenn Sie Probleme mit OliveTin haben und eine Support-Anfrage stellen möchten, wäre es sehr hilfreich, einen sosreport von dieser Seite einzufügen.
|
||||||
diagnostics.where-to-find-help: Wo Sie Hilfe finden
|
diagnostics.where-to-find-help: Wo Sie Hilfe finden
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ translations:
|
||||||
logs.exit-code: Exit code
|
logs.exit-code: Exit code
|
||||||
logs.completed: Completed
|
logs.completed: Completed
|
||||||
logs.clear-filter: Clear search filter
|
logs.clear-filter: Clear search filter
|
||||||
|
logs.calendar-title: Logs Calendar
|
||||||
|
logs.back-to-list: Back to List
|
||||||
diagnostics.get-support: Get support
|
diagnostics.get-support: Get support
|
||||||
diagnostics.get-support-description: If you are having problems with OliveTin and want to raise a support request, it would be very helpful to include a sosreport from this page.
|
diagnostics.get-support-description: If you are having problems with OliveTin and want to raise a support request, it would be very helpful to include a sosreport from this page.
|
||||||
diagnostics.where-to-find-help: Where to find help
|
diagnostics.where-to-find-help: Where to find help
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ translations:
|
||||||
logs.exit-code: Código de salida
|
logs.exit-code: Código de salida
|
||||||
logs.completed: Completado
|
logs.completed: Completado
|
||||||
logs.clear-filter: Limpiar filtro de búsqueda
|
logs.clear-filter: Limpiar filtro de búsqueda
|
||||||
|
logs.calendar-title: Calendario de Registros
|
||||||
|
logs.back-to-list: Volver a la Lista
|
||||||
diagnostics.get-support: Obtener soporte
|
diagnostics.get-support: Obtener soporte
|
||||||
diagnostics.get-support-description: Si tiene problemas con OliveTin y desea presentar una solicitud de soporte, sería muy útil incluir un sosreport de esta página.
|
diagnostics.get-support-description: Si tiene problemas con OliveTin y desea presentar una solicitud de soporte, sería muy útil incluir un sosreport de esta página.
|
||||||
diagnostics.where-to-find-help: Dónde encontrar ayuda
|
diagnostics.where-to-find-help: Dónde encontrar ayuda
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ translations:
|
||||||
logs.exit-code: Codice di uscita
|
logs.exit-code: Codice di uscita
|
||||||
logs.completed: Completato
|
logs.completed: Completato
|
||||||
logs.clear-filter: Cancella filtro di ricerca
|
logs.clear-filter: Cancella filtro di ricerca
|
||||||
|
logs.calendar-title: Calendario dei Registri
|
||||||
|
logs.back-to-list: Torna all'Elenco
|
||||||
diagnostics.get-support: Ottenere supporto
|
diagnostics.get-support: Ottenere supporto
|
||||||
diagnostics.get-support-description: Se hai problemi con OliveTin e vuoi presentare una richiesta di supporto, sarebbe molto utile includere un sosreport da questa pagina.
|
diagnostics.get-support-description: Se hai problemi con OliveTin e vuoi presentare una richiesta di supporto, sarebbe molto utile includere un sosreport da questa pagina.
|
||||||
diagnostics.where-to-find-help: Dove trovare aiuto
|
diagnostics.where-to-find-help: Dove trovare aiuto
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ translations:
|
||||||
logs.exit-code: 退出代码
|
logs.exit-code: 退出代码
|
||||||
logs.completed: 完成
|
logs.completed: 完成
|
||||||
logs.clear-filter: 清除搜索筛选器
|
logs.clear-filter: 清除搜索筛选器
|
||||||
|
logs.calendar-title: 日志日历
|
||||||
|
logs.back-to-list: 返回列表
|
||||||
diagnostics.get-support: 获取支持
|
diagnostics.get-support: 获取支持
|
||||||
diagnostics.get-support-description: 如果您在使用 OliveTin 时遇到问题并希望提交支持请求,从本页面包含 sosreport 将非常有帮助。
|
diagnostics.get-support-description: 如果您在使用 OliveTin 时遇到问题并希望提交支持请求,从本页面包含 sosreport 将非常有帮助。
|
||||||
diagnostics.where-to-find-help: 在哪里找到帮助
|
diagnostics.where-to-find-help: 在哪里找到帮助
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue