|
|
|
@ -1,132 +1,364 @@
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<html>
|
|
|
|
|
|
|
|
|
|
|
|
<head>
|
|
|
|
<head>
|
|
|
|
<meta name="description" content="Org Detail">
|
|
|
|
<meta name="description" content="Org Detail">
|
|
|
|
<title>{{ $t('org.detail') }}</title>
|
|
|
|
<title>{{ org ? org.name : ($t('org.detail') || 'Organization Detail') }}</title>
|
|
|
|
<style>
|
|
|
|
<style>
|
|
|
|
|
|
|
|
body {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
gap: var(--spacing-lg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-header {
|
|
|
|
.page-header {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
align-items: center;
|
|
|
|
margin-bottom: 20px;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.section {
|
|
|
|
|
|
|
|
background: #fff;
|
|
|
|
.header-left {
|
|
|
|
padding: 20px;
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.btn-back {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
width: 36px;
|
|
|
|
|
|
|
|
height: 36px;
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
|
|
|
background: var(--bg-color-tertiary);
|
|
|
|
|
|
|
|
color: var(--text-color);
|
|
|
|
|
|
|
|
border: 1px solid var(--border-color);
|
|
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
transition: all var(--transition-fast);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.btn-back:hover {
|
|
|
|
|
|
|
|
background: var(--border-color);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.header-actions {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.section {
|
|
|
|
|
|
|
|
background: var(--bg-color-secondary);
|
|
|
|
|
|
|
|
padding: var(--spacing-lg);
|
|
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
margin-bottom: 20px;
|
|
|
|
border: 1px solid var(--border-color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
.section-title {
|
|
|
|
font-size: 18px;
|
|
|
|
font-size: var(--font-size-lg);
|
|
|
|
font-weight: bold;
|
|
|
|
font-weight: 600;
|
|
|
|
margin-bottom: 15px;
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
color: var(--text-color);
|
|
|
|
color: var(--text-color);
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-grid {
|
|
|
|
.info-grid {
|
|
|
|
display: grid;
|
|
|
|
display: grid;
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
|
|
gap: 20px;
|
|
|
|
gap: var(--spacing-lg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-item {
|
|
|
|
.info-item {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-direction: column;
|
|
|
|
gap: 5px;
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-label {
|
|
|
|
.info-label {
|
|
|
|
font-size: 14px;
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-value {
|
|
|
|
.info-value {
|
|
|
|
font-size: 16px;
|
|
|
|
font-size: var(--font-size-md);
|
|
|
|
font-weight: 500;
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
|
|
color: var(--text-color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.btn-danger {
|
|
|
|
|
|
|
|
background-color: var(--color-danger);
|
|
|
|
.org-icon-large {
|
|
|
|
|
|
|
|
width: 64px;
|
|
|
|
|
|
|
|
height: 64px;
|
|
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
|
|
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
color: white;
|
|
|
|
color: white;
|
|
|
|
padding: 8px 16px;
|
|
|
|
font-size: 28px;
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
font-weight: bold;
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.btn-edit {
|
|
|
|
|
|
|
|
background-color: var(--color-info);
|
|
|
|
.org-header-info {
|
|
|
|
color: white;
|
|
|
|
display: flex;
|
|
|
|
padding: 8px 16px;
|
|
|
|
align-items: center;
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
gap: var(--spacing-lg);
|
|
|
|
cursor: pointer;
|
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
border: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.org-header-text {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.org-header-name {
|
|
|
|
|
|
|
|
font-size: var(--font-size-2xl);
|
|
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
color: var(--text-color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.org-header-desc {
|
|
|
|
|
|
|
|
font-size: var(--font-size-md);
|
|
|
|
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Members Table */
|
|
|
|
|
|
|
|
.members-header {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
table {
|
|
|
|
table {
|
|
|
|
width: 100%;
|
|
|
|
width: 100%;
|
|
|
|
border-collapse: collapse;
|
|
|
|
border-collapse: collapse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
th, td {
|
|
|
|
th, td {
|
|
|
|
text-align: left;
|
|
|
|
text-align: left;
|
|
|
|
padding: 10px;
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
border-bottom: 1px solid var(--border-color);
|
|
|
|
border-bottom: 1px solid var(--border-color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
th {
|
|
|
|
th {
|
|
|
|
background-color: var(--bg-color-tertiary);
|
|
|
|
background-color: var(--bg-color-tertiary);
|
|
|
|
font-weight: 600;
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
td {
|
|
|
|
|
|
|
|
font-size: var(--font-size-md);
|
|
|
|
|
|
|
|
color: var(--text-color);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tr:hover td {
|
|
|
|
|
|
|
|
background-color: var(--bg-color-tertiary);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.role-badge {
|
|
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.role-admin {
|
|
|
|
|
|
|
|
background-color: var(--color-primary);
|
|
|
|
|
|
|
|
color: var(--color-primary-text);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.role-member {
|
|
|
|
|
|
|
|
background-color: var(--bg-color-tertiary);
|
|
|
|
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.loading-state {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
padding: var(--spacing-xl);
|
|
|
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.spinner {
|
|
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
|
|
border: 3px solid var(--border-color);
|
|
|
|
|
|
|
|
border-top-color: var(--color-primary);
|
|
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
|
|
animation: spin 1s linear infinite;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
|
|
|
to {
|
|
|
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.empty-state {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
padding: var(--spacing-lg);
|
|
|
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.empty-state i {
|
|
|
|
|
|
|
|
font-size: 48px;
|
|
|
|
|
|
|
|
color: var(--border-color);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</style>
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
|
|
<body>
|
|
|
|
<body>
|
|
|
|
|
|
|
|
<!-- Loading State -->
|
|
|
|
|
|
|
|
<div class="loading-state" v-if="loading">
|
|
|
|
|
|
|
|
<div class="spinner"></div>
|
|
|
|
|
|
|
|
<span>{{ $t('common.loading') || 'Loading...' }}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<template v:if="!loading && org">
|
|
|
|
|
|
|
|
<!-- Page Header -->
|
|
|
|
<div class="page-header">
|
|
|
|
<div class="page-header">
|
|
|
|
<h1>{{ org ? org.name : 'Loading...' }}</h1>
|
|
|
|
<div class="header-left">
|
|
|
|
<div class="actions">
|
|
|
|
<button class="btn-back" @click="goBack" title="{{ $t('common.back') || 'Back' }}">
|
|
|
|
<button class="btn-edit" @click="editOrg">{{ $t('common.edit') }}</button>
|
|
|
|
<i class="fas fa-arrow-left"></i>
|
|
|
|
<button class="btn-danger" @click="deleteOrg">{{ $t('common.delete') }}</button>
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<h1>{{ $t('org.detail') || 'Organization Detail' }}</h1>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="header-actions">
|
|
|
|
|
|
|
|
<v-btn variant="outline" :click="openEditModal">
|
|
|
|
|
|
|
|
<i class="fas fa-edit"></i>
|
|
|
|
|
|
|
|
{{ $t('common.edit') || 'Edit' }}
|
|
|
|
|
|
|
|
</v-btn>
|
|
|
|
|
|
|
|
<v-btn color="danger" :click="deleteOrg">
|
|
|
|
|
|
|
|
<i class="fas fa-trash"></i>
|
|
|
|
|
|
|
|
{{ $t('common.delete') || 'Delete' }}
|
|
|
|
|
|
|
|
</v-btn>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Org Info Section -->
|
|
|
|
<div class="section">
|
|
|
|
<div class="section">
|
|
|
|
<div class="section-title">{{ $t('org.info') }}</div>
|
|
|
|
<div class="org-header-info">
|
|
|
|
<div class="info-grid" v-if="org">
|
|
|
|
<div class="org-icon-large">{{ org.name ? org.name.charAt(0).toUpperCase() : 'O' }}</div>
|
|
|
|
|
|
|
|
<div class="org-header-text">
|
|
|
|
|
|
|
|
<div class="org-header-name">{{ org.name }}</div>
|
|
|
|
|
|
|
|
<div class="org-header-desc">{{ org.description || ($t('org.no_description') || 'No description') }}</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-grid">
|
|
|
|
<div class="info-item">
|
|
|
|
<div class="info-item">
|
|
|
|
<span class="info-label">ID</span>
|
|
|
|
<span class="info-label">ID</span>
|
|
|
|
<span class="info-value">{{ org.id }}</span>
|
|
|
|
<span class="info-value">{{ org.id }}</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="info-item">
|
|
|
|
<div class="info-item">
|
|
|
|
<span class="info-label">Name</span>
|
|
|
|
<span class="info-label">{{ $t('org.created_at') || 'Created At' }}</span>
|
|
|
|
<span class="info-value">{{ org.name }}</span>
|
|
|
|
<span class="info-value">{{ formatDate(org.created_at) }}</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="info-item">
|
|
|
|
<div class="info-item" v:if="org.updated_at">
|
|
|
|
<span class="info-label">Created At</span>
|
|
|
|
<span class="info-label">{{ $t('org.updated_at') || 'Updated At' }}</span>
|
|
|
|
<span class="info-value">{{ new Date(org.created_at).toLocaleDateString() }}</span>
|
|
|
|
<span class="info-value">{{ formatDate(org.updated_at) }}</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Members Section -->
|
|
|
|
<div class="section">
|
|
|
|
<div class="section">
|
|
|
|
<div class="section-title">{{ $t('org.members') }}</div>
|
|
|
|
<div class="members-header">
|
|
|
|
<table>
|
|
|
|
<div class="section-title">
|
|
|
|
|
|
|
|
<i class="fas fa-users"></i>
|
|
|
|
|
|
|
|
{{ $t('org.members') || 'Members' }}
|
|
|
|
|
|
|
|
<span style="font-size: var(--font-size-sm); color: var(--text-color-tertiary);">({{ members.length }})</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="empty-state" v:if="members.length === 0">
|
|
|
|
|
|
|
|
<i class="fas fa-user-slash"></i>
|
|
|
|
|
|
|
|
<p>{{ $t('org.no_members') || 'No members yet' }}</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<table v:if="members.length > 0">
|
|
|
|
<thead>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<tr>
|
|
|
|
<th>Username</th>
|
|
|
|
<th>{{ $t('user.username') || 'Username' }}</th>
|
|
|
|
<th>Role</th>
|
|
|
|
<th>{{ $t('user.email') || 'Email' }}</th>
|
|
|
|
<th>Actions</th>
|
|
|
|
<th>{{ $t('user.role') || 'Role' }}</th>
|
|
|
|
|
|
|
|
<th>{{ $t('common.actions') || 'Actions' }}</th>
|
|
|
|
</tr>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
<tbody>
|
|
|
|
<tr v-for="member in members">
|
|
|
|
<tr v:for="member in members">
|
|
|
|
<td>{{ member.username }}</td>
|
|
|
|
<td>{{ member.username }}</td>
|
|
|
|
<td>{{ member.role }}</td>
|
|
|
|
<td>{{ member.email || '-' }}</td>
|
|
|
|
<td>
|
|
|
|
<td>
|
|
|
|
<button class="btn-sm btn-danger" @click="removeMember(member.id)">Remove</button>
|
|
|
|
<span class="role-badge" :class="member.role === 'admin' ? 'role-admin' : 'role-member'">
|
|
|
|
|
|
|
|
{{ member.role || 'member' }}
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
|
|
|
<td>
|
|
|
|
|
|
|
|
<v-btn size="sm" color="danger" variant="outline" :click="() => removeMember(member)"
|
|
|
|
|
|
|
|
v:if="member.id !== currentUserId">
|
|
|
|
|
|
|
|
<i class="fas fa-user-minus"></i>
|
|
|
|
|
|
|
|
{{ $t('org.remove_member') || 'Remove' }}
|
|
|
|
|
|
|
|
</v-btn>
|
|
|
|
|
|
|
|
<span v:else style="color: var(--text-color-tertiary); font-size: var(--font-size-sm);">
|
|
|
|
|
|
|
|
{{ $t('org.you') || 'You' }}
|
|
|
|
|
|
|
|
</span>
|
|
|
|
</td>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</tr>
|
|
|
|
</tbody>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Edit Dialog -->
|
|
|
|
|
|
|
|
<v-dialog v:visible="showEditModal" title="{{ $t('org.edit') || 'Edit Organization' }}">
|
|
|
|
|
|
|
|
<v-input type="text" v:value="editForm.name" label="{{ $t('org.name') || 'Organization Name' }}" required
|
|
|
|
|
|
|
|
placeholder="{{ $t('org.name_placeholder') || 'Enter organization name' }}">
|
|
|
|
|
|
|
|
</v-input>
|
|
|
|
|
|
|
|
<v-input type="textarea" v:value="editForm.description" label="{{ $t('org.description') || 'Description' }}"
|
|
|
|
|
|
|
|
placeholder="{{ $t('org.desc_placeholder') || 'Enter organization description (optional)' }}">
|
|
|
|
|
|
|
|
</v-input>
|
|
|
|
|
|
|
|
<div vslot="footer">
|
|
|
|
|
|
|
|
<v-btn variant="outline" :click="closeEditModal">{{ $t('common.cancel') || 'Cancel' }}</v-btn>
|
|
|
|
|
|
|
|
<v-btn color="primary" :disabled="!editForm.name" :click="saveOrg">
|
|
|
|
|
|
|
|
{{ $t('common.save') || 'Save' }}
|
|
|
|
|
|
|
|
</v-btn>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</v-dialog>
|
|
|
|
</body>
|
|
|
|
</body>
|
|
|
|
<script setup>
|
|
|
|
<script setup>
|
|
|
|
orgId = $router.params.id;
|
|
|
|
orgId = $router.params.id;
|
|
|
|
org = null;
|
|
|
|
org = null;
|
|
|
|
members = [];
|
|
|
|
members = [];
|
|
|
|
|
|
|
|
loading = false;
|
|
|
|
|
|
|
|
currentUserId = $env.$vbase.user?.id;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Edit modal state
|
|
|
|
|
|
|
|
showEditModal = false;
|
|
|
|
|
|
|
|
editForm = {
|
|
|
|
|
|
|
|
name: "",
|
|
|
|
|
|
|
|
description: ""
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
loadData = async () => {
|
|
|
|
loadData = async () => {
|
|
|
|
|
|
|
|
loading = true;
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const [orgRes, membersRes] = await Promise.all([
|
|
|
|
const [orgRes, membersRes] = await Promise.all([
|
|
|
|
$axios.get(`/api/orgs/${orgId}`),
|
|
|
|
$axios.get(`/api/orgs/${orgId}`),
|
|
|
|
@ -136,30 +368,80 @@
|
|
|
|
members = membersRes || [];
|
|
|
|
members = membersRes || [];
|
|
|
|
} catch (e) {
|
|
|
|
} catch (e) {
|
|
|
|
$message.error(e.message);
|
|
|
|
$message.error(e.message);
|
|
|
|
|
|
|
|
if (e.status === 404) {
|
|
|
|
|
|
|
|
$router.push('/org');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
loading = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
editOrg = () => {
|
|
|
|
formatDate = (dateStr) => {
|
|
|
|
$message.info("Edit feature coming soon");
|
|
|
|
if (!dateStr) return '-';
|
|
|
|
|
|
|
|
const date = new Date(dateStr);
|
|
|
|
|
|
|
|
return date.toLocaleString();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
goBack = () => {
|
|
|
|
|
|
|
|
$router.back();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Edit modal
|
|
|
|
|
|
|
|
openEditModal = () => {
|
|
|
|
|
|
|
|
editForm = {
|
|
|
|
|
|
|
|
name: org.name,
|
|
|
|
|
|
|
|
description: org.description || ""
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
showEditModal = true;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
closeEditModal = () => {
|
|
|
|
|
|
|
|
showEditModal = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
saveOrg = async () => {
|
|
|
|
|
|
|
|
if (!editForm.name) return;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
await $axios.patch(`/api/orgs/${orgId}`, {
|
|
|
|
|
|
|
|
name: editForm.name,
|
|
|
|
|
|
|
|
description: editForm.description
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
$message.success($t('org.updated') || "Updated successfully");
|
|
|
|
|
|
|
|
closeEditModal();
|
|
|
|
|
|
|
|
loadData();
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
$message.error(e.message);
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
deleteOrg = async () => {
|
|
|
|
deleteOrg = async () => {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
await $message.confirm("Are you sure you want to delete this organization?");
|
|
|
|
await $message.confirm($t('org.delete_confirm') || `Are you sure you want to delete "${org.name}"?`);
|
|
|
|
await $axios.delete(`/api/orgs/${orgId}`);
|
|
|
|
await $axios.delete(`/api/orgs/${orgId}`);
|
|
|
|
$message.success("Deleted");
|
|
|
|
$message.success($t('org.deleted') || "Deleted successfully");
|
|
|
|
$router.push('/org');
|
|
|
|
$router.push('/org');
|
|
|
|
} catch (e) {
|
|
|
|
} catch (e) {
|
|
|
|
// Cancelled or error
|
|
|
|
// Cancelled
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
removeMember = async (userId) => {
|
|
|
|
removeMember = async (member) => {
|
|
|
|
// Implement remove logic
|
|
|
|
try {
|
|
|
|
$message.info("Remove member feature coming soon");
|
|
|
|
await $message.confirm($t('org.remove_confirm') || `Remove "${member.username}" from organization?`);
|
|
|
|
|
|
|
|
// API might not support this yet, but prepare for it
|
|
|
|
|
|
|
|
await $axios.delete(`/api/orgs/${orgId}/members/${member.id}`);
|
|
|
|
|
|
|
|
$message.success($t('org.member_removed') || "Member removed");
|
|
|
|
|
|
|
|
loadData();
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
// Cancelled or not implemented
|
|
|
|
|
|
|
|
if (e.status === 404) {
|
|
|
|
|
|
|
|
$message.info($t('org.feature_coming') || "Feature coming soon");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
<script>
|
|
|
|
<script>
|
|
|
|
$data.loadData();
|
|
|
|
$data.loadData();
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
</html>
|
|
|
|
</html>
|
|
|
|
|