Merge pull request #8 from Bardioc26/frontend_refactor
Frontend refactor consolidated css styling Export allowes now PDF, VTT and Bamort format added landing page Added script to update versions
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Version Management
|
||||
|
||||
## Current Version: 0.1.30
|
||||
|
||||
The backend version is managed in `/backend/config/version.go`.
|
||||
|
||||
## Updating the Version
|
||||
|
||||
To update the application version:
|
||||
|
||||
1. Edit `/backend/config/version.go`
|
||||
2. Change the `Version` constant:
|
||||
```go
|
||||
const Version = "0.1.31" // Update this
|
||||
```
|
||||
|
||||
## Git Commit Information
|
||||
|
||||
The git commit hash is automatically detected at runtime from:
|
||||
|
||||
1. `GIT_COMMIT` environment variable (preferred for Docker)
|
||||
2. Git command output (works in development)
|
||||
3. Falls back to "unknown" if neither is available
|
||||
|
||||
## Setting Git Commit in Docker
|
||||
|
||||
### Development Environment
|
||||
|
||||
Add to `docker/.env`:
|
||||
```bash
|
||||
GIT_COMMIT=$(git rev-parse --short HEAD)
|
||||
```
|
||||
|
||||
Then restart the container:
|
||||
```bash
|
||||
cd docker
|
||||
docker-compose -f docker-compose.dev.yml up -d backend-dev
|
||||
```
|
||||
|
||||
### Production Build
|
||||
|
||||
Use build-time variable:
|
||||
```bash
|
||||
docker build --build-arg GIT_COMMIT=$(git rev-parse --short HEAD) .
|
||||
```
|
||||
|
||||
Or set in docker-compose.yml:
|
||||
```yaml
|
||||
environment:
|
||||
- GIT_COMMIT=${GIT_COMMIT:-unknown}
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
- **Public**: `GET /api/public/version` (no authentication)
|
||||
- **Protected**: `GET /api/version` (requires JWT token)
|
||||
|
||||
Both return:
|
||||
```json
|
||||
{
|
||||
"version": "0.1.30",
|
||||
"gitCommit": "d0c177b"
|
||||
}
|
||||
```
|
||||
|
||||
## Frontend Integration
|
||||
|
||||
The landing page automatically fetches version information from `/api/public/version` and displays it.
|
||||
|
||||
To see the version in the frontend, visit: http://localhost:5173
|
||||
@@ -82,9 +82,11 @@ func main() {
|
||||
importer.RegisterRoutes(protected)
|
||||
pdfrender.RegisterRoutes(protected)
|
||||
transfer.RegisterRoutes(protected)
|
||||
config.RegisterRoutes(protected)
|
||||
|
||||
// Register public routes (no authentication)
|
||||
pdfrender.RegisterPublicRoutes(r)
|
||||
config.RegisterPublicRoutes(r)
|
||||
|
||||
logger.Info("API-Routen erfolgreich registriert")
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Versionsinfo returns version and git commit information
|
||||
func Versionsinfo(c *gin.Context) {
|
||||
c.JSON(200, GetInfo())
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package config
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// RegisterRoutes registers config-related routes (protected)
|
||||
func RegisterRoutes(r *gin.RouterGroup) {
|
||||
r.GET("/version", Versionsinfo)
|
||||
}
|
||||
|
||||
// RegisterPublicRoutes registers public config routes (no auth required)
|
||||
func RegisterPublicRoutes(r *gin.Engine) {
|
||||
// Public version endpoint - no authentication required
|
||||
public := r.Group("/api/public")
|
||||
public.GET("/version", Versionsinfo)
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package config
|
||||
|
||||
// Version is the application version
|
||||
const Version = "0.1.30"
|
||||
|
||||
var (
|
||||
// GitCommit will be set by build flags or detected at runtime
|
||||
GitCommit = "unknown"
|
||||
)
|
||||
|
||||
// init detects git commit if not set during build
|
||||
func init() {
|
||||
/*
|
||||
if GitCommit == "" {
|
||||
// Try environment variable first
|
||||
if envCommit := os.Getenv("GIT_COMMIT"); envCommit != "" {
|
||||
GitCommit = envCommit
|
||||
} else {
|
||||
// Try to detect from git command
|
||||
GitCommit = detectGitCommit()
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
// detectGitCommit tries to get the current git commit hash
|
||||
func detectGitCommit() string {
|
||||
cmd := exec.Command("git", "rev-parse", "--short", "HEAD")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
return strings.TrimSpace(string(output))
|
||||
}
|
||||
*/
|
||||
// GetVersion returns the current application version
|
||||
func GetVersion() string {
|
||||
return Version
|
||||
}
|
||||
|
||||
/*
|
||||
// GetGitCommit returns the git commit hash
|
||||
func GetGitCommit() string {
|
||||
return GitCommit
|
||||
}
|
||||
*/
|
||||
// Info contains version information
|
||||
type Info struct {
|
||||
Version string `json:"version"`
|
||||
GitCommit string `json:"gitCommit"`
|
||||
}
|
||||
|
||||
// GetInfo returns version information as a struct
|
||||
func GetInfo() Info {
|
||||
return Info{
|
||||
Version: Version,
|
||||
GitCommit: GitCommit,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
version := GetVersion()
|
||||
if version == "" {
|
||||
t.Error("Version should not be empty")
|
||||
}
|
||||
if version != Version {
|
||||
t.Errorf("Expected version %s, got %s", Version, version)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestGetGitCommit(t *testing.T) {
|
||||
commit := GetGitCommit()
|
||||
if commit == "" {
|
||||
t.Error("GitCommit should not be empty")
|
||||
}
|
||||
// Should be either "unknown" or a valid git hash
|
||||
if commit != "unknown" && len(commit) < 7 {
|
||||
t.Errorf("Invalid git commit format: %s", commit)
|
||||
}
|
||||
}
|
||||
*/
|
||||
func TestGetInfo(t *testing.T) {
|
||||
info := GetInfo()
|
||||
|
||||
if info.Version == "" {
|
||||
t.Error("Info.Version should not be empty")
|
||||
}
|
||||
|
||||
if info.GitCommit == "" {
|
||||
t.Error("Info.GitCommit should not be empty")
|
||||
}
|
||||
|
||||
if info.Version != Version {
|
||||
t.Errorf("Expected info.Version %s, got %s", Version, info.Version)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ services:
|
||||
- API_PORT=${API_PORT:-8180}
|
||||
- TEMPLATES_DIR=${TEMPLATES_DIR:-./templatesx}
|
||||
- EXPORT_TEMP_DIR=${EXPORT_TEMP_DIR:-./export_tempx}
|
||||
- GIT_COMMIT=${GIT_COMMIT:-unknown}
|
||||
depends_on:
|
||||
mariadb-dev:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -11,6 +11,10 @@ fi
|
||||
# Gehe ins Docker-Verzeichnis
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Get current git commit
|
||||
export GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
echo "📝 Git Commit: $GIT_COMMIT"
|
||||
|
||||
echo "📦 Building and starting development containers..."
|
||||
|
||||
# Stoppe vorhandene Container
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Frontend Version Management
|
||||
|
||||
## Current Version: 0.1.30
|
||||
|
||||
The frontend version is managed independently from the backend.
|
||||
|
||||
## Version Locations
|
||||
|
||||
1. **Primary source**: `/frontend/src/version.js`
|
||||
- Contains the VERSION constant
|
||||
- Exports version info functions
|
||||
|
||||
2. **Package metadata**: `/frontend/package.json`
|
||||
- Standard npm version field
|
||||
- Should match version.js
|
||||
|
||||
## Updating the Version
|
||||
|
||||
### Option 1: Using the update script (Recommended)
|
||||
```bash
|
||||
# Updates both backend and frontend
|
||||
./scripts/update-version.sh 0.1.31
|
||||
```
|
||||
|
||||
### Option 2: Manual update
|
||||
Edit `/frontend/src/version.js`:
|
||||
```javascript
|
||||
export const VERSION = '0.1.31' // Update this
|
||||
```
|
||||
|
||||
And `/frontend/package.json`:
|
||||
```json
|
||||
{
|
||||
"version": "0.1.31" // Update this
|
||||
}
|
||||
```
|
||||
|
||||
## Git Commit Information
|
||||
|
||||
The git commit is injected via environment variable:
|
||||
- Set `VITE_GIT_COMMIT` in `.env` or at build time
|
||||
- Falls back to "unknown" if not set
|
||||
|
||||
Example `.env`:
|
||||
```bash
|
||||
VITE_GIT_COMMIT=d0c177b
|
||||
```
|
||||
|
||||
## Usage in Components
|
||||
|
||||
```javascript
|
||||
import { getVersion, getGitCommit, getVersionInfo } from '@/version'
|
||||
|
||||
// Get version string
|
||||
const version = getVersion() // "0.1.30"
|
||||
|
||||
// Get git commit
|
||||
const commit = getGitCommit() // "d0c177b" or "unknown"
|
||||
|
||||
// Get full info object
|
||||
const info = getVersionInfo() // { version: "0.1.30", gitCommit: "d0c177b" }
|
||||
```
|
||||
|
||||
## Landing Page Display
|
||||
|
||||
The landing page shows both:
|
||||
- **Frontend Version**: From `/frontend/src/version.js`
|
||||
- **Backend Version**: Fetched from `/api/public/version`
|
||||
|
||||
This allows users to see if frontend and backend are in sync.
|
||||
|
||||
## Build-time Version Injection
|
||||
|
||||
To inject git commit at build time, update `vite.config.js`:
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from 'vite'
|
||||
import { execSync } from 'child_process'
|
||||
|
||||
const gitCommit = execSync('git rev-parse --short HEAD').toString().trim()
|
||||
|
||||
export default defineConfig({
|
||||
define: {
|
||||
'import.meta.env.VITE_GIT_COMMIT': JSON.stringify(gitCommit)
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "0.0.0",
|
||||
"name": "bamort-frontend",
|
||||
"version": "0.1.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 416 KiB |
File diff suppressed because it is too large
Load Diff
@@ -431,276 +431,6 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.audit-log-view {
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.audit-log-view h4 {
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #007bff;
|
||||
}
|
||||
|
||||
.filter-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.date-range-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.filter-select, .date-input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.btn-refresh {
|
||||
padding: 8px 16px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.btn-refresh:hover:not(:disabled) {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
margin-bottom: 25px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.stats-section h5 {
|
||||
margin-bottom: 15px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.stat-value.positive {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.stat-value.negative {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.audit-entries {
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.loading, .no-entries {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.audit-entry {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.audit-entry:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.audit-entry:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.audit-entry.positive-change {
|
||||
border-left: 4px solid #28a745;
|
||||
}
|
||||
|
||||
.audit-entry.negative-change {
|
||||
border-left: 4px solid #dc3545;
|
||||
}
|
||||
|
||||
.entry-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.entry-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.field-icon {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.entry-timestamp {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
text-align: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.timestamp-date {
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.timestamp-time {
|
||||
font-size: 0.85em;
|
||||
color: #888;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.timestamp-relative {
|
||||
font-size: 0.8em;
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.entry-content {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.value-change {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
font-family: monospace;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.old-value {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
color: #007bff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.new-value {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.difference {
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.difference.positive {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.difference.negative {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.entry-reason, .entry-notes {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.reason-label, .notes-label {
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.reason-value {
|
||||
background: #e9ecef;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.85em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.notes-value {
|
||||
color: #555;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.date-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.date-group-header {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
padding: 8px 15px;
|
||||
margin: 0 0 10px 0;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 0.9em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.checkbox-input {
|
||||
margin-right: 8px;
|
||||
}
|
||||
<style>
|
||||
/* All styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -35,3 +35,7 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -38,3 +38,7 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -359,132 +359,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.character-creation {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.creation-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.creation-header {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.creation-header h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-indicator {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.step.active {
|
||||
background-color: #e3f2fd;
|
||||
border: 2px solid #2196f3;
|
||||
}
|
||||
|
||||
.step.completed {
|
||||
background-color: #e8f5e8;
|
||||
border: 2px solid #4caf50;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background-color: #ddd;
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.step.active .step-number {
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.step.completed .step-number {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.step.clickable {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.step.clickable:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.step.completed:hover .step-number {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.step.active:hover .step-number {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.creation-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.session-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
<style>
|
||||
/* All styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -336,22 +336,8 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.attributes-form {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.attributes-form h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.instruction {
|
||||
text-align: center;
|
||||
@@ -361,63 +347,6 @@ export default {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.attributes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
padding: 5px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px;
|
||||
background-color: #fefefe;
|
||||
}
|
||||
|
||||
.attribute-group {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background-color: #fafafa;
|
||||
min-width: 0; /* Prevent overflow */
|
||||
}
|
||||
|
||||
.attribute-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.input-with-dice {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.attribute-label {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.attribute-input {
|
||||
width: 60px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.attribute-input:focus {
|
||||
outline: none;
|
||||
border-color: #2196f3;
|
||||
box-shadow: 0 0 5px rgba(33, 150, 243, 0.3);
|
||||
}
|
||||
|
||||
.attribute-description {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
@@ -467,63 +396,10 @@ export default {
|
||||
background-color: #f57c00;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.attributes-form-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.prev-btn, .next-btn {
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.prev-btn {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.prev-btn:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.next-btn:hover:not(:disabled) {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
.next-btn:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Responsive Design für sehr kleine Bildschirme */
|
||||
@media (max-width: 600px) {
|
||||
.attributes-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.attribute-group {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -390,49 +390,8 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.basic-info-form {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.basic-info-form h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
input, select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus, select:focus {
|
||||
outline: none;
|
||||
border-color: #2196f3;
|
||||
box-shadow: 0 0 5px rgba(33, 150, 243, 0.3);
|
||||
}
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.belief-search {
|
||||
position: relative;
|
||||
@@ -491,63 +450,6 @@ input:focus, select:focus {
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.next-btn:hover:not(:disabled) {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
.next-btn:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Social class roll styles */
|
||||
.input-with-dice {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-with-dice select {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dice-btn {
|
||||
padding: 10px 12px;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dice-btn:hover:not(:disabled) {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.dice-btn:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.roll-result {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
@@ -557,7 +459,6 @@ input:focus, select:focus {
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
/* Roll overlay styles */
|
||||
.roll-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
||||
@@ -480,3 +480,7 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -705,10 +705,9 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Minimal custom styles - most styling comes from main.css */
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
/* Override global fullwidth-page padding to achieve true full-width */
|
||||
.fullwidth-page {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
@@ -717,35 +716,30 @@ export default {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Add minimal padding only where needed */
|
||||
.page-header {
|
||||
padding: 15px 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Full-width three column grid layout */
|
||||
.three-column-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 30px;
|
||||
margin: 0 20px 30px 20px; /* Add horizontal margins for content readability */
|
||||
width: calc(100vw - 40px); /* Use viewport width minus margins */
|
||||
margin: 0 20px 30px 20px;
|
||||
width: calc(100vw - 40px);
|
||||
max-width: calc(100vw - 40px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Ensure grid takes full available width */
|
||||
.skills-content {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Utility classes for dynamic styling that can't be expressed in main.css */
|
||||
.opacity-50 {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Border color variants for category states */
|
||||
.border-primary {
|
||||
border-color: #007bff !important;
|
||||
background-color: #f8fcff;
|
||||
@@ -761,7 +755,6 @@ export default {
|
||||
background-color: #ffebee;
|
||||
}
|
||||
|
||||
/* Responsive behavior for smaller screens */
|
||||
@media (max-width: 1200px) {
|
||||
.three-column-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
@@ -636,10 +636,9 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Minimal custom styles - most styling comes from main.css */
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
/* Spell Points Display */
|
||||
.spell-points-display {
|
||||
margin: 0 20px 30px 20px;
|
||||
padding: 16px;
|
||||
@@ -703,7 +702,6 @@ export default {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Override global fullwidth-page padding to achieve true full-width */
|
||||
.fullwidth-page {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
@@ -712,30 +710,26 @@ export default {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Add minimal padding only where needed */
|
||||
.page-header {
|
||||
padding: 15px 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Full-width three column grid layout */
|
||||
.three-column-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 30px;
|
||||
margin: 0 20px 30px 20px; /* Add horizontal margins for content readability */
|
||||
width: calc(100vw - 40px); /* Use viewport width minus margins */
|
||||
margin: 0 20px 30px 20px;
|
||||
width: calc(100vw - 40px);
|
||||
max-width: calc(100vw - 40px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Ensure grid takes full available width */
|
||||
.spells-content {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Spell-specific styles */
|
||||
.spell-item {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
@@ -828,7 +822,6 @@ export default {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Selected spells styles */
|
||||
.selected-spell-item {
|
||||
padding: 12px;
|
||||
border: 1px solid #007bff;
|
||||
@@ -844,14 +837,6 @@ export default {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.spell-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.selected-spell-item .spell-category {
|
||||
color: #6c757d;
|
||||
font-size: 0.8em;
|
||||
@@ -880,7 +865,6 @@ export default {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
/* Category selection styles */
|
||||
.category-item {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
@@ -924,7 +908,6 @@ export default {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Total costs display */
|
||||
.total-costs {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 12px;
|
||||
@@ -948,12 +931,10 @@ export default {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
/* Utility classes for dynamic styling that can't be expressed in main.css */
|
||||
.opacity-50 {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Message styles */
|
||||
.no-selection-message,
|
||||
.no-spells-message,
|
||||
.no-categories-message {
|
||||
@@ -963,7 +944,6 @@ export default {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Badge styles */
|
||||
.category-badge,
|
||||
.count-badge,
|
||||
.info-badge {
|
||||
@@ -984,7 +964,6 @@ export default {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Responsive behavior for smaller screens */
|
||||
@media (max-width: 1200px) {
|
||||
.three-column-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
@@ -58,7 +58,9 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.sessions-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
@@ -97,7 +99,6 @@ export default {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.session-header {
|
||||
flex-direction: column;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- Character Header -->
|
||||
<div class="character-header">
|
||||
<div class="header-content">
|
||||
<button @click="showExportDialog = true" class="export-button-small" :title="$t('export.exportPDF')">
|
||||
<button @click="showExportDialog = true" class="export-button-small" :title="$t('export.title')">
|
||||
📄
|
||||
</button>
|
||||
<h2>{{ $t('char') }}: {{ character.name }} ({{ $t(currentView) }})</h2>
|
||||
@@ -11,44 +11,12 @@
|
||||
</div>
|
||||
|
||||
<!-- Export Dialog -->
|
||||
<div v-if="showExportDialog" class="modal-overlay" @click.self="showExportDialog = false">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>{{ $t('export.exportPDF') }}</h3>
|
||||
<button @click="showExportDialog = false" class="close-button">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div v-if="isExporting" class="loading-overlay">
|
||||
<div class="spinner"></div>
|
||||
<p>{{ $t('export.generating') }}</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ $t('export.selectTemplate') }}:</label>
|
||||
<select v-model="selectedTemplate" class="template-select" :disabled="isExporting">
|
||||
<option value="">{{ $t('export.pleaseSelectTemplate') }}</option>
|
||||
<option v-for="template in templates" :key="template.id" :value="template.id">
|
||||
{{ template.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="showUserName" :disabled="isExporting">
|
||||
{{ $t('export.showUserName') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button @click="showExportDialog = false" class="btn-cancel" :disabled="isExporting">
|
||||
{{ $t('export.cancel') }}
|
||||
</button>
|
||||
<button @click="exportToPDF" class="btn-export" :disabled="!selectedTemplate || isExporting">
|
||||
<span v-if="!isExporting">{{ $t('export.export') }}</span>
|
||||
<span v-else>{{ $t('export.exporting') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ExportDialog
|
||||
:characterId="id"
|
||||
:showDialog="showExportDialog"
|
||||
@update:showDialog="showExportDialog = $event"
|
||||
@export-success="handleExportSuccess"
|
||||
/>
|
||||
|
||||
<!-- Submenu Content -->
|
||||
<!-- <div class="character-aspect"> -->
|
||||
@@ -69,7 +37,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
/* Component-specific styles only - global styles in main.css */
|
||||
.character-details {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -78,261 +47,12 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.character-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.export-button-small {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 8px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.export-button-small:hover {
|
||||
background: #0056b3;
|
||||
border-color: #0056b3;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.character-header h2 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
font-size: 1.5rem;
|
||||
border-bottom: 2px solid #007bff;
|
||||
padding-bottom: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #007bff;
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-overlay p {
|
||||
color: #007bff;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.template-select {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
background: white;
|
||||
color: #495057;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.template-select:focus {
|
||||
outline: none;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
background: #f8f9fa;
|
||||
color: #495057;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #adb5bd;
|
||||
}
|
||||
|
||||
.btn-export {
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 6px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-export:hover:not(:disabled) {
|
||||
background: #0056b3;
|
||||
border-color: #0056b3;
|
||||
}
|
||||
|
||||
.btn-export:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.submenu {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 20px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.submenu button {
|
||||
padding: 10px 16px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
background: #f8f9fa;
|
||||
color: #495057;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.submenu button:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.submenu button.active {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border-color: #007bff;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import API from '../utils/api'
|
||||
import ExportDialog from "./ExportDialog.vue";
|
||||
import DatasheetView from "./DatasheetView.vue"; // Component for character stats
|
||||
import SkillView from "./SkillView.vue"; // Component for character history
|
||||
import WeaponView from "./WeaponView.vue"; // Component for character history
|
||||
@@ -346,6 +66,7 @@ export default {
|
||||
name: "CharacterDetails",
|
||||
props: ["id"], // Receive the route parameter as a prop
|
||||
components: {
|
||||
ExportDialog,
|
||||
DatasheetView,
|
||||
SkillView,
|
||||
WeaponView,
|
||||
@@ -359,11 +80,7 @@ export default {
|
||||
character: {},
|
||||
currentView: "DatasheetView", // Default view
|
||||
lastView: "DatasheetView",
|
||||
templates: [],
|
||||
selectedTemplate: "",
|
||||
showUserName: false,
|
||||
showExportDialog: false,
|
||||
isExporting: false,
|
||||
menus: [
|
||||
{ id: 1, name: "Datasheet", component: "DatasheetView" },
|
||||
{ id: 2, name: "Skill", component: "SkillView" },
|
||||
@@ -385,63 +102,10 @@ export default {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
this.character = response.data
|
||||
|
||||
// Load available templates
|
||||
await this.loadTemplates()
|
||||
},
|
||||
methods: {
|
||||
async loadTemplates() {
|
||||
try {
|
||||
const response = await API.get('/api/pdf/templates')
|
||||
this.templates = response.data
|
||||
// Auto-select first template if available
|
||||
if (this.templates.length > 0) {
|
||||
this.selectedTemplate = this.templates[0].id
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load templates:', error)
|
||||
}
|
||||
},
|
||||
|
||||
async exportToPDF() {
|
||||
if (!this.selectedTemplate) {
|
||||
alert(this.$t('export.pleaseSelectTemplate'))
|
||||
return
|
||||
}
|
||||
|
||||
this.isExporting = true
|
||||
|
||||
try {
|
||||
// Build URL parameters
|
||||
const params = new URLSearchParams({
|
||||
template: this.selectedTemplate
|
||||
})
|
||||
if (this.showUserName) {
|
||||
params.append('showUserName', 'true')
|
||||
}
|
||||
|
||||
// Get filename from export API (saves PDF to file)
|
||||
const response = await API.get(`/api/pdf/export/${this.id}`, {
|
||||
params: Object.fromEntries(params)
|
||||
})
|
||||
|
||||
const filename = response.data.filename
|
||||
if (!filename) {
|
||||
throw new Error('No filename returned from export')
|
||||
}
|
||||
|
||||
// Open PDF in new window using file endpoint
|
||||
const pdfUrl = `${API.defaults.baseURL}/api/pdf/file/${filename}`
|
||||
window.open(pdfUrl, '_blank')
|
||||
|
||||
// Close dialog on success
|
||||
this.showExportDialog = false
|
||||
} catch (error) {
|
||||
console.error('Failed to export PDF:', error)
|
||||
alert(this.$t('export.exportFailed') + ': ' + (error.response?.data?.error || error.message))
|
||||
} finally {
|
||||
this.isExporting = false
|
||||
}
|
||||
handleExportSuccess() {
|
||||
console.log('PDF exported successfully')
|
||||
},
|
||||
|
||||
changeView(view) {
|
||||
@@ -466,4 +130,4 @@ export default {
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</script>
|
||||
@@ -132,8 +132,9 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Spezifische Styles nur für CharacterList */
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.create-character-section {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
|
||||
@@ -176,92 +176,7 @@
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* DatasheetView spezifische Styles */
|
||||
.datasheet-container {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
max-width: none;
|
||||
white-space: normal;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.info-section p {
|
||||
margin: 15px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.character-overview {
|
||||
margin-bottom: 30px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.character-image {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.character-image .image-upload-container {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.character-info {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.editable-value {
|
||||
cursor: pointer;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.editable-value:hover {
|
||||
background-color: rgba(0, 123, 255, 0.1);
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
width: 60px;
|
||||
padding: 2px 4px;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
border: 2px solid var(--primary-color);
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.edit-input:focus {
|
||||
outline: none;
|
||||
border-color: #0056b3;
|
||||
}
|
||||
|
||||
.editable-prop {
|
||||
cursor: pointer;
|
||||
padding: 1px 3px;
|
||||
border-radius: 2px;
|
||||
transition: background-color 0.2s;
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.editable-prop:hover {
|
||||
background-color: rgba(0, 123, 255, 0.1);
|
||||
}
|
||||
|
||||
.prop-input {
|
||||
padding: 1px 4px;
|
||||
font-size: inherit;
|
||||
border: 1px solid var(--primary-color);
|
||||
border-radius: 3px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.prop-input:focus {
|
||||
outline: none;
|
||||
border-color: #0056b3;
|
||||
}
|
||||
/* All styles moved to main.css */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -39,13 +39,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/*
|
||||
.cd-view {
|
||||
text-align: center;
|
||||
}
|
||||
button {
|
||||
margin: 5px;
|
||||
}*/
|
||||
</style>
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
</style>
|
||||
|
||||
|
||||
@@ -119,272 +119,12 @@
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.fullwidth-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.btn-add-equipment {
|
||||
padding: 8px 16px;
|
||||
background: #1da766;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-add-equipment:hover {
|
||||
background: #16a085;
|
||||
}
|
||||
|
||||
.cd-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.cd-table th,
|
||||
.cd-table td {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
<style>
|
||||
/* All styles moved to main.css */
|
||||
|
||||
/* Equipment-specific table header color override */
|
||||
.cd-table th {
|
||||
background-color: #1da766;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
padding: 2rem !important;
|
||||
}
|
||||
|
||||
.action-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 1.2rem;
|
||||
padding: 4px 8px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-delete:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
animation: modalSlideIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes modalSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 20px;
|
||||
border-bottom: 2px solid #1da766;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 28px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.equipment-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.equipment-item {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.equipment-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.equipment-item.selected {
|
||||
background: #e8f5e9;
|
||||
border-left: 3px solid #1da766;
|
||||
}
|
||||
|
||||
.equipment-name {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.equipment-details {
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.selected-equipment-details {
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.selected-equipment-details h4 {
|
||||
margin-top: 0;
|
||||
color: #1da766;
|
||||
}
|
||||
|
||||
.selected-equipment-details p {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
padding: 8px 20px;
|
||||
background: #1da766;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-confirm:hover:not(:disabled) {
|
||||
background: #16a085;
|
||||
}
|
||||
|
||||
.btn-confirm:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 8px 20px;
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -93,8 +93,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ExperianceView spezifische Styles */
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.experience-section,
|
||||
.wealth-section {
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div v-if="showDialog" class="modal-overlay" @click.self="closeDialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>{{ $t('export.title') }}</h3>
|
||||
<button @click="closeDialog" class="close-button">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div v-if="isExporting" class="loading-overlay">
|
||||
<div class="spinner"></div>
|
||||
<p>{{ $t('export.generating') }}</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ $t('export.selectFormat') }}:</label>
|
||||
<select v-model="selectedFormat" class="template-select" :disabled="isExporting">
|
||||
<option value="">{{ $t('export.pleaseSelectFormat') }}</option>
|
||||
<option value="pdf">{{ $t('export.formatPDF') }}</option>
|
||||
<option value="vtt">{{ $t('export.formatVTT') }}</option>
|
||||
<option value="bamort">{{ $t('export.formatBamort') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="selectedFormat === 'pdf'" class="form-group">
|
||||
<label>{{ $t('export.selectTemplate') }}:</label>
|
||||
<select v-model="selectedTemplate" class="template-select" :disabled="isExporting">
|
||||
<option value="">{{ $t('export.pleaseSelectTemplate') }}</option>
|
||||
<option v-for="template in templates" :key="template.id" :value="template.id">
|
||||
{{ template.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="selectedFormat === 'pdf'" class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="showUserName" :disabled="isExporting">
|
||||
{{ $t('export.showUserName') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button @click="closeDialog" class="btn-cancel" :disabled="isExporting">
|
||||
{{ $t('export.cancel') }}
|
||||
</button>
|
||||
<button @click="performExport" class="btn-export" :disabled="!canExport || isExporting">
|
||||
<span v-if="!isExporting">{{ $t('export.export') }}</span>
|
||||
<span v-else>{{ $t('export.exporting') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* All common styles moved to main.css - no component-specific styles needed */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import API from '../utils/api'
|
||||
|
||||
export default {
|
||||
name: "ExportDialog",
|
||||
props: {
|
||||
characterId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
},
|
||||
showDialog: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templates: [],
|
||||
selectedFormat: "",
|
||||
selectedTemplate: "",
|
||||
showUserName: false,
|
||||
isExporting: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
canExport() {
|
||||
if (!this.selectedFormat) return false
|
||||
if (this.selectedFormat === 'pdf' && !this.selectedTemplate) return false
|
||||
return true
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadTemplates()
|
||||
},
|
||||
methods: {
|
||||
async loadTemplates() {
|
||||
try {
|
||||
const response = await API.get('/api/pdf/templates')
|
||||
this.templates = response.data
|
||||
// Auto-select first template if available
|
||||
if (this.templates.length > 0) {
|
||||
this.selectedTemplate = this.templates[0].id
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load templates:', error)
|
||||
}
|
||||
},
|
||||
|
||||
async performExport() {
|
||||
if (!this.selectedFormat) {
|
||||
alert(this.$t('export.pleaseSelectFormat'))
|
||||
return
|
||||
}
|
||||
|
||||
if (this.selectedFormat === 'pdf') {
|
||||
await this.exportToPDF()
|
||||
} else if (this.selectedFormat === 'vtt') {
|
||||
await this.exportToVTT()
|
||||
} else if (this.selectedFormat === 'bamort') {
|
||||
await this.exportToBamort()
|
||||
}
|
||||
},
|
||||
|
||||
async exportToPDF() {
|
||||
if (!this.selectedTemplate) {
|
||||
alert(this.$t('export.pleaseSelectTemplate'))
|
||||
return
|
||||
}
|
||||
|
||||
this.isExporting = true
|
||||
|
||||
try {
|
||||
// Build URL parameters
|
||||
const params = new URLSearchParams({
|
||||
template: this.selectedTemplate
|
||||
})
|
||||
if (this.showUserName) {
|
||||
params.append('showUserName', 'true')
|
||||
}
|
||||
|
||||
// Get filename from export API (saves PDF to file)
|
||||
const response = await API.get(`/api/pdf/export/${this.characterId}`, {
|
||||
params: Object.fromEntries(params)
|
||||
})
|
||||
|
||||
const filename = response.data.filename
|
||||
if (!filename) {
|
||||
throw new Error('No filename returned from export')
|
||||
}
|
||||
|
||||
// Open PDF in new window using file endpoint
|
||||
const pdfUrl = `${API.defaults.baseURL}/api/pdf/file/${filename}`
|
||||
window.open(pdfUrl, '_blank')
|
||||
|
||||
// Emit success event and close dialog
|
||||
this.$emit('export-success')
|
||||
this.closeDialog()
|
||||
} catch (error) {
|
||||
console.error('Failed to export PDF:', error)
|
||||
alert(this.$t('export.exportFailed') + ': ' + (error.response?.data?.error || error.message))
|
||||
} finally {
|
||||
this.isExporting = false
|
||||
}
|
||||
},
|
||||
|
||||
async exportToVTT() {
|
||||
this.isExporting = true
|
||||
|
||||
try {
|
||||
// Get VTT data and trigger download
|
||||
const response = await API.get(`/api/importer/export/vtt/${this.characterId}/file`, {
|
||||
responseType: 'blob'
|
||||
})
|
||||
|
||||
// Create download link
|
||||
const blob = new Blob([response.data], { type: 'application/json' })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `character_${this.characterId}_vtt.json`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
this.$emit('export-success')
|
||||
this.closeDialog()
|
||||
} catch (error) {
|
||||
console.error('Failed to export VTT:', error)
|
||||
alert(this.$t('export.exportFailed') + ': ' + (error.response?.data?.error || error.message))
|
||||
} finally {
|
||||
this.isExporting = false
|
||||
}
|
||||
},
|
||||
|
||||
async exportToBamort() {
|
||||
this.isExporting = true
|
||||
|
||||
try {
|
||||
// Get Bamort JSON data and trigger download
|
||||
const response = await API.get(`/api/transfer/download/${this.characterId}`, {
|
||||
responseType: 'blob'
|
||||
})
|
||||
|
||||
// Create download link
|
||||
const blob = new Blob([response.data], { type: 'application/json' })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `character_${this.characterId}_bamort.json`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
this.$emit('export-success')
|
||||
this.closeDialog()
|
||||
} catch (error) {
|
||||
console.error('Failed to export Bamort format:', error)
|
||||
alert(this.$t('export.exportFailed') + ': ' + (error.response?.data?.error || error.message))
|
||||
} finally {
|
||||
this.isExporting = false
|
||||
}
|
||||
},
|
||||
|
||||
closeDialog() {
|
||||
this.$emit('update:showDialog', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -95,6 +95,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* No custom CSS needed - using main.css classes */
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -74,7 +74,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
/* ImageUploadCropper specific styles */
|
||||
.btn-upload {
|
||||
padding: 8px 16px;
|
||||
background-color: var(--primary-color);
|
||||
@@ -151,19 +154,6 @@
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
padding: 10px 20px;
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -90,6 +90,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* No custom CSS needed - using main.css classes */
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<nav class="top-nav"><!---<nav class="menu"> --->
|
||||
<ul>
|
||||
<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>
|
||||
</li>
|
||||
|
||||
@@ -47,6 +47,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
@@ -201,18 +201,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #007bff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
</style>
|
||||
|
||||
@@ -166,286 +166,10 @@
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Modal Styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
animation: modalSlideIn 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-wide {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
/* Ressourcen-Anzeige im Dialog */
|
||||
.current-resources {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.resource-display-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
flex: 1;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.resource-display-card .resource-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.resource-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.resource-label {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.resource-amount {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.resource-remaining {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.resource-remaining small {
|
||||
color: #6c757d;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f0ad4e !important;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #d9534f !important;
|
||||
}
|
||||
|
||||
/* Lernbare Stufen */
|
||||
.selection-summary {
|
||||
background: #e7f3ff;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.cost-summary {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
/* Component-specific styles - common styles are in main.css */
|
||||
|
||||
.learning-levels {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.level-option {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.level-option:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.level-option:hover:not(.disabled) {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.level-option.selected {
|
||||
background: #e7f3ff;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.level-option.in-sequence:not(.selected) {
|
||||
background: #f0f8ff;
|
||||
border-left: 2px solid #87ceeb;
|
||||
}
|
||||
|
||||
.level-option.disabled {
|
||||
background: #f8f9fa;
|
||||
color: #6c757d;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.level-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.level-target {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.level-cost {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.level-option.disabled .level-cost {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.level-details {
|
||||
margin-top: 4px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
@keyframes modalSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #1da766;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.form-col {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.form-col-main {
|
||||
flex: 2;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.form-col-input {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
height: 80px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
padding: 8px 20px;
|
||||
background: #1da766;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-confirm:hover:not(:disabled) {
|
||||
background: #16a085;
|
||||
}
|
||||
|
||||
.btn-confirm:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 8px 20px;
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-cancel:hover:not(:disabled) {
|
||||
background: #5a6268;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -740,17 +740,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: block;
|
||||
z-index: 1000;
|
||||
}
|
||||
/* Component-specific styles - common styles are in main.css */
|
||||
|
||||
/* Fullscreen modal override */
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 0;
|
||||
@@ -779,278 +771,22 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 2px solid #1da766;
|
||||
background: #f8f9fa;
|
||||
border-radius: 0;
|
||||
flex-shrink: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.dialog-header h3 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-close:hover {
|
||||
background: #e9ecef;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.resources-section {
|
||||
padding: 20px 24px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.resources-section h4 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #495057;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.current-resources {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Component-specific overrides */
|
||||
.resource-display-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.resource-display-card .resource-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.resource-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.resource-label {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.resource-amount {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #1da766;
|
||||
}
|
||||
|
||||
.resource-remaining {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.resource-remaining small {
|
||||
color: #6c757d;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f0ad4e !important;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #d9534f !important;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: #17a2b8 !important;
|
||||
}
|
||||
|
||||
.reward-method-section {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.reward-method-section label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.reward-method-section .form-select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.reward-method-section .form-select:focus {
|
||||
outline: none;
|
||||
border-color: #1da766;
|
||||
box-shadow: 0 0 0 3px rgba(29, 167, 102, 0.1);
|
||||
}
|
||||
|
||||
.reward-method-section .form-hint {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 0.85rem;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
padding: 24px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-select:focus,
|
||||
.form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: #1da766;
|
||||
box-shadow: 0 0 0 3px rgba(29, 167, 102, 0.1);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 0.85rem;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.costs-preview {
|
||||
padding: 20px 24px;
|
||||
background: #fff3cd;
|
||||
border-top: 1px solid #ffeaa7;
|
||||
border-bottom: 1px solid #ffeaa7;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.costs-preview h4 {
|
||||
margin: 0 0 12px 0;
|
||||
color: #856404;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.cost-breakdown {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cost-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cost-label {
|
||||
color: #856404;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cost-value {
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
background: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 20px 24px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 0;
|
||||
border-top: 1px solid #dee2e6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.selection-count {
|
||||
font-size: 0.9rem;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Button overrides for this specific dialog */
|
||||
.btn-confirm {
|
||||
padding: 12px 24px;
|
||||
background: #1da766;
|
||||
@@ -1113,418 +849,6 @@ export default {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Skills Selection Styles */
|
||||
.skills-selection-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.skills-available,
|
||||
.skills-selected {
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.skills-available h4,
|
||||
.skills-selected h4 {
|
||||
margin: 0;
|
||||
padding: 12px 16px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
font-size: 1rem;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.category-filters {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.category-filter-btn {
|
||||
padding: 6px 12px;
|
||||
background: white;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.category-filter-btn:hover {
|
||||
border-color: #1da766;
|
||||
color: #1da766;
|
||||
}
|
||||
|
||||
.category-filter-btn.active {
|
||||
background: #1da766;
|
||||
border-color: #1da766;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.category-filter-btn:first-child {
|
||||
font-weight: 600;
|
||||
background: #e9ecef;
|
||||
border-color: #adb5bd;
|
||||
}
|
||||
|
||||
.category-filter-btn:first-child.active {
|
||||
background: #495057;
|
||||
border-color: #495057;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.sort-and-search-controls {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
background: #f8f9fa;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sort-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.skills-search {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.sort-label {
|
||||
font-size: 0.9rem;
|
||||
color: #495057;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sort-btn {
|
||||
padding: 6px 12px;
|
||||
background: white;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.sort-btn:hover {
|
||||
border-color: #1da766;
|
||||
color: #1da766;
|
||||
}
|
||||
|
||||
.sort-btn.active {
|
||||
background: #1da766;
|
||||
border-color: #1da766;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.skills-list {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.skill-item {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f8f9fa;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
cursor: grab;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.skill-item:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.skill-item:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.skill-item.skill-affordable {
|
||||
border-left: 4px solid #1da766;
|
||||
}
|
||||
|
||||
.skill-item:not(.skill-affordable) {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.skill-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.skill-main-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.skill-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
font-size: 0.95rem;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.skill-category {
|
||||
font-size: 0.8rem;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.skill-costs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 0.85rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.cost-ep {
|
||||
color: #1da766;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cost-gold {
|
||||
color: #ffc107;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.skill-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-select {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 2px solid #1da766;
|
||||
background: white;
|
||||
color: #1da766;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-select:hover:not(:disabled) {
|
||||
background: #1da766;
|
||||
color: white;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.btn-select:disabled {
|
||||
border-color: #6c757d;
|
||||
color: #6c757d;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.skills-drop-zone {
|
||||
min-height: 60vh;
|
||||
padding: 16px;
|
||||
background: white;
|
||||
border: 2px dashed #dee2e6;
|
||||
margin: 16px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.skills-drop-zone.drag-over {
|
||||
border-color: #1da766;
|
||||
background: rgba(29, 167, 102, 0.05);
|
||||
}
|
||||
|
||||
.drop-zone-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #6c757d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.selected-skills-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.selected-skill-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.selected-skill-item:hover {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selected-skill-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.selected-skill-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.selected-skill-costs {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.btn-remove {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-remove:hover {
|
||||
background: #c82333;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.total-costs {
|
||||
margin-top: 16px;
|
||||
padding: 12px;
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.total-costs-header {
|
||||
font-weight: 600;
|
||||
color: #856404;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.total-costs-breakdown {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.total-ep,
|
||||
.total-gold {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.total-ep {
|
||||
color: #1da766;
|
||||
}
|
||||
|
||||
.total-gold {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.affordability-check {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.loading-skills {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #6c757d;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.simple-input-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.simple-input-section .form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.modal-content {
|
||||
@@ -1533,25 +857,6 @@ export default {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.skills-selection-container {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.resources-display {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.current-resources {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.resource-display-card {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.cost-breakdown {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
@@ -251,105 +251,18 @@
|
||||
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.tables-container {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
<style scoped>
|
||||
/* Component-specific styles - common styles are in main.css */
|
||||
|
||||
.table-wrapper-left {
|
||||
flex: 6;
|
||||
min-width: 0; /* Prevent table from overflowing */
|
||||
}
|
||||
.table-wrapper-right {
|
||||
flex: 4;
|
||||
min-width: 0; /* Prevent table from overflowing */
|
||||
}
|
||||
|
||||
.cd-table {
|
||||
width: 100%;
|
||||
}
|
||||
/* Only component-specific overrides remain here */
|
||||
.cd-table-header {
|
||||
background-color: #1da766;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Header mit Lernmodus-Kontrollen */
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.learning-mode-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* Ressourcen-Anzeige */
|
||||
.resources-display {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
animation: slideIn 0.3s ease;
|
||||
}
|
||||
|
||||
.resource-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 6px 12px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.resource-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.resource-value {
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Lernmodus Toggle Button */
|
||||
.btn-learning-mode {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #1da766;
|
||||
background: white;
|
||||
color: #1da766;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-learning-mode:hover {
|
||||
background: #1da766;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-learning-mode.active {
|
||||
background: #1da766;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Lernmodus Action Buttons */
|
||||
.learning-actions {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
animation: slideIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
@@ -360,262 +273,8 @@
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-learn-new,
|
||||
.btn-improve,
|
||||
.btn-add {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 2px solid #007bff;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-learn-new:hover {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-improve {
|
||||
border-color: #28a745;
|
||||
}
|
||||
|
||||
.btn-improve:hover {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-add {
|
||||
border-color: #17a2b8;
|
||||
}
|
||||
|
||||
.btn-add:hover {
|
||||
background: #17a2b8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Aktions-Buttons in der Tabelle */
|
||||
.action-cell {
|
||||
text-align: center;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.btn-action {
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #28a745;
|
||||
background: white;
|
||||
color: #28a745;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-action:hover {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
animation: modalSlideIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes modalSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #1da766;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
height: 80px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
padding: 8px 20px;
|
||||
background: #1da766;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-confirm:hover:not(:disabled) {
|
||||
background: #16a085;
|
||||
}
|
||||
|
||||
.btn-confirm:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 8px 20px;
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* PP-Button Styles */
|
||||
.pp-cell {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.pp-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.pp-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #007bff;
|
||||
background: white;
|
||||
color: #007bff;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.pp-btn:hover:not(:disabled) {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pp-btn:disabled {
|
||||
border-color: #ccc;
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.pp-btn-plus {
|
||||
border-color: #28a745;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.pp-btn-plus:hover:not(:disabled) {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pp-btn-minus {
|
||||
border-color: #dc3545;
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.pp-btn-minus:hover:not(:disabled) {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pp-value {
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import API from '@/utils/api'
|
||||
import SkillImproveDialog from './SkillImproveDialog.vue'
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
<!-- Ausgewählter Zauber Aktionen und Details -->
|
||||
<div v-if="selectedSpell" class="form-group">
|
||||
<div class="spell-details-section">
|
||||
<!---
|
||||
<!--
|
||||
<div class="selection-summary">
|
||||
<div class="spell-actions">
|
||||
<strong>Ausgewählt:</strong> {{ selectedSpell.name }}
|
||||
@@ -198,7 +198,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
--->
|
||||
-->
|
||||
<!-- Detaillierte Zauber-Informationen -->
|
||||
<div v-if="isLoadingSpellDetails" class="loading-spell-details">
|
||||
<span>Lade Zauber-Details...</span>
|
||||
@@ -612,541 +612,18 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Modal Styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
z-index: 1000;
|
||||
}
|
||||
/* Component-specific styles - common styles are in main.css */
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
animation: modalSlideIn 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.modal-wide {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
/* Ressourcen-Anzeige im Dialog */
|
||||
.current-resources {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.resource-display-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
flex: 1;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.resource-display-card .resource-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.resource-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.resource-label {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.resource-amount {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.resource-remaining {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.resource-remaining small {
|
||||
color: #6c757d;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f0ad4e !important;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #d9534f !important;
|
||||
}
|
||||
|
||||
/* Zweispaltiges Layout */
|
||||
.spells-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.available-spells-section,
|
||||
.learning-list-section {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.learning-item {
|
||||
background: #f0f8ff !important;
|
||||
border-left: 3px solid #007bff !important;
|
||||
}
|
||||
|
||||
.learning-item .level-header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.remove-btn:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
.already-selected {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.spell-actions-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn-add-inline {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-add-inline:hover:not(:disabled) {
|
||||
background: #218838;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.btn-add-inline:disabled {
|
||||
background: #6c757d;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.already-selected .btn-add-inline {
|
||||
background: #17a2b8;
|
||||
}
|
||||
|
||||
.total-costs {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background: #e7f3ff;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.spell-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cost-info {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn-add-spell {
|
||||
padding: 4px 12px;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-add-spell:hover:not(:disabled) {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.btn-add-spell:disabled {
|
||||
background: #6c757d;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.spells-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Filter und Sortierung */
|
||||
.school-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.school-btn {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
color: #495057;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.school-btn:hover {
|
||||
background: #f8f9fa;
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.school-btn.active {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
/* Zauber-Auswahl und Details */
|
||||
.spell-details-section {
|
||||
background: #e7f3ff;
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.loading-spell-details {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.spell-details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.spell-detail-card {
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.spell-detail-card h4 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #495057;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.detail-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
flex: 0 0 auto;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
color: #495057;
|
||||
text-align: right;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.spell-description {
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.spell-description h4 {
|
||||
margin: 0 0 8px 0;
|
||||
color: #495057;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.spell-description p {
|
||||
margin: 0;
|
||||
color: #495057;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.selection-summary {
|
||||
background: #e7f3ff;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.cost-summary {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.learning-levels {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.level-option {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.level-option:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.level-option:hover:not(.disabled) {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.level-option.selected {
|
||||
background: #e7f3ff;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.level-option.disabled {
|
||||
background: #f8f9fa;
|
||||
color: #6c757d;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.level-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.level-target {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.level-cost {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.level-option.disabled .level-cost {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.no-spells {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
@keyframes modalSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #1da766;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.form-col {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.form-col-main {
|
||||
flex: 2;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.form-col-input {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
height: 80px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
padding: 8px 20px;
|
||||
background: #1da766;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-confirm:hover:not(:disabled) {
|
||||
background: #16a085;
|
||||
}
|
||||
|
||||
.btn-confirm:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 8px 20px;
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-cancel:hover:not(:disabled) {
|
||||
background: #5a6268;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -191,7 +191,9 @@
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.cd-view {
|
||||
padding: 1rem;
|
||||
}
|
||||
@@ -302,107 +304,6 @@
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
animation: modalSlideIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes modalSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 20px;
|
||||
border-bottom: 2px solid #1da766;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 28px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
@@ -466,40 +367,6 @@
|
||||
.selected-weapon-details p {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
padding: 8px 20px;
|
||||
background: #1da766;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-confirm:hover:not(:disabled) {
|
||||
background: #16a085;
|
||||
}
|
||||
|
||||
.btn-confirm:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 8px 20px;
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
+19
-2
@@ -44,6 +44,17 @@ export default {
|
||||
Register:'Registrieren',
|
||||
Maintenance:'Wartung',
|
||||
UserManagement:'Benutzerverwaltung',
|
||||
Home:'Startseite',
|
||||
},
|
||||
landing:{
|
||||
title:'BaMoRT - Charakterverwaltung für mein Lieblingsrollenspielsystem',
|
||||
description:'Bamort ist ein Werkzeug zur Charakterverwaltung für Rollenspiele. Es bietet Funktionen zur Charaktererstellung, -entwicklung und -verwaltung mit Unterstützung für Fertigkeiten, Zauber, Ausrüstung und mehr. Viele Ausrüstungsteile, Fertikeiten und Zauber fehlen noch, da das Projekt noch in der Entwicklung ist.',
|
||||
frontendVersion:'Frontend Version',
|
||||
backendVersion:'Backend Version',
|
||||
version:'Version',
|
||||
commit:'Commit',
|
||||
login:'Zum Login',
|
||||
github:'Projekt auf GitHub',
|
||||
},
|
||||
Equipment:'Ausrüstung',
|
||||
equipment:{
|
||||
@@ -433,15 +444,21 @@ export default {
|
||||
notes: 'Notizen'
|
||||
},
|
||||
export: {
|
||||
title: 'Figur exportieren',
|
||||
selectFormat: 'Format wählen',
|
||||
formatPDF: 'PDF',
|
||||
formatVTT: 'VTT Format',
|
||||
formatBamort: 'Bamort Format (JSON)',
|
||||
selectTemplate: 'Vorlage',
|
||||
exportPDF: 'PDF Export',
|
||||
exporting: 'Exportiere...',
|
||||
pleaseSelectTemplate: 'Bitte Vorlage auswählen',
|
||||
exportFailed: 'PDF Export fehlgeschlagen',
|
||||
pleaseSelectFormat: 'Bitte Format auswählen',
|
||||
exportFailed: 'Export fehlgeschlagen',
|
||||
showUserName: 'Benutzername anzeigen',
|
||||
cancel: 'Abbrechen',
|
||||
export: 'Exportieren',
|
||||
generating: 'PDF wird generiert...',
|
||||
generating: 'Datei wird generiert...',
|
||||
pleaseWait: 'Bitte warten, dies kann einen Moment dauern',
|
||||
popupBlocked: 'Popup wurde blockiert. Bitte erlauben Sie Popups für diese Seite.'
|
||||
},
|
||||
|
||||
+21
-3
@@ -42,7 +42,19 @@ export default {
|
||||
ImportData:'Import Data',
|
||||
Logout:'Logout',
|
||||
Register:'Register',
|
||||
Maintenance:'Maintenance', UserManagement:'User Management', },
|
||||
Maintenance:'Maintenance', UserManagement:'User Management',
|
||||
Home:'Home',
|
||||
},
|
||||
landing:{
|
||||
title:'BaMoRT - Character Management for Role-Playing Games',
|
||||
description:'Bamort is a modern character management tool for role-playing games. It provides comprehensive features for character creation, development, and management with support for skills, spells, equipment, and more.',
|
||||
frontendVersion:'Frontend Version',
|
||||
backendVersion:'Backend Version',
|
||||
version:'Version',
|
||||
commit:'Commit',
|
||||
login:'Login',
|
||||
github:'Project on GitHub',
|
||||
},
|
||||
Equipment:'Equipment',
|
||||
equipment:{
|
||||
id:'ID',
|
||||
@@ -429,15 +441,21 @@ export default {
|
||||
notes: 'Notes'
|
||||
},
|
||||
export: {
|
||||
title: 'Export Character',
|
||||
selectFormat: 'Select Format',
|
||||
formatPDF: 'PDF',
|
||||
formatVTT: 'VTT Format',
|
||||
formatBamort: 'Bamort Format (JSON)',
|
||||
selectTemplate: 'Template',
|
||||
exportPDF: 'Export PDF',
|
||||
exporting: 'Exporting...',
|
||||
pleaseSelectTemplate: 'Please select a template',
|
||||
exportFailed: 'PDF export failed',
|
||||
pleaseSelectFormat: 'Please select a format',
|
||||
exportFailed: 'Export failed',
|
||||
showUserName: 'Show username',
|
||||
cancel: 'Cancel',
|
||||
export: 'Export',
|
||||
generating: 'Generating PDF...',
|
||||
generating: 'Generating file...',
|
||||
pleaseWait: 'Please wait, this may take a moment',
|
||||
popupBlocked: 'Popup was blocked. Please allow popups for this site.'
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { isLoggedIn } from "../utils/auth"; // Import the helper function
|
||||
import LandingView from "../views/LandingView.vue";
|
||||
import LoginView from "../views/LoginView.vue";
|
||||
import RegisterView from "../views/RegisterView.vue";
|
||||
import ForgotPasswordView from "../views/ForgotPasswordView.vue";
|
||||
@@ -18,7 +19,8 @@ import CharacterCreation from "@/components/CharacterCreation.vue";
|
||||
|
||||
|
||||
const routes = [
|
||||
{ path: "/", name: "Login", component: LoginView },
|
||||
{ path: "/", name: "Landing", component: LandingView },
|
||||
{ path: "/login", name: "Login", component: LoginView },
|
||||
{ path: "/register", name: "Register", component: RegisterView },
|
||||
{ path: "/forgot-password", name: "ForgotPassword", component: ForgotPasswordView },
|
||||
{ path: "/reset-password", name: "ResetPassword", component: ResetPasswordView },
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Frontend version information
|
||||
export const VERSION = '0.1.21'
|
||||
|
||||
// Git commit will be injected at build time or detected from env
|
||||
export const GIT_COMMIT = import.meta.env.VITE_GIT_COMMIT || 'unknown'
|
||||
|
||||
export function getVersion() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
export function getGitCommit() {
|
||||
return GIT_COMMIT
|
||||
}
|
||||
|
||||
export function getVersionInfo() {
|
||||
return {
|
||||
version: VERSION,
|
||||
gitCommit: GIT_COMMIT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="landing-page">
|
||||
<div class="landing-content">
|
||||
<div class="dragon-container">
|
||||
<img src="/Drache.png" alt="Bamort Dragon" class="dragon-image" />
|
||||
</div>
|
||||
|
||||
<div class="info-container">
|
||||
<h1>{{ $t('landing.title') }}</h1>
|
||||
<p class="description">{{ $t('landing.description') }}</p>
|
||||
|
||||
<div class="version-info">
|
||||
<p>{{ $t('landing.frontendVersion') }}: {{ frontendVersion }}<!-- ({{ frontendCommit }})--> </p>
|
||||
<p>{{ $t('landing.backendVersion') }}: {{ backendVersion }}<!-- ({{ backendCommit }})--> </p>
|
||||
</div>
|
||||
|
||||
<div class="action-links">
|
||||
<router-link to="/login" class="btn btn-primary">
|
||||
{{ $t('landing.login') }}
|
||||
</router-link>
|
||||
<a :href="githubUrl" target="_blank" rel="noopener noreferrer" class="btn btn-secondary">
|
||||
{{ $t('landing.github') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Component-specific styles moved to main.css as per project conventions */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { getVersion, getGitCommit } from '../version'
|
||||
|
||||
export default {
|
||||
name: "LandingView",
|
||||
data() {
|
||||
return {
|
||||
frontendVersion: getVersion(),
|
||||
frontendCommit: getGitCommit(),
|
||||
backendVersion: "Loading...",
|
||||
backendCommit: "Loading...",
|
||||
githubUrl: "https://github.com/Bardioc26/bamort"
|
||||
}
|
||||
},
|
||||
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>
|
||||
@@ -95,7 +95,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
/* All common styles moved to main.css */
|
||||
|
||||
.user-profile {
|
||||
padding: var(--padding-lg);
|
||||
margin-top: 2%;
|
||||
@@ -164,49 +166,6 @@ h1 {
|
||||
gap: var(--margin-md);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--margin-xs);
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-weight: bold;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
padding: var(--padding-sm);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
padding: var(--padding-sm) var(--padding-md);
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background-color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.badge-role-standard {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
# Script to update version number across the project
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <backend_version> [frontend_version]"
|
||||
echo "Example: $0 0.1.31 # Sets both to 0.1.31"
|
||||
echo "Example: $0 0.1.31 0.2.0 # Sets different versions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKEND_VERSION="$1"
|
||||
FRONTEND_VERSION="${2:-$1}" # Use backend version if frontend not specified
|
||||
|
||||
if [ "$BACKEND_VERSION" = "$FRONTEND_VERSION" ]; then
|
||||
echo "Updating both backend and frontend to version $BACKEND_VERSION..."
|
||||
else
|
||||
echo "Updating backend to $BACKEND_VERSION and frontend to $FRONTEND_VERSION..."
|
||||
fi
|
||||
|
||||
# Update backend version
|
||||
BACKEND_VERSION_FILE="backend/config/version.go"
|
||||
if [ -f "$BACKEND_VERSION_FILE" ]; then
|
||||
sed -i "s/const Version = \"[^\"]*\"/const Version = \"$BACKEND_VERSION\"/" "$BACKEND_VERSION_FILE"
|
||||
echo "✓ Updated $BACKEND_VERSION_FILE to $BACKEND_VERSION"
|
||||
else
|
||||
echo "⚠ Warning: $BACKEND_VERSION_FILE not found"
|
||||
fi
|
||||
|
||||
# Update frontend version
|
||||
FRONTEND_VERSION_FILE="frontend/src/version.js"
|
||||
if [ -f "$FRONTEND_VERSION_FILE" ]; then
|
||||
sed -i "s/export const VERSION = '[^']*'/export const VERSION = '$FRONTEND_VERSION'/" "$FRONTEND_VERSION_FILE"
|
||||
echo "✓ Updated $FRONTEND_VERSION_FILE to $FRONTEND_VERSION"
|
||||
else
|
||||
echo "⚠ Warning: $FRONTEND_VERSION_FILE not found"
|
||||
fi
|
||||
|
||||
# Update frontend package.json version
|
||||
FRONTEND_PACKAGE="frontend/package.json"
|
||||
if [ -f "$FRONTEND_PACKAGE" ]; then
|
||||
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$FRONTEND_VERSION\"/" "$FRONTEND_PACKAGE"
|
||||
echo "✓ Updated $FRONTEND_PACKAGE to $FRONTEND_VERSION"
|
||||
else
|
||||
echo "⚠ Warning: $FRONTEND_PACKAGE not found"
|
||||
fi
|
||||
|
||||
# Update VERSION.md files
|
||||
BACKEND_VERSION_MD="backend/VERSION.md"
|
||||
if [ -f "$BACKEND_VERSION_MD" ]; then
|
||||
sed -i "s/## Current Version: .*/## Current Version: $BACKEND_VERSION/" "$BACKEND_VERSION_MD"
|
||||
echo "✓ Updated $BACKEND_VERSION_MD to $BACKEND_VERSION"
|
||||
fi
|
||||
|
||||
FRONTEND_VERSION_MD="frontend/VERSION.md"
|
||||
if [ -f "$FRONTEND_VERSION_MD" ]; then
|
||||
sed -i "s/## Current Version: .*/## Current Version: $FRONTEND_VERSION/" "$FRONTEND_VERSION_MD"
|
||||
echo "✓ Updated $FRONTEND_VERSION_MD to $FRONTEND_VERSION"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ Version update complete!"
|
||||
echo " Backend: $BACKEND_VERSION"
|
||||
echo " Frontend: $FRONTEND_VERSION"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Review changes: git diff"
|
||||
if [ "$BACKEND_VERSION" = "$FRONTEND_VERSION" ]; then
|
||||
echo "2. Commit changes: git commit -am 'Bump version to $BACKEND_VERSION'"
|
||||
echo "3. Tag release: git tag v$BACKEND_VERSION"
|
||||
else
|
||||
echo "2. Commit changes: git commit -am 'Bump backend to $BACKEND_VERSION, frontend to $FRONTEND_VERSION'"
|
||||
echo "3. Tag releases: git tag backend-v$BACKEND_VERSION && git tag frontend-v$FRONTEND_VERSION"
|
||||
fi
|
||||
echo "4. Push: git push && git push --tags"
|
||||
Reference in New Issue
Block a user