Files
thetool/public/js/pages/Radius/RadiusFreeUsers.js
T

112 lines
5.0 KiB
JavaScript

/* ===== RadiusFreeUsers.js =====
* Vereinfachte freie Benutzer:
* - Kein Suchfeld, nur Reload-Button
* - Autoload beim ersten Tab-Klick (initIfNeeded() wird von <radius> aufgerufen)
* - Zweispaltiges Layout bei breiten Screens
* - Robust gegen Server-Response (users/count/filter), trimmt Info
*/
Vue.component('radius-free-users', {
template: `
<div class="radius-scope">
<div class="grid g-6 cols-1 cols-2-xl">
<!-- NAT -->
<div class="subcard">
<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">{{ nat.length }}</span></span>
<button class="ghost-btn" @click="reload" :disabled="loadingNat || loadingStf">
<span v-if="!loadingNat && !loadingStf"><i class="fa-duotone fa-rotate-right"></i> Neu laden</span>
<span v-else class="btn-loader"></span>
</button>
</div>
<div class="table-wrap" style="margin-top:8px;">
<table class="tt-table ultra-compact">
<thead><tr><th>Username</th><th>Info</th></tr></thead>
<tbody>
<tr v-if="loadingNat" v-for="n in 8" :key="'nats'+n"><td colspan="2"><div class="skeleton-line"></div></td></tr>
<transition-group name="rows" tag="tbody">
<tr v-for="u in nat" :key="u.Username" class="row-fade-in">
<td><a class="link" target="_blank" :href="'http://radius.xinon.at/edit_user.php?user=' + u.Username">{{ u.Username }}</a></td>
<td class="clamp-2 mono">{{ u.Info }}</td>
</tr>
</transition-group>
<tr v-if="!loadingNat && !nat.length"><td colspan="2" class="muted center p-sm">Keine Treffer</td></tr>
</tbody>
</table>
</div>
</div>
<!-- STF -->
<div class="subcard">
<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">{{ stf.length }}</span></span>
<button class="ghost-btn" @click="reload" :disabled="loadingNat || loadingStf">
<span v-if="!loadingNat && !loadingStf"><i class="fa-duotone fa-rotate-right"></i> Neu laden</span>
<span v-else class="btn-loader"></span>
</button>
</div>
<div class="table-wrap" style="margin-top:8px;">
<table class="tt-table ultra-compact">
<thead><tr><th>Username</th><th>Info</th></tr></thead>
<tbody>
<tr v-if="loadingStf" v-for="n in 8" :key="'stfs'+n"><td colspan="2"><div class="skeleton-line"></div></td></tr>
<transition-group name="rows" tag="tbody">
<tr v-for="u in stf" :key="u.Username" class="row-fade-in">
<td><a class="link" target="_blank" :href="'http://radius.xinon.at/edit_user.php?user=' + u.Username">{{ u.Username }}</a></td>
<td class="clamp-2 mono">{{ u.Info }}</td>
</tr>
</transition-group>
<tr v-if="!loadingStf && !stf.length"><td colspan="2" class="muted center p-sm">Keine Treffer</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
`,
data() {
return {
nat: [],
stf: [],
loadingNat: false,
loadingStf: false,
_initialized: false
};
},
methods: {
initIfNeeded(){
if (this._initialized) return;
this._initialized = true;
this.reload();
},
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 reload() {
// NAT
this.loadingNat = true;
try {
const natRes = await fetch(window.TT_CONFIG['BASE_PATH'] + '/Radius/proxyUnsecureHTTPRequestToRadius?action2=free_user&filter=nat');
const j = natRes.ok ? await natRes.json() : { users: [] };
this.nat = this.normalizeUsers(j.users);
} catch { this.nat = []; }
this.loadingNat = false;
// STF
this.loadingStf = true;
try {
const stfRes = await fetch(window.TT_CONFIG['BASE_PATH'] + '/Radius/proxyUnsecureHTTPRequestToRadius?action2=free_user&filter=stf');
const j = stfRes.ok ? await stfRes.json() : { users: [] };
this.stf = this.normalizeUsers(j.users);
} catch { this.stf = []; }
this.loadingStf = false;
}
}
});