Add team roles and content management updates

This commit is contained in:
AzuTear
2026-06-25 19:52:46 +02:00
parent 54293f4c45
commit 693769e5a8
126 changed files with 6966 additions and 738 deletions
@@ -0,0 +1,276 @@
import { computed, onMounted, reactive, ref } from 'vue'
import { ApiRequestError, api } from '../../lib/api'
import type { AdminTeamMember, AdminTeamPermission, AdminTeamRole } from '../../types/awards'
export interface TeamMemberForm {
login: string
displayName: string
role: string
isActive: boolean
}
const emptyMemberForm = (): TeamMemberForm => ({
login: '',
displayName: '',
role: 'member',
isActive: true,
})
export function useAdminTeamManager() {
const loading = ref(true)
const saving = ref(false)
const errorMessage = ref('')
const successMessage = ref('')
const generatedPassword = ref('')
const generatedPasswordLogin = ref('')
const confirmDeleteMemberId = ref<number | null>(null)
const members = ref<AdminTeamMember[]>([])
const roles = ref<AdminTeamRole[]>([])
const permissions = ref<AdminTeamPermission[]>([])
const editingMemberId = ref<number | null>(null)
const rolePermissionDrafts = ref<Record<string, string[]>>({})
const savedRoleSnapshot = ref('')
const memberForm = reactive<TeamMemberForm>(emptyMemberForm())
const activeMembers = computed(() => members.value.filter((member) => member.isActive).length)
const pendingPasswordChanges = computed(() => members.value.filter((member) => member.mustChangePassword).length)
const roleOptions = computed(() => roles.value.map((role) => ({ value: role.key, label: role.label })))
const selectedMember = computed(() => members.value.find((member) => member.id === editingMemberId.value) ?? null)
const hasRoleChanges = computed(() =>
Boolean(savedRoleSnapshot.value) && JSON.stringify(rolePermissionDrafts.value) !== savedRoleSnapshot.value,
)
function resetMessages() {
errorMessage.value = ''
successMessage.value = ''
}
function applyTeamResponse(response: { members: AdminTeamMember[]; roles: AdminTeamRole[]; permissions: AdminTeamPermission[] }) {
members.value = response.members
roles.value = response.roles
permissions.value = response.permissions
rolePermissionDrafts.value = Object.fromEntries(
response.roles.map((role) => [role.key, [...role.permissionKeys]]),
)
savedRoleSnapshot.value = JSON.stringify(rolePermissionDrafts.value)
}
async function loadTeam() {
loading.value = true
resetMessages()
try {
applyTeamResponse(await api.getAdminTeam())
} catch (error) {
errorMessage.value = error instanceof ApiRequestError
? error.message
: 'Team-Daten konnten nicht geladen werden.'
} finally {
loading.value = false
}
}
function startCreateMember() {
editingMemberId.value = null
Object.assign(memberForm, emptyMemberForm())
generatedPassword.value = ''
generatedPasswordLogin.value = ''
resetMessages()
}
function startEditMember(member: AdminTeamMember) {
editingMemberId.value = member.id
memberForm.login = member.login
memberForm.displayName = member.displayName
memberForm.role = member.role
memberForm.isActive = member.isActive
generatedPassword.value = ''
generatedPasswordLogin.value = ''
resetMessages()
}
function validateMemberForm() {
if (!memberForm.login.trim() && editingMemberId.value === null) {
errorMessage.value = 'Bitte gib einen Login an.'
return false
}
if (!memberForm.displayName.trim()) {
errorMessage.value = 'Bitte gib einen Anzeigenamen an.'
return false
}
return true
}
async function saveMember() {
resetMessages()
generatedPassword.value = ''
generatedPasswordLogin.value = ''
if (!validateMemberForm()) return false
saving.value = true
try {
if (editingMemberId.value === null) {
const result = await api.createAdminTeamMember({
login: memberForm.login,
displayName: memberForm.displayName,
role: memberForm.role,
})
await loadTeam()
generatedPassword.value = result.generatedPassword
generatedPasswordLogin.value = memberForm.login
successMessage.value = 'Team-Login wurde erstellt. Das temporäre Passwort ist nur jetzt sichtbar.'
} else {
await api.updateAdminTeamMember(editingMemberId.value, {
login: memberForm.login,
displayName: memberForm.displayName,
role: memberForm.role,
isActive: memberForm.isActive,
})
await loadTeam()
successMessage.value = 'Team-Mitglied wurde gespeichert.'
}
return true
} catch (error) {
errorMessage.value = error instanceof ApiRequestError
? error.message
: 'Team-Mitglied konnte nicht gespeichert werden.'
return false
} finally {
saving.value = false
}
}
async function resetMemberPassword(member: AdminTeamMember) {
resetMessages()
generatedPassword.value = ''
generatedPasswordLogin.value = ''
saving.value = true
try {
const result = await api.resetAdminTeamMemberPassword(member.id)
await loadTeam()
generatedPassword.value = result.generatedPassword
generatedPasswordLogin.value = member.login
successMessage.value = 'Passwort wurde zurückgesetzt. Das Mitglied muss es beim nächsten Login ändern.'
} catch (error) {
errorMessage.value = error instanceof ApiRequestError
? error.message
: 'Passwort konnte nicht zurückgesetzt werden.'
} finally {
saving.value = false
}
}
function requestDeleteMember(member: AdminTeamMember) {
confirmDeleteMemberId.value = member.id
resetMessages()
}
function cancelDeleteMember() {
confirmDeleteMemberId.value = null
}
async function deleteMember(member: AdminTeamMember) {
resetMessages()
generatedPassword.value = ''
generatedPasswordLogin.value = ''
saving.value = true
try {
await api.deleteAdminTeamMember(member.id)
confirmDeleteMemberId.value = null
if (editingMemberId.value === member.id) {
startCreateMember()
}
await loadTeam()
successMessage.value = 'Team-Mitglied wurde gelöscht.'
} catch (error) {
errorMessage.value = error instanceof ApiRequestError
? error.message
: 'Team-Mitglied konnte nicht gelöscht werden.'
} finally {
saving.value = false
}
}
function roleHasPermission(roleKey: string, permissionKey: string) {
return rolePermissionDrafts.value[roleKey]?.includes(permissionKey) ?? false
}
function setRolePermission(roleKey: string, permissionKey: string, checked: boolean) {
if (roleKey === 'owner') return
const current = new Set(rolePermissionDrafts.value[roleKey] ?? [])
if (checked) {
current.add(permissionKey)
} else {
current.delete(permissionKey)
}
rolePermissionDrafts.value = {
...rolePermissionDrafts.value,
[roleKey]: [...current].sort(),
}
}
async function saveRolePermissions() {
resetMessages()
saving.value = true
try {
const result = await api.updateAdminTeamRoles({
roles: roles.value.map((role) => ({
key: role.key,
permissionKeys: rolePermissionDrafts.value[role.key] ?? [],
})),
})
roles.value = result.roles
savedRoleSnapshot.value = JSON.stringify(rolePermissionDrafts.value)
successMessage.value = 'Rollen und Berechtigungen wurden gespeichert.'
} catch (error) {
errorMessage.value = error instanceof ApiRequestError
? error.message
: 'Berechtigungen konnten nicht gespeichert werden.'
} finally {
saving.value = false
}
}
onMounted(loadTeam)
return {
loading,
saving,
errorMessage,
successMessage,
generatedPassword,
generatedPasswordLogin,
confirmDeleteMemberId,
members,
roles,
permissions,
memberForm,
editingMemberId,
selectedMember,
activeMembers,
pendingPasswordChanges,
roleOptions,
hasRoleChanges,
loadTeam,
startCreateMember,
startEditMember,
saveMember,
resetMemberPassword,
requestDeleteMember,
cancelDeleteMember,
deleteMember,
roleHasPermission,
setRolePermission,
saveRolePermissions,
}
}