Added new pages

This commit is contained in:
2026-01-13 23:34:35 +01:00
parent 24919dcd64
commit 4972816c75
12 changed files with 878 additions and 134 deletions
+7 -5
View File
@@ -2,10 +2,10 @@
# API Configuration
# API_URL=http://localhost:8180
ENVIRONMENT=development
# Database Configuration (for development)
DATABASE_TYPE=mysql
DATABASE_URL="bamort:bG4)efozrc@tcp(mariadb-dev:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local"
DATABASE_URL="bamort:bG4)efozrc@tcp(192.168.0.36:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local"
# MariaDB Configuration (development)
MARIADB_ROOT_PASSWORD=root_password_dev
@@ -14,8 +14,8 @@ MARIADB_DATABASE=bamort
MARIADB_USER=bamort
# Frontend Configuration
API_URL="http://192.168.0.36:8180"
VITE_API_URL="http://192.168.0.36:8180"
API_URL="http://localhost:8180"
VITE_API_URL="http://localhost:8180"
API_PORT=8180
BASE_URL="http://localhost:5173"
TEMPLATES_DIR=./templates
@@ -26,4 +26,6 @@ COMPOSE_PROJECT_NAME=bamort
CHROME_BIN="/usr/bin/chromium"
#./server
/usr/local/go/bin/go run ./cmd/main.go
#/usr/local/go/bin/go run ./cmd/main.go
echo $DATABASE_URL
/home/de31a2/.local/bin/go/bin/go run ./cmd/main.go
+29 -27
View File
@@ -7,6 +7,7 @@
"": {
"name": "bamort-frontend",
"version": "0.1.23",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"axios": "^1.7.9",
"pinia": "^2.3.0",
@@ -859,12 +860,12 @@
}
},
"node_modules/@intlify/core-base": {
"version": "9.14.4",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.4.tgz",
"integrity": "sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
"dependencies": {
"@intlify/message-compiler": "9.14.4",
"@intlify/shared": "9.14.4"
"@intlify/message-compiler": "9.14.5",
"@intlify/shared": "9.14.5"
},
"engines": {
"node": ">= 16"
@@ -874,11 +875,11 @@
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.14.4",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.4.tgz",
"integrity": "sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
"dependencies": {
"@intlify/shared": "9.14.4",
"@intlify/shared": "9.14.5",
"source-map-js": "^1.0.2"
},
"engines": {
@@ -889,9 +890,9 @@
}
},
"node_modules/@intlify/shared": {
"version": "9.14.4",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.4.tgz",
"integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
"engines": {
"node": ">= 16"
},
@@ -1482,12 +1483,12 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
@@ -1904,9 +1905,9 @@
}
},
"node_modules/form-data": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -2724,9 +2725,9 @@
}
},
"node_modules/vite": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"dependencies": {
"esbuild": "^0.25.0",
@@ -2927,12 +2928,13 @@
}
},
"node_modules/vue-i18n": {
"version": "9.14.4",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.4.tgz",
"integrity": "sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
"deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
"dependencies": {
"@intlify/core-base": "9.14.4",
"@intlify/shared": "9.14.4",
"@intlify/core-base": "9.14.5",
"@intlify/shared": "9.14.5",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
+186 -12
View File
@@ -57,41 +57,134 @@ a:focus {
/* Top Navigation Bar */
.top-nav {
display: flex;
justify-content: space-around;
justify-content: space-between;
align-items: center;
background-color: var(--vt-c-black-soft);
/* Use your palette from base.css */
color: var(--vt-c-white);
height: 60px;
width: 100%;
/* Full width */
position: fixed;
/* Fix it to the top */
top: 0;
left: 0;
z-index: 1000;
padding: 0 12px;
padding: 0 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.top-nav ul {
.top-nav .menu-left {
list-style: none;
display: flex;
gap: 20px;
gap: 5px;
padding: 0;
margin: 0;
align-items: center;
}
.top-nav li a {
.top-nav li {
position: relative;
}
.top-nav li a,
.top-nav .dropdown-trigger {
text-decoration: none;
color: var(--vt-c-white);
font-size: 1rem;
transition: color 0.3s;
padding: 8px 16px;
display: block;
border-radius: 4px;
transition: background-color 0.2s, color 0.2s;
cursor: pointer;
white-space: nowrap;
}
.top-nav li a:hover {
color: var(--vt-c-indigo);
/* Highlight on hover */
.top-nav li a:hover,
.top-nav .dropdown-trigger:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.top-nav li a.active {
background-color: rgba(255, 255, 255, 0.2);
font-weight: 600;
}
/* Dropdown Menu Styles */
.top-nav .dropdown {
position: relative;
}
.top-nav .dropdown-menu {
position: absolute;
top: 100%;
left: 0;
background-color: var(--vt-c-black-soft);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 4px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
min-width: 180px;
list-style: none;
padding: 8px 0;
margin: 4px 0 0 0;
z-index: 1001;
}
.top-nav .dropdown-menu li {
margin: 0;
}
.top-nav .dropdown-menu a {
padding: 10px 20px;
border-radius: 0;
display: block;
}
.top-nav .dropdown-menu a:hover {
background-color: rgba(255, 255, 255, 0.15);
}
.top-nav .dropdown-menu a.router-link-active {
background-color: rgba(255, 255, 255, 0.2);
font-weight: 600;
}
/* Menu Right Side */
.menu-right {
display: flex;
align-items: center;
gap: 10px;
}
.profile-link {
color: var(--vt-c-white);
text-decoration: none;
padding: 8px 16px;
border-radius: 4px;
transition: background-color 0.2s;
white-space: nowrap;
}
.profile-link:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.profile-link.active {
background-color: rgba(255, 255, 255, 0.2);
font-weight: 600;
}
.logout-btn {
background: none;
border: none;
color: var(--vt-c-white);
cursor: pointer;
padding: 8px 16px;
border-radius: 4px;
font-size: 1rem;
transition: background-color 0.2s;
white-space: nowrap;
}
.logout-btn:hover {
background-color: rgba(255, 255, 255, 0.1);
}
/* Main Content Area */
@@ -141,6 +234,37 @@ a:focus {
transition: all 0.3s ease;
}
.card h4 {
margin: 0 0 10px 0;
color: #333;
font-size: 1.1rem;
}
.card p {
margin: 5px 0;
color: #495057;
line-height: 1.6;
}
.card ul {
margin: 10px 0;
padding-left: 20px;
}
.card li {
margin: 8px 0;
color: #495057;
line-height: 1.6;
}
.card code {
background: #f4f4f4;
padding: 2px 6px;
border-radius: 3px;
font-family: monospace;
font-size: 0.9em;
}
/* Common Grid Layouts */
.grid-container {
display: grid;
@@ -165,6 +289,8 @@ a:focus {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #007bff;
display: flex;
flex-direction: column;
}
.page-header h2,
@@ -174,6 +300,11 @@ a:focus {
font-size: 1.5rem;
}
.back-button {
margin-bottom: 10px;
align-self: flex-start;
}
.section-header {
margin: 30px 0 15px 0;
padding-bottom: 8px;
@@ -255,6 +386,22 @@ a:focus {
background: #218838;
}
/* Status Indicator Styles */
.status-available {
color: #28a745;
font-weight: 600;
}
.status-unavailable {
color: #dc3545;
font-weight: 600;
}
.status-loading {
color: #ffc107;
font-weight: 600;
}
/* Common Form Elements */
.form-group {
margin-bottom: 15px;
@@ -3905,6 +4052,7 @@ a:focus {
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 1.5rem;
}
.action-links .btn {
@@ -3913,6 +4061,32 @@ a:focus {
padding: 12px 24px;
}
.quick-links {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
margin-top: 1rem;
}
.quick-link {
color: rgba(255, 255, 255, 0.9);
text-decoration: none;
font-size: 0.95rem;
padding: 8px 16px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.1);
transition: all 0.2s ease;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.quick-link:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.4);
text-decoration: none;
transform: translateY(-2px);
}
@media (max-width: 768px) {
.landing-content {
padding: 1rem;
+65 -75
View File
@@ -1,31 +1,51 @@
<template>
<nav class="top-nav"><!---<nav class="menu"> --->
<ul>
<nav class="top-nav">
<ul class="menu-left">
<!-- Home -->
<li>
<router-link to="/" active-class="active">{{ $t('menu.Home') }}</router-link>
</li>
<li>
<router-link to="/dashboard" active-class="active">{{ $t('menu.Dashboard') }}</router-link>
<!-- Info Dropdown -->
<li class="dropdown" @mouseenter="openMenu('info')" @mouseleave="closeMenu('info')">
<span class="dropdown-trigger">{{ $t('menu.Info') }} </span>
<ul v-show="showInfoMenu" class="dropdown-menu" @mouseenter="openMenu('info')" @mouseleave="closeMenu('info')">
<li><router-link to="/help" @click="closeAllMenus">{{ $t('menu.Help') }}</router-link></li>
<li><router-link to="/sponsors" @click="closeAllMenus">{{ $t('menu.Sponsors') }}</router-link></li>
<li><router-link to="/system-info" @click="closeAllMenus">{{ $t('menu.SystemInfo') }}</router-link></li>
</ul>
</li>
<li>
<router-link to="/upload" active-class="active">{{ $t('menu.ImportData') }}</router-link>
<!-- Characters Dropdown (only when logged in) -->
<li v-if="isLoggedIn" class="dropdown" @mouseenter="openMenu('char')" @mouseleave="closeMenu('char')">
<span class="dropdown-trigger">{{ $t('menu.Characters') }} </span>
<ul v-show="showCharMenu" class="dropdown-menu" @mouseenter="openMenu('char')" @mouseleave="closeMenu('char')">
<li><router-link to="/dashboard" @click="closeAllMenus">{{ $t('menu.Dashboard') }}</router-link></li>
<li><router-link to="/upload" @click="closeAllMenus">{{ $t('menu.ImportData') }}</router-link></li>
</ul>
</li>
<li v-if="isLoggedIn">
<button @click="logout">{{ $t('menu.Logout') }}</button>
<!-- Admin Dropdown (only for maintainers/admins) -->
<li v-if="isLoggedIn && (isMaintainer || isAdmin)" class="dropdown" @mouseenter="openMenu('admin')" @mouseleave="closeMenu('admin')">
<span class="dropdown-trigger">{{ $t('menu.Admin') }} </span>
<ul v-show="showAdminMenu" class="dropdown-menu" @mouseenter="openMenu('admin')" @mouseleave="closeMenu('admin')">
<li v-if="isMaintainer"><router-link to="/maintenance" @click="closeAllMenus">{{ $t('menu.Maintenance') }}</router-link></li>
<li v-if="isAdmin"><router-link to="/users" @click="closeAllMenus">{{ $t('menu.UserManagement') }}</router-link></li>
</ul>
</li>
<!-- Register (only when not logged in) -->
<li v-if="!isLoggedIn">
<router-link to="/register" active-class="active">{{ $t('menu.Register') }}</router-link>
</li>
<li v-if="isLoggedIn && isMaintainer">
<router-link to="/maintenance" active-class="active">{{ $t('menu.Maintenance') }}</router-link>
</li>
<li v-if="isLoggedIn && isAdmin">
<router-link to="/users" active-class="active">{{ $t('menu.UserManagement') }}</router-link>
</li>
</ul>
<div class="menu-right">
<LanguageSwitcher />
<router-link v-if="isLoggedIn" to="/profile" active-class="active" class="profile-link">{{ $t('menu.Profile') }}</router-link>
<router-link v-if="isLoggedIn" to="/profile" active-class="active" class="profile-link">
{{ $t('menu.Profile') }}
</router-link>
<button v-if="isLoggedIn" @click="logout" class="logout-btn">{{ $t('menu.Logout') }}</button>
</div>
</nav>
</template>
@@ -43,7 +63,11 @@ export default {
},
data() {
return {
userStore: null
userStore: null,
showInfoMenu: false,
showCharMenu: false,
showAdminMenu: false,
closeTimeout: null
}
},
async created() {
@@ -69,6 +93,31 @@ export default {
}
},
methods: {
openMenu(menu) {
// Clear any pending close timeout
if (this.closeTimeout) {
clearTimeout(this.closeTimeout)
this.closeTimeout = null
}
// Open the requested menu
if (menu === 'info') this.showInfoMenu = true
if (menu === 'char') this.showCharMenu = true
if (menu === 'admin') this.showAdminMenu = true
},
closeMenu(menu) {
// Delay closing to allow mouse to move to submenu
this.closeTimeout = setTimeout(() => {
if (menu === 'info') this.showInfoMenu = false
if (menu === 'char') this.showCharMenu = false
if (menu === 'admin') this.showAdminMenu = false
}, 200)
},
closeAllMenus() {
this.showInfoMenu = false
this.showCharMenu = false
this.showAdminMenu = false
},
logout() {
logout();
this.userStore.clearUser()
@@ -86,62 +135,3 @@ export default {
},
};
</script>
<style>
.menu {
background-color: #333;
color: white;
padding: 1rem;
}
.menu ul {
list-style: none;
display: flex;
gap: 1rem;
}
.menu a {
color: white;
text-decoration: none;
}
.menu a:hover {
text-decoration: underline;
}
.menu .active {
font-weight: bold;
text-decoration: underline;
}
.menu button {
background: none;
border: none;
color: white;
cursor: pointer;
}
.menu-right {
display: flex;
align-items: center;
gap: 1rem;
}
.profile-link {
color: white;
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: var(--border-radius);
transition: background-color 0.2s;
}
.profile-link:hover {
background-color: rgba(255, 255, 255, 0.1);
text-decoration: none;
}
.profile-link.active {
background-color: rgba(255, 255, 255, 0.2);
font-weight: bold;
}
</style>
+93 -7
View File
@@ -1,4 +1,11 @@
export default {
common: {
loading: 'Laden...',
cancel: 'Abbrechen',
previous: 'Zurück',
next: 'Weiter',
back: 'Zurück',
},
DatasheetView:'Datenblatt',
SkillView: 'Fertigkeiten',
WeaponView: 'Waffen',
@@ -45,6 +52,12 @@ export default {
Maintenance:'Wartung',
UserManagement:'Benutzerverwaltung',
Home:'Startseite',
Help:'Hilfe',
Sponsors:'Sponsoren',
SystemInfo:'Systeminfo',
Info:'Info',
Characters:'Charaktere',
Admin:'Administration',
},
landing:{
title:'BaMoRT - Charakterverwaltung für mein Lieblingsrollenspielsystem',
@@ -55,6 +68,9 @@ export default {
commit:'Commit',
login:'Zum Login',
github:'Projekt auf GitHub',
help:'Hilfe',
sponsors:'Sponsoren',
systemInfo:'Systeminfo',
},
Equipment:'Ausrüstung',
equipment:{
@@ -238,12 +254,6 @@ export default {
Skill:'Fertigkeit',
newSkill:'Neue Fertigkeit',
createSkill:'Fertigkeit erstellen',
common: {
loading: 'Laden...',
cancel: 'Abbrechen',
previous: 'Zurück',
next: 'Weiter'
},
experience: {
title: 'Erfahrung & Vermögen',
experience_points: 'Erfahrungspunkte',
@@ -557,5 +567,81 @@ export default {
imageUploadSuccess: 'Bild erfolgreich hochgeladen',
imageUploadError: 'Fehler beim Hochladen des Bildes'
},
uploading: 'Hochladen'
uploading: 'Hochladen',
sponsors: {
title: 'Sponsoren & Unterstützer',
introduction: 'BaMoRT ist ein Open-Source-Projekt, das von der Community unterstützt wird. Vielen Dank an alle, die zu diesem Projekt beitragen!',
contributors: 'Mitwirkende',
specialThanks: 'Besonderer Dank',
support: 'Unterstützen Sie das Projekt',
supportText: 'BaMoRT ist ein Open-Source-Projekt. Sie können das Projekt auf GitHub unterstützen, indem Sie Code beitragen, Fehler melden oder Funktionen vorschlagen.',
github: 'Projekt auf GitHub',
roleCreator: 'Projektgründer & Hauptentwickler',
contributionCreator: 'Entwicklung des Backend- und Frontend-Systems',
thanksOpenSource: 'Allen Open-Source-Projekten, auf denen BaMoRT aufbaut',
thanksVue: 'Vue.js Team für das exzellente Frontend-Framework',
thanksGo: 'Go Team für die großartige Programmiersprache',
thanksCommunity: 'Der Rollenspiel-Community für Feedback und Unterstützung'
},
help: {
title: 'Hilfe & Dokumentation',
introduction: 'Willkommen zur BaMoRT-Hilfe! Hier finden Sie Informationen zur Verwendung des Systems.',
gettingStarted: 'Erste Schritte',
step1Title: '1. Registrierung',
step1Text: 'Erstellen Sie ein Benutzerkonto über die Registrierungsseite. Nach der Registrierung können Sie sich anmelden und mit der Charaktererstellung beginnen.',
step2Title: '2. Dashboard',
step2Text: 'Im Dashboard sehen Sie alle Ihre Charaktere. Klicken Sie auf einen Charakter, um Details anzuzeigen, oder erstellen Sie einen neuen Charakter.',
step3Title: '3. Charakterverwaltung',
step3Text: 'In der Charakteransicht können Sie Fertigkeiten verbessern, Ausrüstung verwalten, Zauber lernen und vieles mehr. Alle Änderungen werden automatisch gespeichert.',
features: 'Funktionen',
featureCharacterManagement: 'Charakterverwaltung',
featureCharacterManagementText: 'Erstellen und verwalten Sie Ihre Rollenspielcharaktere mit allen wichtigen Attributen und Eigenschaften.',
featureSkills: 'Fertigkeiten',
featureSkillsText: 'Verwalten Sie Charakterfertigkeiten, verbessern Sie diese mit Lernpunkten und verfolgen Sie den Fortschritt.',
featureSpells: 'Zauber',
featureSpellsText: 'Lernen Sie neue Zauber, verwalten Sie Ihre Zaubersammlung und organisieren Sie diese nach Kategorien.',
featureEquipment: 'Ausrüstung',
featureEquipmentText: 'Verwalten Sie Waffen, Rüstungen und andere Ausrüstungsgegenstände Ihres Charakters.',
featureExport: 'PDF-Export',
featureExportText: 'Exportieren Sie Ihre Charaktere als PDF-Dokumente für den Spieltisch.',
featureImport: 'Datenimport',
featureImportText: 'Importieren Sie Charaktere aus anderen Formaten oder exportieren Sie Ihre Daten.',
faq: 'Häufig gestellte Fragen',
faq1Question: 'Wie erstelle ich einen neuen Charakter?',
faq1Answer: 'Gehen Sie zum Dashboard und klicken Sie auf "Neuen Charakter erstellen". Folgen Sie den Schritten zur Charaktererstellung.',
faq2Question: 'Kann ich meine Charaktere mit anderen teilen?',
faq2Answer: 'Derzeit ist das Teilen von Charakteren nicht implementiert. Diese Funktion ist für eine zukünftige Version geplant.',
faq3Question: 'Wie kann ich Fehler melden?',
faq3Answer: 'Fehler können Sie auf der GitHub-Projektseite als Issue melden. Wir freuen uns über detaillierte Fehlerberichte!',
support: 'Support',
supportText: 'Benötigen Sie weitere Hilfe? Besuchen Sie das GitHub-Repository für Dokumentation, um Issues zu melden oder Fragen zu stellen.',
github: 'Projekt auf GitHub'
},
systemInfo: {
title: 'Systeminformationen',
introduction: 'Technische Informationen über BaMoRT und die verwendeten Technologien.',
versions: 'Versionen',
frontend: 'Frontend',
backend: 'Backend',
version: 'Version',
commit: 'Commit',
status: 'Status',
statusAvailable: 'Verfügbar',
statusUnavailable: 'Nicht verfügbar',
statusLoading: 'Lädt...',
technologies: 'Technologien',
infrastructure: 'Infrastruktur',
features: 'Systemfunktionen',
feature1: 'Multi-User-System mit JWT-Authentifizierung',
feature2: 'Reaktive Frontend-Oberfläche mit Vue 3',
feature3: 'RESTful API mit Go und Gin',
feature4: 'Datenbankunterstützung (MariaDB/SQLite)',
feature5: 'PDF-Export für Charakterbögen',
feature6: 'Internationalisierung (Deutsch/Englisch)',
feature7: 'Docker-basierte Entwicklungsumgebung',
feature8: 'Live-Reload für schnelle Entwicklung',
license: 'Lizenz',
licenseText: 'BaMoRT ist Open-Source-Software, lizenziert unter einer dualen Lizenz. Details finden Sie im GitHub-Repository.',
github: 'Projekt auf GitHub'
}
}
+94 -8
View File
@@ -1,5 +1,12 @@
export default {
DatasheetView:'Datasheet',
common: {
loading: 'Laden...',
cancel: 'Abbrechen',
previous: 'Zurück',
next: 'Weiter',
back: 'Back'
},
DatasheetView:'Datasheet',
SkillView: 'Skills',
WeaponView: 'Weapons',
SpellView: 'Spells',
@@ -44,6 +51,12 @@ export default {
Register:'Register',
Maintenance:'Maintenance', UserManagement:'User Management',
Home:'Home',
Help:'Help',
Sponsors:'Sponsors',
SystemInfo:'System Info',
Info:'Info',
Characters:'Characters',
Admin:'Administration',
},
landing:{
title:'BaMoRT - Character Management for Role-Playing Games',
@@ -54,6 +67,9 @@ export default {
commit:'Commit',
login:'Login',
github:'Project on GitHub',
help:'Help',
sponsors:'Sponsors',
systemInfo:'System Info',
},
Equipment:'Equipment',
equipment:{
@@ -234,12 +250,6 @@ export default {
Skill:'Fertigkeit',
newSkill:'New Skill',
createSkill:'Create Skill',
common: {
loading: 'Laden...',
cancel: 'Abbrechen',
previous: 'Zurück',
next: 'Weiter'
},
experience: {
title: 'Experience & Wealth',
experience_points: 'Experience Points',
@@ -553,5 +563,81 @@ export default {
imageUploadSuccess: 'Image uploaded successfully',
imageUploadError: 'Failed to upload image'
},
uploading: 'Uploading'
uploading: 'Uploading',
sponsors: {
title: 'Sponsors & Contributors',
introduction: 'BaMoRT is an open-source project supported by the community. Thank you to everyone who contributes to this project!',
contributors: 'Contributors',
specialThanks: 'Special Thanks',
support: 'Support the Project',
supportText: 'BaMoRT is an open-source project. You can support the project on GitHub by contributing code, reporting bugs, or suggesting features.',
github: 'Project on GitHub',
roleCreator: 'Project Founder & Lead Developer',
contributionCreator: 'Development of backend and frontend systems',
thanksOpenSource: 'All open-source projects that BaMoRT is built upon',
thanksVue: 'Vue.js team for the excellent frontend framework',
thanksGo: 'Go team for the great programming language',
thanksCommunity: 'The role-playing community for feedback and support'
},
help: {
title: 'Help & Documentation',
introduction: 'Welcome to BaMoRT help! Here you will find information on how to use the system.',
gettingStarted: 'Getting Started',
step1Title: '1. Registration',
step1Text: 'Create a user account via the registration page. After registration, you can log in and start creating characters.',
step2Title: '2. Dashboard',
step2Text: 'In the dashboard you can see all your characters. Click on a character to view details, or create a new character.',
step3Title: '3. Character Management',
step3Text: 'In the character view, you can improve skills, manage equipment, learn spells, and much more. All changes are saved automatically.',
features: 'Features',
featureCharacterManagement: 'Character Management',
featureCharacterManagementText: 'Create and manage your role-playing characters with all important attributes and properties.',
featureSkills: 'Skills',
featureSkillsText: 'Manage character skills, improve them with learning points, and track progress.',
featureSpells: 'Spells',
featureSpellsText: 'Learn new spells, manage your spell collection, and organize them by categories.',
featureEquipment: 'Equipment',
featureEquipmentText: 'Manage weapons, armor, and other equipment items for your character.',
featureExport: 'PDF Export',
featureExportText: 'Export your characters as PDF documents for the gaming table.',
featureImport: 'Data Import',
featureImportText: 'Import characters from other formats or export your data.',
faq: 'Frequently Asked Questions',
faq1Question: 'How do I create a new character?',
faq1Answer: 'Go to the dashboard and click "Create New Character". Follow the steps for character creation.',
faq2Question: 'Can I share my characters with others?',
faq2Answer: 'Currently, character sharing is not implemented. This feature is planned for a future version.',
faq3Question: 'How can I report bugs?',
faq3Answer: 'You can report bugs on the GitHub project page as an issue. We appreciate detailed bug reports!',
support: 'Support',
supportText: 'Need more help? Visit the GitHub repository for documentation, to report issues, or ask questions.',
github: 'Project on GitHub'
},
systemInfo: {
title: 'System Information',
introduction: 'Technical information about BaMoRT and the technologies used.',
versions: 'Versions',
frontend: 'Frontend',
backend: 'Backend',
version: 'Version',
commit: 'Commit',
status: 'Status',
statusAvailable: 'Available',
statusUnavailable: 'Unavailable',
statusLoading: 'Loading...',
technologies: 'Technologies',
infrastructure: 'Infrastructure',
features: 'System Features',
feature1: 'Multi-user system with JWT authentication',
feature2: 'Reactive frontend interface with Vue 3',
feature3: 'RESTful API with Go and Gin',
feature4: 'Database support (MariaDB/SQLite)',
feature5: 'PDF export for character sheets',
feature6: 'Internationalization (German/English)',
feature7: 'Docker-based development environment',
feature8: 'Live-reload for rapid development',
license: 'License',
licenseText: 'BaMoRT is open-source software, licensed under a dual license. Details can be found in the GitHub repository.',
github: 'Project on GitHub'
}
}
+6
View File
@@ -11,6 +11,9 @@ import MaintenanceView from "../views/MaintenanceView.vue";
import FileUploadPage from "../views/FileUploadPage.vue";
import UserProfileView from "../views/UserProfileView.vue";
import UserManagementView from "../views/UserManagementView.vue";
import SponsorsView from "../views/SponsorsView.vue";
import HelpView from "../views/HelpView.vue";
import SystemInfoView from "../views/SystemInfoView.vue";
import CharacterDetails from "@/components/CharacterDetails.vue";
import CharacterCreation from "@/components/CharacterCreation.vue";
@@ -30,6 +33,9 @@ const routes = [
{ path: "/ausruestung/:characterId", name: "Ausruestung", component: AusruestungView, meta: { requiresAuth: true } },
{ path: "/maintenance", name: "Maintenance", component: MaintenanceView, meta: { requiresAuth: true } },
{ path: "/upload", name: "FileUpload", component: FileUploadPage },
{ path: "/sponsors", name: "Sponsors", component: SponsorsView },
{ path: "/help", name: "Help", component: HelpView },
{ path: "/system-info", name: "SystemInfo", component: SystemInfoView },
// Route for character details // Pass route params as props to the component
{ path: "/character/:id", name: "CharacterDetails", component: CharacterDetails, props: true, meta: { requiresAuth: true } },
// Route for character creation
+116
View File
@@ -0,0 +1,116 @@
<template>
<div class="fullwidth-page">
<div class="page-header">
<button @click="$router.back()" class="btn btn-secondary back-button">
{{ $t('common.back') }}
</button>
<h2>{{ $t('help.title') }}</h2>
</div>
<div class="card">
<p>{{ $t('help.introduction') }}</p>
</div>
<div class="section-header">
<h3>{{ $t('help.gettingStarted') }}</h3>
</div>
<div class="card">
<h4>{{ $t('help.step1Title') }}</h4>
<p>{{ $t('help.step1Text') }}</p>
<h4 style="margin-top: 20px;">{{ $t('help.step2Title') }}</h4>
<p>{{ $t('help.step2Text') }}</p>
<h4 style="margin-top: 20px;">{{ $t('help.step3Title') }}</h4>
<p>{{ $t('help.step3Text') }}</p>
</div>
<div class="section-header">
<h3>{{ $t('help.features') }}</h3>
</div>
<div class="grid-container grid-2-columns">
<div class="card">
<h4>{{ $t('help.featureCharacterManagement') }}</h4>
<p>{{ $t('help.featureCharacterManagementText') }}</p>
</div>
<div class="card">
<h4>{{ $t('help.featureSkills') }}</h4>
<p>{{ $t('help.featureSkillsText') }}</p>
</div>
<div class="card">
<h4>{{ $t('help.featureSpells') }}</h4>
<p>{{ $t('help.featureSpellsText') }}</p>
</div>
<div class="card">
<h4>{{ $t('help.featureEquipment') }}</h4>
<p>{{ $t('help.featureEquipmentText') }}</p>
</div>
<div class="card">
<h4>{{ $t('help.featureExport') }}</h4>
<p>{{ $t('help.featureExportText') }}</p>
</div>
<div class="card">
<h4>{{ $t('help.featureImport') }}</h4>
<p>{{ $t('help.featureImportText') }}</p>
</div>
</div>
<div class="section-header">
<h3>{{ $t('help.faq') }}</h3>
</div>
<div class="card" v-for="(faq, index) in faqs" :key="index">
<h4>{{ faq.question }}</h4>
<p>{{ faq.answer }}</p>
</div>
<div class="section-header">
<h3>{{ $t('help.support') }}</h3>
</div>
<div class="card">
<p>{{ $t('help.supportText') }}</p>
<div style="display: flex; gap: 10px; margin-top: 15px;">
<a :href="githubUrl" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
{{ $t('help.github') }}
</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: "HelpView",
data() {
return {
githubUrl: "https://github.com/Bardioc26/bamort",
faqs: []
}
},
created() {
// Populate FAQs with translations
this.faqs = [
{
question: this.$t('help.faq1Question'),
answer: this.$t('help.faq1Answer')
},
{
question: this.$t('help.faq2Question'),
answer: this.$t('help.faq2Answer')
},
{
question: this.$t('help.faq3Question'),
answer: this.$t('help.faq3Answer')
}
]
}
}
</script>
+12
View File
@@ -22,6 +22,18 @@
{{ $t('landing.github') }}
</a>
</div>
<div class="quick-links">
<router-link to="/help" class="quick-link">
{{ $t('landing.help') }}
</router-link>
<router-link to="/sponsors" class="quick-link">
{{ $t('landing.sponsors') }}
</router-link>
<router-link to="/system-info" class="quick-link">
{{ $t('landing.systemInfo') }}
</router-link>
</div>
</div>
</div>
</div>
+77
View File
@@ -0,0 +1,77 @@
<template>
<div class="fullwidth-page">
<div class="page-header">
<button @click="$router.back()" class="btn btn-secondary back-button">
{{ $t('common.back') }}
</button>
<h2>{{ $t('sponsors.title') }}</h2>
</div>
<div class="card">
<p>{{ $t('sponsors.introduction') }}</p>
</div>
<div class="section-header">
<h3>{{ $t('sponsors.contributors') }}</h3>
</div>
<div class="grid-container grid-2-columns">
<div class="card" v-for="contributor in contributors" :key="contributor.name">
<h4>{{ contributor.name }}</h4>
<p>{{ contributor.role }}</p>
<p v-if="contributor.contribution">{{ contributor.contribution }}</p>
</div>
</div>
<div class="section-header">
<h3>{{ $t('sponsors.specialThanks') }}</h3>
</div>
<div class="card">
<ul>
<li v-for="thanks in specialThanks" :key="thanks">{{ thanks }}</li>
</ul>
</div>
<div class="section-header">
<h3>{{ $t('sponsors.support') }}</h3>
</div>
<div class="card">
<p>{{ $t('sponsors.supportText') }}</p>
<div style="display: flex; gap: 10px; margin-top: 15px;">
<a :href="githubUrl" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
{{ $t('sponsors.github') }}
</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: "SponsorsView",
data() {
return {
githubUrl: "https://github.com/Bardioc26/bamort",
contributors: [
{
name: "Bardioc26",
role: this.$t('sponsors.roleCreator'),
contribution: this.$t('sponsors.contributionCreator')
}
],
specialThanks: []
}
},
created() {
// Populate special thanks with translations
this.specialThanks = [
this.$t('sponsors.thanksOpenSource'),
this.$t('sponsors.thanksVue'),
this.$t('sponsors.thanksGo'),
this.$t('sponsors.thanksCommunity')
]
}
}
</script>
+161
View File
@@ -0,0 +1,161 @@
<template>
<div class="fullwidth-page">
<div class="page-header">
<button @click="$router.back()" class="btn btn-secondary back-button">
{{ $t('common.back') }}
</button>
<h2>{{ $t('systemInfo.title') }}</h2>
</div>
<div class="card">
<p>{{ $t('systemInfo.introduction') }}</p>
</div>
<div class="section-header">
<h3>{{ $t('systemInfo.versions') }}</h3>
</div>
<div class="grid-container grid-2-columns">
<div class="card">
<h4>{{ $t('systemInfo.frontend') }}</h4>
<p><strong>{{ $t('systemInfo.version') }}:</strong> {{ frontendVersion }}</p>
<p><strong>{{ $t('systemInfo.commit') }}:</strong> <code>{{ frontendCommit }}</code></p>
</div>
<div class="card">
<h4>{{ $t('systemInfo.backend') }}</h4>
<p><strong>{{ $t('systemInfo.version') }}:</strong> {{ backendVersion }}</p>
<p><strong>{{ $t('systemInfo.commit') }}:</strong> <code>{{ backendCommit }}</code></p>
<p><strong>{{ $t('systemInfo.status') }}:</strong>
<span :class="statusClass">{{ statusText }}</span>
</p>
</div>
</div>
<div class="section-header">
<h3>{{ $t('systemInfo.technologies') }}</h3>
</div>
<div class="grid-container grid-3-columns">
<div class="card">
<h4>Frontend</h4>
<ul>
<li>Vue 3</li>
<li>Vite</li>
<li>Vue Router</li>
<li>Pinia</li>
<li>Axios</li>
<li>Vue i18n</li>
</ul>
</div>
<div class="card">
<h4>Backend</h4>
<ul>
<li>Go 1.25</li>
<li>Gin Framework</li>
<li>GORM</li>
<li>MariaDB</li>
<li>JWT Auth</li>
<li>Chromedp (PDF)</li>
</ul>
</div>
<div class="card">
<h4>{{ $t('systemInfo.infrastructure') }}</h4>
<ul>
<li>Docker</li>
<li>Docker Compose</li>
<li>Air (Hot Reload)</li>
<li>phpMyAdmin</li>
</ul>
</div>
</div>
<div class="section-header">
<h3>{{ $t('systemInfo.features') }}</h3>
</div>
<div class="card">
<ul>
<li>{{ $t('systemInfo.feature1') }}</li>
<li>{{ $t('systemInfo.feature2') }}</li>
<li>{{ $t('systemInfo.feature3') }}</li>
<li>{{ $t('systemInfo.feature4') }}</li>
<li>{{ $t('systemInfo.feature5') }}</li>
<li>{{ $t('systemInfo.feature6') }}</li>
<li>{{ $t('systemInfo.feature7') }}</li>
<li>{{ $t('systemInfo.feature8') }}</li>
</ul>
</div>
<div class="section-header">
<h3>{{ $t('systemInfo.license') }}</h3>
</div>
<div class="card">
<p>{{ $t('systemInfo.licenseText') }}</p>
<div style="display: flex; gap: 10px; margin-top: 15px;">
<a :href="githubUrl" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
{{ $t('systemInfo.github') }}
</a>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import { getVersion, getGitCommit } from '../version'
export default {
name: "SystemInfoView",
data() {
return {
frontendVersion: getVersion(),
frontendCommit: getGitCommit(),
backendVersion: "Loading...",
backendCommit: "Loading...",
githubUrl: "https://github.com/Bardioc26/bamort"
}
},
computed: {
statusClass() {
if (this.backendVersion === "Loading...") return "status-loading"
if (this.isBackendAvailable) return "status-available"
return "status-unavailable"
},
statusText() {
if (this.backendVersion === "Loading...") return this.$t('systemInfo.statusLoading')
if (this.isBackendAvailable) return this.$t('systemInfo.statusAvailable')
return this.$t('systemInfo.statusUnavailable')
},
isBackendAvailable() {
return this.backendVersion !== "Loading..." &&
this.backendVersion !== "Unavailable" &&
this.backendVersion !== "Unreachable" &&
this.backendVersion !== "Unknown"
}
},
mounted() {
this.fetchBackendVersion()
},
methods: {
async fetchBackendVersion() {
try {
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8180'
const response = await axios.get(`${apiUrl}/api/public/version`)
if (response.data) {
this.backendVersion = response.data.version || "Unknown"
this.backendCommit = response.data.gitCommit || "Unknown"
}
} catch (error) {
console.warn("Could not fetch backend version:", error)
this.backendVersion = "Unavailable"
this.backendCommit = "N/A"
}
}
}
}
</script>
+32
View File
@@ -0,0 +1,32 @@
# Environment variables for Bamort development environment
# API Configuration
# API_URL=http://localhost:8180
# Database Configuration (for development)
DATABASE_TYPE=mysql
DATABASE_URL="bamort:bG4)efozrc@tcp(192.168.0.36:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local"
# MariaDB Configuration (development)
MARIADB_ROOT_PASSWORD=root_password_dev
MARIADB_PASSWORD="bG4)efozrc"
MARIADB_DATABASE=bamort
MARIADB_USER=bamort
# Frontend Configuration
API_URL="http://localhost:8180"
VITE_API_URL="http://localhost:8180"
API_PORT=8180
BASE_URL="http://localhost:5173"
TEMPLATES_DIR=./templates
EXPORT_TEMP_DIR=./export_temp
GIT_COMMIT=d0c177b
LOG_LEVEL=debug
COMPOSE_PROJECT_NAME=bamort
CHROME_BIN="/usr/bin/chromium"
#./server
#/usr/local/go/bin/go run ./cmd/main.go
npm install
npm run dev -- --host 0.0.0.0