fix: standardize frontend date formatting with shared utility (Closes #175)

This commit was merged in pull request #186.
This commit is contained in:
2026-04-10 18:45:25 +02:00
parent 21b025af87
commit af006d7250
10 changed files with 91 additions and 68 deletions
+2 -12
View File
@@ -200,6 +200,7 @@ import { reactive, ref, computed, onMounted } from 'vue'
import { NcButton, NcLoadingIcon } from '@nextcloud/vue'
import { useFeesStore } from '../stores/fees.js'
import Plus from 'vue-material-design-icons/Plus.vue'
import { formatDate } from '../utils/dateFormat.js'
const feesStore = useFeesStore()
@@ -312,18 +313,7 @@ function formatCurrency(value) {
}).format(value)
}
function formatDate(dateStr) {
if (!dateStr) return '--'
try {
return new Date(dateStr).toLocaleDateString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
} catch {
return dateStr
}
}
// formatDate imported from utils/dateFormat.js
</script>
<style scoped>
+3 -18
View File
@@ -74,7 +74,7 @@
{{ file.type === 'folder' ? '—' : formatSize(file.size) }}
</td>
<td class="file-explorer__td file-explorer__td--date">
{{ formatDate(file.mtime) }}
{{ formatTimestamp(file.mtime) }}
</td>
</tr>
</tbody>
@@ -93,6 +93,7 @@ import Folder from 'vue-material-design-icons/Folder.vue'
import FolderPlus from 'vue-material-design-icons/FolderPlus.vue'
import File from 'vue-material-design-icons/File.vue'
import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
import { formatTimestamp } from '../utils/dateFormat.js'
const props = defineProps({
memberId: {
@@ -172,23 +173,7 @@ function formatSize(bytes) {
return size + ' ' + units[i]
}
/**
* Format a Unix timestamp as a human-readable date.
*
* @param {number} timestamp - Unix timestamp in seconds
* @returns {string} Formatted date
*/
function formatDate(timestamp) {
if (!timestamp) return '—'
const date = new Date(timestamp * 1000)
return date.toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
}
// formatTimestamp imported from utils/dateFormat.js
</script>
<style scoped>
+71
View File
@@ -0,0 +1,71 @@
/**
* Shared date formatting utilities for consistent German date display.
*
* All views should use these functions instead of inline toLocaleDateString()
* calls to ensure consistent formatting across the application.
*
* Part of Issue #175.
*/
/**
* Format a date string (ISO 8601) as a German date (dd.mm.yyyy).
*
* @param {string|null|undefined} dateStr ISO date string (e.g. "2026-04-10")
* @returns {string} Formatted date or em-dash if empty
*/
export function formatDate(dateStr) {
if (!dateStr) return '\u2014'
try {
return new Date(dateStr).toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
})
} catch {
return dateStr
}
}
/**
* Format a date string (ISO 8601) as a German date+time (dd.mm.yyyy, hh:mm:ss).
*
* @param {string|null|undefined} dateStr ISO datetime string
* @returns {string} Formatted datetime or em-dash if empty
*/
export function formatDateTime(dateStr) {
if (!dateStr) return '\u2014'
try {
return new Date(dateStr).toLocaleString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
})
} catch {
return dateStr
}
}
/**
* Format a Unix timestamp (seconds) as a German date+time.
*
* @param {number|null|undefined} timestamp Unix timestamp in seconds
* @returns {string} Formatted datetime or em-dash if empty
*/
export function formatTimestamp(timestamp) {
if (!timestamp) return '\u2014'
try {
const date = new Date(timestamp * 1000)
return date.toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
} catch {
return String(timestamp)
}
}
+3 -13
View File
@@ -91,7 +91,7 @@
:key="entry.id"
class="audit-log__row">
<td class="audit-log__td audit-log__td--time">
{{ formatDate(entry.zeitpunkt) }}
{{ formatDateTime(entry.zeitpunkt) }}
</td>
<td class="audit-log__td">
{{ entry.ncUserId }}
@@ -148,6 +148,7 @@ import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { NcButton, NcTextField, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
import { useAuditLogStore } from '../stores/auditlog.js'
import { formatDateTime } from '../utils/dateFormat.js'
const store = useAuditLogStore()
const router = useRouter()
@@ -170,18 +171,7 @@ function reload() {
store.fetchEntries()
}
function formatDate(dateStr) {
if (!dateStr) return '—'
const d = new Date(dateStr)
return d.toLocaleString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
})
}
// formatDateTime imported from utils/dateFormat.js
function formatAktion(aktion) {
const map = {
+2 -4
View File
@@ -208,6 +208,7 @@ import { ref, computed, onMounted } from 'vue'
import { NcButton, NcLoadingIcon, NcEmptyContent } from '@nextcloud/vue'
import { useFeesStore } from '../stores/fees.js'
import { useMembersStore } from '../stores/members.js'
import { formatDate } from '../utils/dateFormat.js'
const feesStore = useFeesStore()
const membersStore = useMembersStore()
@@ -327,10 +328,7 @@ async function saveNotes(recordId) {
// ── Formatting ──────────────────────────────────────────────────────
function formatDate(dateStr) {
if (!dateStr) return '--'
return new Date(dateStr).toLocaleDateString('de-DE')
}
// formatDate imported from utils/dateFormat.js
function formatCurrency(value) {
if (value === null || value === undefined || isNaN(value)) return '--'
+2 -4
View File
@@ -95,6 +95,7 @@ import { ref, onMounted } from 'vue'
import { NcButton } from '@nextcloud/vue'
import { useInjuryStore } from '../stores/injuries.js'
import InjuryForm from '../components/InjuryForm.vue'
import { formatDate } from '../utils/dateFormat.js'
const injuryStore = useInjuryStore()
@@ -103,10 +104,7 @@ const editingInjury = ref(null)
const filterDateFrom = ref('')
const filterDateTo = ref('')
function formatDate(dateStr) {
if (!dateStr) return '-'
return new Date(dateStr).toLocaleDateString('de-DE')
}
// formatDate imported from utils/dateFormat.js
function truncate(text, maxLen) {
if (!text) return ''
+2 -4
View File
@@ -101,6 +101,7 @@ import { ref, computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { NcButton } from '@nextcloud/vue'
import { useLagerStore } from '../stores/lager.js'
import { formatDate } from '../utils/dateFormat.js'
const route = useRoute()
const lagerStore = useLagerStore()
@@ -110,10 +111,7 @@ const newRolle = ref('Teilnehmer')
const camp = computed(() => lagerStore.currentCamp)
function formatDate(dateStr) {
if (!dateStr) return '-'
return new Date(dateStr).toLocaleDateString('de-DE')
}
// formatDate imported from utils/dateFormat.js
async function doAddTeilnehmer() {
if (!newMemberId.value) return
+2 -5
View File
@@ -121,6 +121,7 @@ import { ref, onMounted } from 'vue'
import { NcButton } from '@nextcloud/vue'
import { useLagerStore } from '../stores/lager.js'
import { useStufenStore } from '../stores/stufen.js'
import { formatDate } from '../utils/dateFormat.js'
const lagerStore = useLagerStore()
const stufenStore = useStufenStore()
@@ -128,11 +129,7 @@ const stufenStore = useStufenStore()
const showCreate = ref(false)
const newCamp = ref({ name: '', startdatum: '', enddatum: '', ort: '', beschreibung: '' })
function formatDate(dateStr) {
if (!dateStr) return '-'
const d = new Date(dateStr)
return d.toLocaleDateString('de-DE')
}
// formatDate imported from utils/dateFormat.js
function onYearChange(val) {
lagerStore.filterYear = val ? parseInt(val) : null
+2 -4
View File
@@ -147,6 +147,7 @@ import Plus from 'vue-material-design-icons/Plus.vue'
import AlertCircle from 'vue-material-design-icons/AlertCircle.vue'
import AccountGroup from 'vue-material-design-icons/AccountGroup.vue'
import SortIcon from '../components/SortIcon.vue'
import { formatDate } from '../utils/dateFormat.js'
const store = useMembersStore()
const stufenStore = useStufenStore()
@@ -235,10 +236,7 @@ function getStufeNameById(stufeId) {
/**
* Calculate age from birthday string.
*/
function formatDate(dateStr) {
if (!dateStr) return '—'
return new Date(dateStr).toLocaleDateString('de-DE')
}
// formatDate imported from utils/dateFormat.js
function calculateAge(geburtsdatum) {
if (!geburtsdatum) return '—'
+2 -4
View File
@@ -128,6 +128,7 @@ import QueryBuilderComponent from '../components/QueryBuilder.vue'
import Delete from 'vue-material-design-icons/Delete.vue'
import Play from 'vue-material-design-icons/Play.vue'
import ContentSave from 'vue-material-design-icons/ContentSave.vue'
import { formatDate } from '../utils/dateFormat.js'
const router = useRouter()
const store = useQueriesStore()
@@ -195,10 +196,7 @@ function resetQuery() {
store.clearResults()
}
function formatDate(dateStr) {
if (!dateStr) return '—'
return new Date(dateStr).toLocaleDateString('de-DE')
}
// formatDate imported from utils/dateFormat.js
function openMember(id) {
router.push({ name: 'member-detail', params: { id } })