CreateSessions aus characterlist ausgegliedert
common functions un utils.js ausgelagert
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,11 +38,13 @@ var (
|
|||||||
|
|
||||||
func ConnectDatabase() *gorm.DB {
|
func ConnectDatabase() *gorm.DB {
|
||||||
SetupTestDB()
|
SetupTestDB()
|
||||||
db, err := gorm.Open(sqlite.Open(PreparedTestDB), &gorm.Config{})
|
/*
|
||||||
if err != nil {
|
db, err := gorm.Open(sqlite.Open(PreparedTestDB), &gorm.Config{})
|
||||||
log.Fatal("Failed to connect to database:", err)
|
if err != nil {
|
||||||
}
|
log.Fatal("Failed to connect to database:", err)
|
||||||
DB = db
|
}
|
||||||
|
DB = db
|
||||||
|
*/
|
||||||
return DB
|
return DB
|
||||||
}
|
}
|
||||||
func ConnectDatabaseOrig() *gorm.DB {
|
func ConnectDatabaseOrig() *gorm.DB {
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="sessions.length > 0" class="sessions-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<h3>{{ $t('characters.list.continue_creation') }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="grid-container grid-2-columns">
|
||||||
|
<div
|
||||||
|
v-for="session in sessions"
|
||||||
|
:key="session.session_id"
|
||||||
|
class="card session-card"
|
||||||
|
@click="continueSession(session.session_id)"
|
||||||
|
>
|
||||||
|
<div class="session-header">
|
||||||
|
<h4 class="list-item-title">{{ session.name || $t('characters.list.unnamed_character') }}</h4>
|
||||||
|
<span class="badge badge-primary progress-badge">{{ $t('characters.list.step') }} {{ session.current_step }}/{{ session.total_steps }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="session-details">
|
||||||
|
<p class="list-item-details"><strong>{{ $t('characters.list.race') }}:</strong> {{ session.rasse || $t('characters.list.not_selected') }}
|
||||||
|
<span class="list-item-separator">|</span><strong>{{ $t('characters.list.class') }}:</strong> {{ session.typ || $t('characters.list.not_selected') }}
|
||||||
|
<span class="list-item-separator">|</span><strong>{{ $t('characters.list.current_step') }}:</strong> {{ session.progress_text }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="session-meta">
|
||||||
|
<span class="session-date">{{ $t('characters.list.last_updated') }}: {{ formatDate(session.updated_at) }}</span>
|
||||||
|
<span class="session-date">{{ $t('characters.list.expires') }}: {{ formatDate(session.expires_at) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="list-item-actions">
|
||||||
|
<button @click.stop="deleteSession(session.session_id)" class="btn btn-danger btn-small">
|
||||||
|
{{ $t('characters.list.delete_draft') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { formatDate } from '@/utils/dateUtils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CharacterCreationSessions',
|
||||||
|
props: {
|
||||||
|
sessions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
continueSession(sessionId) {
|
||||||
|
this.$emit('continue-session', sessionId)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteSession(sessionId) {
|
||||||
|
this.$emit('delete-session', sessionId)
|
||||||
|
},
|
||||||
|
|
||||||
|
formatDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sessions-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-card {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-details {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-date {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.session-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,51 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fullwidth-container">
|
<div class="fullwidth-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h2>Your Characters</h2>
|
<h2>{{ $t('characters.list.title') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Create New Character Button -->
|
<!-- Create New Character Button -->
|
||||||
<div class="create-character-section">
|
<div class="create-character-section">
|
||||||
<button @click="createNewCharacter" class="btn btn-success btn-large">Create New Character</button>
|
<button @click="createNewCharacter" class="btn btn-success btn-large">{{ $t('characters.list.create_new') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Active Character Creation Sessions -->
|
<!-- Active Character Creation Sessions -->
|
||||||
<div v-if="creationSessions.length > 0" class="sessions-section">
|
<CharacterCreationSessions
|
||||||
<div class="section-header">
|
:sessions="creationSessions"
|
||||||
<h3>Continue Character Creation</h3>
|
@continue-session="continueSession"
|
||||||
</div>
|
@delete-session="handleDeleteSession"
|
||||||
<div class="grid-container grid-2-columns">
|
/>
|
||||||
<div
|
|
||||||
v-for="session in creationSessions"
|
|
||||||
:key="session.session_id"
|
|
||||||
class="card session-card"
|
|
||||||
@click="continueSession(session.session_id)"
|
|
||||||
>
|
|
||||||
<div class="session-header">
|
|
||||||
<h4 class="list-item-title">{{ session.name || 'Unnamed Character' }}</h4>
|
|
||||||
<span class="badge badge-primary progress-badge">Step {{ session.current_step }}/{{ session.total_steps }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="session-details">
|
|
||||||
<p class="list-item-details"><strong>Race:</strong> {{ session.rasse || 'Not selected' }}
|
|
||||||
<span class="list-item-separator">|</span><strong>Class:</strong> {{ session.typ || 'Not selected' }}
|
|
||||||
<span class="list-item-separator">|</span><strong>Current step:</strong> {{ session.progress_text }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="session-meta">
|
|
||||||
<span class="session-date">Last updated: {{ formatDate(session.updated_at) }}</span>
|
|
||||||
<span class="session-date">Expires: {{ formatDate(session.expires_at) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="list-item-actions">
|
|
||||||
<button @click.stop="deleteSession(session.session_id)" class="btn btn-danger btn-small">
|
|
||||||
Delete Draft
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="characters.length === 0" class="empty-state">
|
<div v-if="characters.length === 0" class="empty-state">
|
||||||
<h3>No Characters Yet</h3>
|
<h3>{{ $t('characters.list.no_characters') }}</h3>
|
||||||
<p>Create your first character to get started!</p>
|
<p>{{ $t('characters.list.no_characters_description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="list-container">
|
<div v-else class="list-container">
|
||||||
@@ -55,24 +28,29 @@
|
|||||||
<div class="list-item-details">
|
<div class="list-item-details">
|
||||||
{{ character.rasse }} <span class="list-item-separator">|</span>
|
{{ character.rasse }} <span class="list-item-separator">|</span>
|
||||||
{{ character.typ }} <span class="list-item-separator">|</span>
|
{{ character.typ }} <span class="list-item-separator">|</span>
|
||||||
{{ character.grad }} <span class="list-item-separator">|</span>
|
{{ $t('characters.list.grade') }}: {{ character.grad }} <span class="list-item-separator">|</span>
|
||||||
{{ character.owner }} <span class="list-item-separator">|</span>
|
{{ $t('characters.list.owner') }}: {{ character.owner }} <span class="list-item-separator">|</span>
|
||||||
<span class="badge" :class="character.public ? 'badge-success' : 'badge-secondary'">
|
<span class="badge" :class="character.public ? 'badge-success' : 'badge-secondary'">
|
||||||
{{ character.public ? 'Public' : 'Private' }}
|
{{ character.public ? $t('characters.list.public') : $t('characters.list.private') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-item-actions">
|
<div class="list-item-actions">
|
||||||
<router-link :to="`/character/${character.id}`" class="btn btn-primary">View Details</router-link>
|
<router-link :to="`/character/${character.id}`" class="btn btn-primary">{{ $t('characters.list.view_details') }}</router-link>
|
||||||
<button @click="goToAusruestung(character.character_id)" class="btn btn-secondary">Manage Equipment</button>
|
<button @click="goToAusruestung(character.character_id)" class="btn btn-secondary">{{ $t('characters.list.manage_equipment') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template><script>
|
</template><script>
|
||||||
import API from '../utils/api'
|
import API from '../utils/api'
|
||||||
|
import { formatDate } from '@/utils/dateUtils'
|
||||||
|
import CharacterCreationSessions from './CharacterCreationSessions.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
CharacterCreationSessions
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
characters: [],
|
characters: [],
|
||||||
@@ -114,8 +92,12 @@ export default {
|
|||||||
this.$router.push(`/character/create/${sessionId}`)
|
this.$router.push(`/character/create/${sessionId}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleDeleteSession(sessionId) {
|
||||||
|
this.deleteSession(sessionId)
|
||||||
|
},
|
||||||
|
|
||||||
async deleteSession(sessionId) {
|
async deleteSession(sessionId) {
|
||||||
if (confirm('Are you sure you want to delete this character draft?')) {
|
if (confirm(this.$t('characters.list.delete_draft_confirm'))) {
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
await API.delete(`/api/characters/create-session/${sessionId}`, {
|
await API.delete(`/api/characters/create-session/${sessionId}`, {
|
||||||
@@ -149,10 +131,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
formatDate(dateString) {
|
formatDate
|
||||||
if (!dateString) return 'Unknown'
|
|
||||||
return new Date(dateString).toLocaleDateString()
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -173,52 +152,8 @@ export default {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-small {
|
|
||||||
padding: 5px 10px;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sessions-section {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-card {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-details {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-date {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.session-header {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-item {
|
.list-item {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
@@ -176,6 +176,29 @@ export default {
|
|||||||
total_in_gs: 'Gesamt in GS'
|
total_in_gs: 'Gesamt in GS'
|
||||||
},
|
},
|
||||||
characters: {
|
characters: {
|
||||||
|
list: {
|
||||||
|
title: 'Ihre Charaktere',
|
||||||
|
create_new: 'Neuen Charakter erstellen',
|
||||||
|
continue_creation: 'Charaktererstellung fortsetzen',
|
||||||
|
no_characters: 'Noch keine Charaktere',
|
||||||
|
no_characters_description: 'Erstellen Sie Ihren ersten Charakter um zu beginnen!',
|
||||||
|
view_details: 'Details anzeigen',
|
||||||
|
manage_equipment: 'Ausrüstung verwalten',
|
||||||
|
delete_draft: 'Entwurf löschen',
|
||||||
|
delete_draft_confirm: 'Sind Sie sicher, dass Sie diesen Charakterentwurf löschen möchten?',
|
||||||
|
race: 'Rasse',
|
||||||
|
class: 'Klasse',
|
||||||
|
current_step: 'Aktueller Schritt',
|
||||||
|
step: 'Schritt',
|
||||||
|
last_updated: 'Zuletzt aktualisiert',
|
||||||
|
expires: 'Läuft ab',
|
||||||
|
not_selected: 'Nicht ausgewählt',
|
||||||
|
unnamed_character: 'Unbenannter Charakter',
|
||||||
|
public: 'Öffentlich',
|
||||||
|
private: 'Privat',
|
||||||
|
grade: 'Grad',
|
||||||
|
owner: 'Besitzer'
|
||||||
|
},
|
||||||
create: {
|
create: {
|
||||||
spells: {
|
spells: {
|
||||||
title: 'Zauber auswählen',
|
title: 'Zauber auswählen',
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
export default {
|
export default {
|
||||||
|
DatasheetView:'Datasheet',
|
||||||
|
SkillView: 'Skills',
|
||||||
|
WeaponView: 'Weapons',
|
||||||
|
SpellView: 'Spells',
|
||||||
|
EquipmentView: 'Equipment',
|
||||||
|
ExperianceView: 'Experience & Wealth',
|
||||||
|
DeleteCharView: 'Delete Character',
|
||||||
char:'Char',
|
char:'Char',
|
||||||
stats: {
|
stats: {
|
||||||
strength: 'St',
|
strength: 'St',
|
||||||
@@ -75,6 +82,29 @@ export default {
|
|||||||
total_in_gs: 'Total in GS'
|
total_in_gs: 'Total in GS'
|
||||||
},
|
},
|
||||||
characters: {
|
characters: {
|
||||||
|
list: {
|
||||||
|
title: 'Your Characters',
|
||||||
|
create_new: 'Create New Character',
|
||||||
|
continue_creation: 'Continue Character Creation',
|
||||||
|
no_characters: 'No Characters Yet',
|
||||||
|
no_characters_description: 'Create your first character to get started!',
|
||||||
|
view_details: 'View Details',
|
||||||
|
manage_equipment: 'Manage Equipment',
|
||||||
|
delete_draft: 'Delete Draft',
|
||||||
|
delete_draft_confirm: 'Are you sure you want to delete this character draft?',
|
||||||
|
race: 'Race',
|
||||||
|
class: 'Class',
|
||||||
|
current_step: 'Current step',
|
||||||
|
step: 'Step',
|
||||||
|
last_updated: 'Last updated',
|
||||||
|
expires: 'Expires',
|
||||||
|
not_selected: 'Not selected',
|
||||||
|
unnamed_character: 'Unnamed Character',
|
||||||
|
public: 'Public',
|
||||||
|
private: 'Private',
|
||||||
|
grade: 'Grade',
|
||||||
|
owner: 'Owner'
|
||||||
|
},
|
||||||
create: {
|
create: {
|
||||||
spells: {
|
spells: {
|
||||||
title: 'Select Spells',
|
title: 'Select Spells',
|
||||||
|
|||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* TypeScript Definitionen für Utility-Funktionen
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface DateFormatOptions {
|
||||||
|
year?: 'numeric' | '2-digit'
|
||||||
|
month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow'
|
||||||
|
day?: 'numeric' | '2-digit'
|
||||||
|
hour?: 'numeric' | '2-digit'
|
||||||
|
minute?: 'numeric' | '2-digit'
|
||||||
|
second?: 'numeric' | '2-digit'
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare function formatDate(
|
||||||
|
dateString: string | null | undefined,
|
||||||
|
locale?: string,
|
||||||
|
options?: DateFormatOptions
|
||||||
|
): string
|
||||||
|
|
||||||
|
export declare function formatDateTime(
|
||||||
|
dateString: string | null | undefined,
|
||||||
|
locale?: string
|
||||||
|
): string
|
||||||
|
|
||||||
|
export declare function formatRelativeDate(
|
||||||
|
dateString: string | null | undefined,
|
||||||
|
locale?: string
|
||||||
|
): string
|
||||||
|
|
||||||
|
export declare function safeValue<T>(
|
||||||
|
value: T | null | undefined,
|
||||||
|
fallback?: T
|
||||||
|
): T
|
||||||
|
|
||||||
|
export declare function capitalize(str: string): string
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* Gemeinsame Utility-Funktionen für die gesamte Anwendung
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert ein Datum-String in ein lokales Datumsformat
|
||||||
|
* @param {string} dateString - ISO-Datum-String
|
||||||
|
* @param {string} locale - Sprach-/Ländercode (optional, default: browser locale)
|
||||||
|
* @param {Object} options - Intl.DateTimeFormat Optionen (optional)
|
||||||
|
* @returns {string} - Formatiertes Datum
|
||||||
|
*/
|
||||||
|
export function formatDate(dateString, locale = undefined, options = {}) {
|
||||||
|
if (!dateString) {
|
||||||
|
return 'Unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString)
|
||||||
|
|
||||||
|
// Prüfe ob das Datum gültig ist
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
return 'Invalid Date'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default-Optionen für Datumsformatierung
|
||||||
|
const defaultOptions = {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
|
||||||
|
return date.toLocaleDateString(locale, defaultOptions)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Error formatting date:', error)
|
||||||
|
return 'Invalid Date'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert ein Datum-String mit Uhrzeit
|
||||||
|
* @param {string} dateString - ISO-Datum-String
|
||||||
|
* @param {string} locale - Sprach-/Ländercode (optional)
|
||||||
|
* @returns {string} - Formatiertes Datum mit Uhrzeit
|
||||||
|
*/
|
||||||
|
export function formatDateTime(dateString, locale = undefined) {
|
||||||
|
return formatDate(dateString, locale, {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert ein Datum relativ (z.B. "vor 2 Stunden")
|
||||||
|
* @param {string} dateString - ISO-Datum-String
|
||||||
|
* @param {string} locale - Sprach-/Ländercode (optional)
|
||||||
|
* @returns {string} - Relatives Datum
|
||||||
|
*/
|
||||||
|
export function formatRelativeDate(dateString, locale = undefined) {
|
||||||
|
if (!dateString) {
|
||||||
|
return 'Unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString)
|
||||||
|
const now = new Date()
|
||||||
|
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
return 'Invalid Date'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Für moderne Browser mit Intl.RelativeTimeFormat
|
||||||
|
if (typeof Intl !== 'undefined' && Intl.RelativeTimeFormat) {
|
||||||
|
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })
|
||||||
|
const diffTime = date.getTime() - now.getTime()
|
||||||
|
const diffDays = Math.round(diffTime / (1000 * 60 * 60 * 24))
|
||||||
|
|
||||||
|
if (Math.abs(diffDays) < 1) {
|
||||||
|
const diffHours = Math.round(diffTime / (1000 * 60 * 60))
|
||||||
|
if (Math.abs(diffHours) < 1) {
|
||||||
|
const diffMinutes = Math.round(diffTime / (1000 * 60))
|
||||||
|
return rtf.format(diffMinutes, 'minute')
|
||||||
|
}
|
||||||
|
return rtf.format(diffHours, 'hour')
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtf.format(diffDays, 'day')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback für ältere Browser
|
||||||
|
return formatDate(dateString, locale)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Error formatting relative date:', error)
|
||||||
|
return formatDate(dateString, locale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weitere gemeinsame Utility-Funktionen können hier hinzugefügt werden
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sicherheits-Check für leere Werte
|
||||||
|
* @param {*} value - Zu prüfender Wert
|
||||||
|
* @param {*} fallback - Fallback-Wert
|
||||||
|
* @returns {*} - Wert oder Fallback
|
||||||
|
*/
|
||||||
|
export function safeValue(value, fallback = '-') {
|
||||||
|
return value != null && value !== '' ? value : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kapitalisiert den ersten Buchstaben eines Strings
|
||||||
|
* @param {string} str - Input String
|
||||||
|
* @returns {string} - String mit großem ersten Buchstaben
|
||||||
|
*/
|
||||||
|
export function capitalize(str) {
|
||||||
|
if (!str || typeof str !== 'string') return str
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Vue Plugin für globale Utility-Funktionen
|
||||||
|
*
|
||||||
|
* Usage in main.js:
|
||||||
|
* import UtilsPlugin from './utils/utilsPlugin'
|
||||||
|
* app.use(UtilsPlugin)
|
||||||
|
*
|
||||||
|
* Usage in components:
|
||||||
|
* this.$formatDate(dateString)
|
||||||
|
* this.$safeValue(value, 'fallback')
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { formatDate, formatDateTime, formatRelativeDate, safeValue, capitalize } from './dateUtils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install(app) {
|
||||||
|
// Globale Properties für Vue 3
|
||||||
|
app.config.globalProperties.$formatDate = formatDate
|
||||||
|
app.config.globalProperties.$formatDateTime = formatDateTime
|
||||||
|
app.config.globalProperties.$formatRelativeDate = formatRelativeDate
|
||||||
|
app.config.globalProperties.$safeValue = safeValue
|
||||||
|
app.config.globalProperties.$capitalize = capitalize
|
||||||
|
|
||||||
|
// Provide/Inject für Composition API
|
||||||
|
app.provide('utils', {
|
||||||
|
formatDate,
|
||||||
|
formatDateTime,
|
||||||
|
formatRelativeDate,
|
||||||
|
safeValue,
|
||||||
|
capitalize
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user