fix: standardize frontend date formatting with shared utility (Closes #175)
This commit was merged in pull request #186.
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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 = {
|
||||
|
||||
@@ -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 '--'
|
||||
|
||||
@@ -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 ''
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 '—'
|
||||
|
||||
@@ -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 } })
|
||||
|
||||
Reference in New Issue
Block a user