User can change a character from private to public and back
This commit is contained in:
@@ -10,6 +10,8 @@ func RegisterRoutes(r *gin.RouterGroup) {
|
|||||||
charGrp.POST("", CreateCharacter)
|
charGrp.POST("", CreateCharacter)
|
||||||
charGrp.GET("/:id", GetCharacter)
|
charGrp.GET("/:id", GetCharacter)
|
||||||
charGrp.PUT("/:id", UpdateCharacter)
|
charGrp.PUT("/:id", UpdateCharacter)
|
||||||
|
charGrp.PATCH("/:id", UpdateCharacter)
|
||||||
|
|
||||||
charGrp.DELETE("/:id", DeleteCharacter)
|
charGrp.DELETE("/:id", DeleteCharacter)
|
||||||
charGrp.PUT("/:id/image", UpdateCharacterImage)
|
charGrp.PUT("/:id/image", UpdateCharacterImage)
|
||||||
charGrp.GET("/:id/datasheet-options", GetDatasheetOptions)
|
charGrp.GET("/:id/datasheet-options", GetDatasheetOptions)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func SetupGin(r *gin.Engine) {
|
|||||||
r.Use(cors.New(cors.Config{
|
r.Use(cors.New(cors.Config{
|
||||||
//AllowOrigins: []string{"http://localhost:3000"}, // Replace with your frontend's URL
|
//AllowOrigins: []string{"http://localhost:3000"}, // Replace with your frontend's URL
|
||||||
AllowOrigins: allowedOrigins,
|
AllowOrigins: allowedOrigins,
|
||||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
|
||||||
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
||||||
ExposeHeaders: []string{"Content-Length"},
|
ExposeHeaders: []string{"Content-Length"},
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
<button @click="showExportDialog = true" class="export-button-small" :title="$t('export.title')">
|
<button @click="showExportDialog = true" class="export-button-small" :title="$t('export.title')">
|
||||||
📄
|
📄
|
||||||
</button>
|
</button>
|
||||||
|
<button @click="showVisibilityDialog = true" class="export-button-small" :title="$t('visibility.title')">
|
||||||
|
{{ character.public ? '🌐' : '🔒' }}
|
||||||
|
</button>
|
||||||
<h2>{{ $t('char') }}: {{ character.name }} ({{ $t(currentView) }})</h2>
|
<h2>{{ $t('char') }}: {{ character.name }} ({{ $t(currentView) }})</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,6 +21,15 @@
|
|||||||
@export-success="handleExportSuccess"
|
@export-success="handleExportSuccess"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Visibility Dialog -->
|
||||||
|
<VisibilityDialog
|
||||||
|
:characterId="id"
|
||||||
|
:currentVisibility="character.public"
|
||||||
|
:showDialog="showVisibilityDialog"
|
||||||
|
@update:showDialog="showVisibilityDialog = $event"
|
||||||
|
@visibility-updated="handleVisibilityUpdated"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Submenu Content -->
|
<!-- Submenu Content -->
|
||||||
<!-- <div class="character-aspect"> -->
|
<!-- <div class="character-aspect"> -->
|
||||||
<component :is="currentView" :character="character" @character-updated="refreshCharacter"/>
|
<component :is="currentView" :character="character" @character-updated="refreshCharacter"/>
|
||||||
@@ -53,6 +65,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import API from '../utils/api'
|
import API from '../utils/api'
|
||||||
import ExportDialog from "./ExportDialog.vue";
|
import ExportDialog from "./ExportDialog.vue";
|
||||||
|
import VisibilityDialog from "./VisibilityDialog.vue";
|
||||||
import DatasheetView from "./DatasheetView.vue"; // Component for character stats
|
import DatasheetView from "./DatasheetView.vue"; // Component for character stats
|
||||||
import SkillView from "./SkillView.vue"; // Component for character history
|
import SkillView from "./SkillView.vue"; // Component for character history
|
||||||
import WeaponView from "./WeaponView.vue"; // Component for character history
|
import WeaponView from "./WeaponView.vue"; // Component for character history
|
||||||
@@ -67,6 +80,7 @@ export default {
|
|||||||
props: ["id"], // Receive the route parameter as a prop
|
props: ["id"], // Receive the route parameter as a prop
|
||||||
components: {
|
components: {
|
||||||
ExportDialog,
|
ExportDialog,
|
||||||
|
VisibilityDialog,
|
||||||
DatasheetView,
|
DatasheetView,
|
||||||
SkillView,
|
SkillView,
|
||||||
WeaponView,
|
WeaponView,
|
||||||
@@ -81,6 +95,7 @@ export default {
|
|||||||
currentView: "DatasheetView", // Default view
|
currentView: "DatasheetView", // Default view
|
||||||
lastView: "DatasheetView",
|
lastView: "DatasheetView",
|
||||||
showExportDialog: false,
|
showExportDialog: false,
|
||||||
|
showVisibilityDialog: false,
|
||||||
menus: [
|
menus: [
|
||||||
{ id: 1, name: "Datasheet", component: "DatasheetView" },
|
{ id: 1, name: "Datasheet", component: "DatasheetView" },
|
||||||
{ id: 2, name: "Skill", component: "SkillView" },
|
{ id: 2, name: "Skill", component: "SkillView" },
|
||||||
@@ -108,6 +123,12 @@ export default {
|
|||||||
console.log('PDF exported successfully')
|
console.log('PDF exported successfully')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleVisibilityUpdated(isPublic) {
|
||||||
|
this.character.public = isPublic
|
||||||
|
this.showVisibilityDialog = false
|
||||||
|
console.log('Character visibility updated to:', isPublic ? 'public' : 'private')
|
||||||
|
},
|
||||||
|
|
||||||
changeView(view) {
|
changeView(view) {
|
||||||
this.lastView = this.currentView;
|
this.lastView = this.currentView;
|
||||||
this.currentView = view;
|
this.currentView = view;
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="showDialog" class="modal-overlay" @click.self="closeDialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>{{ $t('visibility.title') }}</h3>
|
||||||
|
<button @click="closeDialog" class="close-button">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div v-if="isUpdating" class="loading-overlay">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<p>{{ $t('visibility.updating') }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<p>{{ $t('visibility.description') }}</p>
|
||||||
|
<div class="visibility-options">
|
||||||
|
<label class="radio-option">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
:value="false"
|
||||||
|
v-model="isPublic"
|
||||||
|
:disabled="isUpdating"
|
||||||
|
>
|
||||||
|
<div class="option-content">
|
||||||
|
<span class="option-label">{{ $t('visibility.private') }}</span>
|
||||||
|
<span class="option-description">{{ $t('visibility.privateDescription') }}</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<label class="radio-option">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
:value="true"
|
||||||
|
v-model="isPublic"
|
||||||
|
:disabled="isUpdating"
|
||||||
|
>
|
||||||
|
<div class="option-content">
|
||||||
|
<span class="option-label">{{ $t('visibility.public') }}</span>
|
||||||
|
<span class="option-description">{{ $t('visibility.publicDescription') }}</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button @click="closeDialog" class="btn-cancel" :disabled="isUpdating">
|
||||||
|
{{ $t('visibility.cancel') }}
|
||||||
|
</button>
|
||||||
|
<button @click="updateVisibility" class="btn-primary" :disabled="isUpdating">
|
||||||
|
<span v-if="!isUpdating">{{ $t('visibility.save') }}</span>
|
||||||
|
<span v-else>{{ $t('visibility.saving') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import API from '../utils/api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VisibilityDialog",
|
||||||
|
props: {
|
||||||
|
characterId: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
currentVisibility: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showDialog: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isPublic: false,
|
||||||
|
isUpdating: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
currentVisibility: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newValue) {
|
||||||
|
this.isPublic = newValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showDialog(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.isPublic = this.currentVisibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeDialog() {
|
||||||
|
if (!this.isUpdating) {
|
||||||
|
this.$emit('update:showDialog', false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateVisibility() {
|
||||||
|
this.isUpdating = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('token')
|
||||||
|
await API.patch(`/api/characters/${this.characterId}`,
|
||||||
|
{ public: this.isPublic },
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
this.$emit('visibility-updated', this.isPublic)
|
||||||
|
this.closeDialog()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update character visibility:', error)
|
||||||
|
alert(this.$t('visibility.updateError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
} finally {
|
||||||
|
this.isUpdating = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.visibility-options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 15px;
|
||||||
|
border: 2px solid #dee2e6;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-option:hover {
|
||||||
|
border-color: #007bff;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-option input[type="radio"] {
|
||||||
|
margin-top: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-label {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-description {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-option input[type="radio"]:checked + .option-content .option-label {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-option:has(input[type="radio"]:checked) {
|
||||||
|
border-color: #007bff;
|
||||||
|
background-color: #e7f3ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -513,6 +513,19 @@ export default {
|
|||||||
pleaseWait: 'Bitte warten, dies kann einen Moment dauern',
|
pleaseWait: 'Bitte warten, dies kann einen Moment dauern',
|
||||||
popupBlocked: 'Popup wurde blockiert. Bitte erlauben Sie Popups für diese Seite.'
|
popupBlocked: 'Popup wurde blockiert. Bitte erlauben Sie Popups für diese Seite.'
|
||||||
},
|
},
|
||||||
|
visibility: {
|
||||||
|
title: 'Sichtbarkeit ändern',
|
||||||
|
description: 'Legen Sie fest, wer diesen Charakter sehen kann.',
|
||||||
|
private: 'Privat',
|
||||||
|
privateDescription: 'Nur Sie können diesen Charakter sehen',
|
||||||
|
public: 'Öffentlich',
|
||||||
|
publicDescription: 'Alle Benutzer können diesen Charakter sehen',
|
||||||
|
cancel: 'Abbrechen',
|
||||||
|
save: 'Speichern',
|
||||||
|
saving: 'Wird gespeichert...',
|
||||||
|
updating: 'Sichtbarkeit wird aktualisiert...',
|
||||||
|
updateError: 'Fehler beim Aktualisieren der Sichtbarkeit'
|
||||||
|
},
|
||||||
userManagement: {
|
userManagement: {
|
||||||
title: 'Benutzerverwaltung',
|
title: 'Benutzerverwaltung',
|
||||||
loading: 'Lade Benutzer...',
|
loading: 'Lade Benutzer...',
|
||||||
|
|||||||
@@ -509,6 +509,19 @@ export default {
|
|||||||
pleaseWait: 'Please wait, this may take a moment',
|
pleaseWait: 'Please wait, this may take a moment',
|
||||||
popupBlocked: 'Popup was blocked. Please allow popups for this site.'
|
popupBlocked: 'Popup was blocked. Please allow popups for this site.'
|
||||||
},
|
},
|
||||||
|
visibility: {
|
||||||
|
title: 'Change Visibility',
|
||||||
|
description: 'Set who can see this character.',
|
||||||
|
private: 'Private',
|
||||||
|
privateDescription: 'Only you can see this character',
|
||||||
|
public: 'Public',
|
||||||
|
publicDescription: 'All users can see this character',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
save: 'Save',
|
||||||
|
saving: 'Saving...',
|
||||||
|
updating: 'Updating visibility...',
|
||||||
|
updateError: 'Failed to update visibility'
|
||||||
|
},
|
||||||
userManagement: {
|
userManagement: {
|
||||||
title: 'User Management',
|
title: 'User Management',
|
||||||
loading: 'Loading users...',
|
loading: 'Loading users...',
|
||||||
|
|||||||
Reference in New Issue
Block a user