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 { NcButton, NcLoadingIcon } from '@nextcloud/vue'
|
||||||
import { useFeesStore } from '../stores/fees.js'
|
import { useFeesStore } from '../stores/fees.js'
|
||||||
import Plus from 'vue-material-design-icons/Plus.vue'
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
||||||
|
import { formatDate } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const feesStore = useFeesStore()
|
const feesStore = useFeesStore()
|
||||||
|
|
||||||
@@ -312,18 +313,7 @@ function formatCurrency(value) {
|
|||||||
}).format(value)
|
}).format(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
// formatDate imported from utils/dateFormat.js
|
||||||
if (!dateStr) return '--'
|
|
||||||
try {
|
|
||||||
return new Date(dateStr).toLocaleDateString('de-DE', {
|
|
||||||
year: 'numeric',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
return dateStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
{{ file.type === 'folder' ? '—' : formatSize(file.size) }}
|
{{ file.type === 'folder' ? '—' : formatSize(file.size) }}
|
||||||
</td>
|
</td>
|
||||||
<td class="file-explorer__td file-explorer__td--date">
|
<td class="file-explorer__td file-explorer__td--date">
|
||||||
{{ formatDate(file.mtime) }}
|
{{ formatTimestamp(file.mtime) }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -93,6 +93,7 @@ import Folder from 'vue-material-design-icons/Folder.vue'
|
|||||||
import FolderPlus from 'vue-material-design-icons/FolderPlus.vue'
|
import FolderPlus from 'vue-material-design-icons/FolderPlus.vue'
|
||||||
import File from 'vue-material-design-icons/File.vue'
|
import File from 'vue-material-design-icons/File.vue'
|
||||||
import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
|
import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
|
||||||
|
import { formatTimestamp } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
memberId: {
|
memberId: {
|
||||||
@@ -172,23 +173,7 @@ function formatSize(bytes) {
|
|||||||
return size + ' ' + units[i]
|
return size + ' ' + units[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// formatTimestamp imported from utils/dateFormat.js
|
||||||
* 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',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<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"
|
:key="entry.id"
|
||||||
class="audit-log__row">
|
class="audit-log__row">
|
||||||
<td class="audit-log__td audit-log__td--time">
|
<td class="audit-log__td audit-log__td--time">
|
||||||
{{ formatDate(entry.zeitpunkt) }}
|
{{ formatDateTime(entry.zeitpunkt) }}
|
||||||
</td>
|
</td>
|
||||||
<td class="audit-log__td">
|
<td class="audit-log__td">
|
||||||
{{ entry.ncUserId }}
|
{{ entry.ncUserId }}
|
||||||
@@ -148,6 +148,7 @@ import { onMounted } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { NcButton, NcTextField, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
import { NcButton, NcTextField, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
||||||
import { useAuditLogStore } from '../stores/auditlog.js'
|
import { useAuditLogStore } from '../stores/auditlog.js'
|
||||||
|
import { formatDateTime } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const store = useAuditLogStore()
|
const store = useAuditLogStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -170,18 +171,7 @@ function reload() {
|
|||||||
store.fetchEntries()
|
store.fetchEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
// formatDateTime imported from utils/dateFormat.js
|
||||||
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',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatAktion(aktion) {
|
function formatAktion(aktion) {
|
||||||
const map = {
|
const map = {
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { NcButton, NcLoadingIcon, NcEmptyContent } from '@nextcloud/vue'
|
import { NcButton, NcLoadingIcon, NcEmptyContent } from '@nextcloud/vue'
|
||||||
import { useFeesStore } from '../stores/fees.js'
|
import { useFeesStore } from '../stores/fees.js'
|
||||||
import { useMembersStore } from '../stores/members.js'
|
import { useMembersStore } from '../stores/members.js'
|
||||||
|
import { formatDate } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const feesStore = useFeesStore()
|
const feesStore = useFeesStore()
|
||||||
const membersStore = useMembersStore()
|
const membersStore = useMembersStore()
|
||||||
@@ -327,10 +328,7 @@ async function saveNotes(recordId) {
|
|||||||
|
|
||||||
// ── Formatting ──────────────────────────────────────────────────────
|
// ── Formatting ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
// formatDate imported from utils/dateFormat.js
|
||||||
if (!dateStr) return '--'
|
|
||||||
return new Date(dateStr).toLocaleDateString('de-DE')
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatCurrency(value) {
|
function formatCurrency(value) {
|
||||||
if (value === null || value === undefined || isNaN(value)) return '--'
|
if (value === null || value === undefined || isNaN(value)) return '--'
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { NcButton } from '@nextcloud/vue'
|
import { NcButton } from '@nextcloud/vue'
|
||||||
import { useInjuryStore } from '../stores/injuries.js'
|
import { useInjuryStore } from '../stores/injuries.js'
|
||||||
import InjuryForm from '../components/InjuryForm.vue'
|
import InjuryForm from '../components/InjuryForm.vue'
|
||||||
|
import { formatDate } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const injuryStore = useInjuryStore()
|
const injuryStore = useInjuryStore()
|
||||||
|
|
||||||
@@ -103,10 +104,7 @@ const editingInjury = ref(null)
|
|||||||
const filterDateFrom = ref('')
|
const filterDateFrom = ref('')
|
||||||
const filterDateTo = ref('')
|
const filterDateTo = ref('')
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
// formatDate imported from utils/dateFormat.js
|
||||||
if (!dateStr) return '-'
|
|
||||||
return new Date(dateStr).toLocaleDateString('de-DE')
|
|
||||||
}
|
|
||||||
|
|
||||||
function truncate(text, maxLen) {
|
function truncate(text, maxLen) {
|
||||||
if (!text) return ''
|
if (!text) return ''
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { NcButton } from '@nextcloud/vue'
|
import { NcButton } from '@nextcloud/vue'
|
||||||
import { useLagerStore } from '../stores/lager.js'
|
import { useLagerStore } from '../stores/lager.js'
|
||||||
|
import { formatDate } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const lagerStore = useLagerStore()
|
const lagerStore = useLagerStore()
|
||||||
@@ -110,10 +111,7 @@ const newRolle = ref('Teilnehmer')
|
|||||||
|
|
||||||
const camp = computed(() => lagerStore.currentCamp)
|
const camp = computed(() => lagerStore.currentCamp)
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
// formatDate imported from utils/dateFormat.js
|
||||||
if (!dateStr) return '-'
|
|
||||||
return new Date(dateStr).toLocaleDateString('de-DE')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doAddTeilnehmer() {
|
async function doAddTeilnehmer() {
|
||||||
if (!newMemberId.value) return
|
if (!newMemberId.value) return
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { NcButton } from '@nextcloud/vue'
|
import { NcButton } from '@nextcloud/vue'
|
||||||
import { useLagerStore } from '../stores/lager.js'
|
import { useLagerStore } from '../stores/lager.js'
|
||||||
import { useStufenStore } from '../stores/stufen.js'
|
import { useStufenStore } from '../stores/stufen.js'
|
||||||
|
import { formatDate } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const lagerStore = useLagerStore()
|
const lagerStore = useLagerStore()
|
||||||
const stufenStore = useStufenStore()
|
const stufenStore = useStufenStore()
|
||||||
@@ -128,11 +129,7 @@ const stufenStore = useStufenStore()
|
|||||||
const showCreate = ref(false)
|
const showCreate = ref(false)
|
||||||
const newCamp = ref({ name: '', startdatum: '', enddatum: '', ort: '', beschreibung: '' })
|
const newCamp = ref({ name: '', startdatum: '', enddatum: '', ort: '', beschreibung: '' })
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
// formatDate imported from utils/dateFormat.js
|
||||||
if (!dateStr) return '-'
|
|
||||||
const d = new Date(dateStr)
|
|
||||||
return d.toLocaleDateString('de-DE')
|
|
||||||
}
|
|
||||||
|
|
||||||
function onYearChange(val) {
|
function onYearChange(val) {
|
||||||
lagerStore.filterYear = val ? parseInt(val) : null
|
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 AlertCircle from 'vue-material-design-icons/AlertCircle.vue'
|
||||||
import AccountGroup from 'vue-material-design-icons/AccountGroup.vue'
|
import AccountGroup from 'vue-material-design-icons/AccountGroup.vue'
|
||||||
import SortIcon from '../components/SortIcon.vue'
|
import SortIcon from '../components/SortIcon.vue'
|
||||||
|
import { formatDate } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const store = useMembersStore()
|
const store = useMembersStore()
|
||||||
const stufenStore = useStufenStore()
|
const stufenStore = useStufenStore()
|
||||||
@@ -235,10 +236,7 @@ function getStufeNameById(stufeId) {
|
|||||||
/**
|
/**
|
||||||
* Calculate age from birthday string.
|
* Calculate age from birthday string.
|
||||||
*/
|
*/
|
||||||
function formatDate(dateStr) {
|
// formatDate imported from utils/dateFormat.js
|
||||||
if (!dateStr) return '—'
|
|
||||||
return new Date(dateStr).toLocaleDateString('de-DE')
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateAge(geburtsdatum) {
|
function calculateAge(geburtsdatum) {
|
||||||
if (!geburtsdatum) return '—'
|
if (!geburtsdatum) return '—'
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ import QueryBuilderComponent from '../components/QueryBuilder.vue'
|
|||||||
import Delete from 'vue-material-design-icons/Delete.vue'
|
import Delete from 'vue-material-design-icons/Delete.vue'
|
||||||
import Play from 'vue-material-design-icons/Play.vue'
|
import Play from 'vue-material-design-icons/Play.vue'
|
||||||
import ContentSave from 'vue-material-design-icons/ContentSave.vue'
|
import ContentSave from 'vue-material-design-icons/ContentSave.vue'
|
||||||
|
import { formatDate } from '../utils/dateFormat.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const store = useQueriesStore()
|
const store = useQueriesStore()
|
||||||
@@ -195,10 +196,7 @@ function resetQuery() {
|
|||||||
store.clearResults()
|
store.clearResults()
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
// formatDate imported from utils/dateFormat.js
|
||||||
if (!dateStr) return '—'
|
|
||||||
return new Date(dateStr).toLocaleDateString('de-DE')
|
|
||||||
}
|
|
||||||
|
|
||||||
function openMember(id) {
|
function openMember(id) {
|
||||||
router.push({ name: 'member-detail', params: { id } })
|
router.push({ name: 'member-detail', params: { id } })
|
||||||
|
|||||||
Reference in New Issue
Block a user