160 lines
6.0 KiB
JavaScript
160 lines
6.0 KiB
JavaScript
/* ===== RadiusFreeUsers.js (Vue 3 + TT-Core) ===== */
|
|
|
|
const RadiusFreeUsers = {
|
|
name: 'RadiusFreeUsers',
|
|
template: `
|
|
<div class="tt-scope">
|
|
<div class="free-users-grid">
|
|
<div class="free-users-column">
|
|
<div class="h5" style="display:flex;align-items:center;justify-content:space-between;gap:10px;">
|
|
<span><i class="fa-duotone fa-shield-keyhole"></i> Freie NAT Benutzer <span class="badge">{{ filteredNat.length }}</span></span>
|
|
<button class="ghost-btn" @click="reloadNat" :disabled="loadingNat">
|
|
<span v-if="!loadingNat"><i class="fa-duotone fa-rotate-right"></i> Neu laden</span>
|
|
<span v-else class="btn-loader"></span>
|
|
</button>
|
|
</div>
|
|
<tt-data-table
|
|
:items="filteredNat"
|
|
:is-loading="loadingNat"
|
|
:has-searched="true"
|
|
density="ultra-compact"
|
|
table-class="no-min-width"
|
|
no-results-placeholder-text="Keine Treffer"
|
|
:skeleton-row-count="8"
|
|
>
|
|
<template #head>
|
|
<thead>
|
|
<tr>
|
|
<th>Username</th>
|
|
<th>Info</th>
|
|
</tr>
|
|
</thead>
|
|
</template>
|
|
<template #skeleton-row>
|
|
<td colspan="2"><tt-skeleton /></td>
|
|
</template>
|
|
<template #row="{ item }">
|
|
<td>
|
|
<a class="link" target="_blank"
|
|
:href="'http://radius.xinon.at/edit_user.php?user=' + item.Username"
|
|
data-tooltip="User in Radius öffnen"
|
|
data-tooltip-align="right">{{ item.Username }}</a>
|
|
</td>
|
|
<td class="clamp-2 mono">{{ item.Info }}</td>
|
|
</template>
|
|
</tt-data-table>
|
|
<div v-if="!loadingNat && filteredNat.length" class="results-summary">
|
|
{{ filteredNat.length }} Treffer gefunden
|
|
</div>
|
|
</div>
|
|
<div class="free-users-column">
|
|
<div class="h5" style="display:flex;align-items:center;justify-content:space-between;gap:10px;">
|
|
<span><i class="fa-duotone fa-id-card-clip"></i> Freie STF Benutzer <span class="badge">{{ filteredStf.length }}</span></span>
|
|
<button class="ghost-btn" @click="reloadStf" :disabled="loadingStf">
|
|
<span v-if="!loadingStf"><i class="fa-duotone fa-rotate-right"></i> Neu laden</span>
|
|
<span v-else class="btn-loader"></span>
|
|
</button>
|
|
</div>
|
|
<tt-data-table
|
|
:items="filteredStf"
|
|
:is-loading="loadingStf"
|
|
:has-searched="true"
|
|
density="ultra-compact"
|
|
table-class="no-min-width"
|
|
no-results-placeholder-text="Keine Treffer"
|
|
:skeleton-row-count="8"
|
|
>
|
|
<template #head>
|
|
<thead>
|
|
<tr>
|
|
<th>Username</th>
|
|
<th>Info</th>
|
|
</tr>
|
|
</thead>
|
|
</template>
|
|
<template #skeleton-row>
|
|
<td colspan="2"><tt-skeleton /></td>
|
|
</template>
|
|
<template #row="{ item }">
|
|
<td>
|
|
<a class="link" target="_blank"
|
|
:href="'http://radius.xinon.at/edit_user.php?user=' + item.Username"
|
|
data-tooltip="User in Radius öffnen"
|
|
data-tooltip-align="right">{{ item.Username }}</a>
|
|
</td>
|
|
<td class="clamp-2 mono">{{ item.Info }}</td>
|
|
</template>
|
|
</tt-data-table>
|
|
<div v-if="!loadingStf && filteredStf.length" class="results-summary">
|
|
{{ filteredStf.length }} Treffer gefunden
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
data: () => ({
|
|
nat: [],
|
|
stf: [],
|
|
loadingNat: false,
|
|
loadingStf: false,
|
|
_initialized: false
|
|
}),
|
|
computed: {
|
|
filteredNat() {
|
|
return this.nat.filter(this.isTrulyFree);
|
|
},
|
|
filteredStf() {
|
|
return this.stf.filter(this.isTrulyFree);
|
|
}
|
|
},
|
|
methods: {
|
|
initIfNeeded() {
|
|
if (this._initialized) return;
|
|
this._initialized = true;
|
|
this.reloadNat();
|
|
this.reloadStf();
|
|
},
|
|
isTrulyFree(user) {
|
|
return !/frei[a-z]/.test((user.Info || '').toLowerCase());
|
|
},
|
|
normalizeUsers(arr) {
|
|
if (!Array.isArray(arr)) return [];
|
|
return arr.map(u => ({
|
|
Username: (u.Username || u.username || '').trim(),
|
|
Info: (u.Info || u.info || '').toString().replace(/\s+$/, '')
|
|
})).filter(u => u.Username);
|
|
},
|
|
async reloadNat() {
|
|
this.nat = [];
|
|
this.loadingNat = true;
|
|
try {
|
|
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/Radius/proxyUnsecureHTTPRequestToRadius`, {
|
|
params: { action2: 'free_user', filter: 'nat' }
|
|
});
|
|
this.nat = this.normalizeUsers(data?.users || []);
|
|
} catch (error) {
|
|
this.nat = [];
|
|
}
|
|
this.loadingNat = false;
|
|
},
|
|
async reloadStf() {
|
|
this.stf = [];
|
|
this.loadingStf = true;
|
|
try {
|
|
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/Radius/proxyUnsecureHTTPRequestToRadius`, {
|
|
params: { action2: 'free_user', filter: 'stf' }
|
|
});
|
|
this.stf = this.normalizeUsers(data?.users || []);
|
|
} catch (error) {
|
|
this.stf = [];
|
|
}
|
|
this.loadingStf = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Register component with Vue 3 app
|
|
if (window.VueApp) {
|
|
window.VueApp.component('radius-free-users', RadiusFreeUsers);
|
|
}
|