Files
service-finder/frontend_admin/.output/server/chunks/build/index-DP7v0fI5.mjs
2026-07-01 02:27:38 +00:00

502 lines
38 KiB
JavaScript

import { _ as __nuxt_component_0 } from './nuxt-link-1NBbHb-h.mjs';
import { defineComponent, computed, ref, watch, withCtx, createTextVNode, toDisplayString, mergeProps, useSSRContext } from 'vue';
import { ssrRenderAttrs, ssrInterpolate, ssrRenderAttr, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderComponent, ssrRenderList, ssrRenderClass } from 'vue/server-renderer';
import { useRouter } from 'vue-router';
import { c as useAuthStore, d as useCookie } from './server.mjs';
import '../nitro/nitro.mjs';
import 'node:http';
import 'node:https';
import 'node:events';
import 'node:buffer';
import 'node:fs';
import 'node:path';
import 'node:crypto';
import 'node:url';
import 'pinia';
import '../routes/renderer.mjs';
import 'vue-bundle-renderer/runtime';
import 'unhead/server';
import 'devalue';
import 'unhead/utils';
import 'unhead/plugins';
const svgWidth = 800;
const svgHeight = 220;
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
__name: "UserTrendChart",
__ssrInlineRender: true,
props: {
data: {},
loading: { type: Boolean }
},
setup(__props) {
const props = __props;
const selectedPeriod = ref("30");
const hoveredBar = ref(null);
const padding = { top: 10, right: 16, bottom: 30, left: 36 };
const filteredData = computed(() => {
const days = parseInt(selectedPeriod.value, 10);
if (!props.data || props.data.length === 0) return [];
const sorted = [...props.data].sort(
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
);
const cutoff = /* @__PURE__ */ new Date();
cutoff.setDate(cutoff.getDate() - days);
return sorted.filter((d) => new Date(d.date) >= cutoff);
});
const maxCount = computed(() => {
if (filteredData.value.length === 0) return 1;
return Math.max(...filteredData.value.map((d) => d.count), 1);
});
const chartHeight = svgHeight - padding.top - padding.bottom;
const gridLines = computed(() => {
const lines = [];
for (let i = 0; i <= 4; i++) {
lines.push(padding.top + chartHeight / 4 * i);
}
return lines;
});
const yLabels = computed(() => {
const labels = [];
for (let i = 0; i <= 4; i++) {
const y = padding.top + chartHeight / 4 * i;
const value = Math.round(maxCount.value - maxCount.value / 4 * i);
labels.push({ y, text: value.toString() });
}
return labels;
});
const barWidth = computed(() => {
const count = filteredData.value.length;
if (count === 0) return 0;
const availableWidth = svgWidth - padding.left - padding.right;
const maxBarWidth = 32;
const calculatedWidth = availableWidth / count * 0.7;
return Math.min(calculatedWidth, maxBarWidth);
});
const visibleBars = computed(() => {
const count = filteredData.value.length;
if (count === 0) return [];
const availableWidth = svgWidth - padding.left - padding.right;
const gap = count > 1 ? (availableWidth - barWidth.value * count) / (count - 1) : 0;
return filteredData.value.map((item, idx) => {
const x = padding.left + idx * (barWidth.value + gap);
const barHeight = maxCount.value > 0 ? item.count / maxCount.value * chartHeight : 0;
const y = svgHeight - padding.bottom - barHeight;
const ratio = maxCount.value > 0 ? item.count / maxCount.value : 0;
let color;
if (ratio > 0.8) color = "#6366f1";
else if (ratio > 0.5) color = "#818cf8";
else if (ratio > 0.3) color = "#a5b4fc";
else color = "#c7d2fe";
const tooltipText = `${item.count} ${formatDate(item.date)}`;
const tooltipWidth = tooltipText.length * 7 + 16;
let tooltipX = x + barWidth.value / 2 - tooltipWidth / 2;
if (tooltipX < padding.left) tooltipX = padding.left;
if (tooltipX + tooltipWidth > svgWidth - padding.right) tooltipX = svgWidth - padding.right - tooltipWidth;
const tooltipY = y - 4;
return {
x,
y,
height: barHeight,
color,
tooltipX,
tooltipY,
tooltipWidth,
tooltipText
};
});
});
const xLabels = computed(() => {
const count = filteredData.value.length;
if (count === 0) return [];
const availableWidth = svgWidth - padding.left - padding.right;
const gap = count > 1 ? (availableWidth - barWidth.value * count) / (count - 1) : 0;
let interval = 1;
if (count > 14) interval = 2;
if (count > 30) interval = 5;
if (count > 60) interval = 10;
return filteredData.value.map((item, idx) => {
const x = padding.left + idx * (barWidth.value + gap);
const d = new Date(item.date);
const label = `${d.getMonth() + 1}/${d.getDate()}`;
return { x, label, idx };
}).filter((_, i) => i % interval === 0 || i === count - 1);
});
const averageCount = computed(() => {
if (filteredData.value.length === 0) return 0;
const sum = filteredData.value.reduce((acc, d) => acc + d.count, 0);
return Math.round(sum / filteredData.value.length);
});
const totalCount = computed(() => {
return filteredData.value.reduce((acc, d) => acc + d.count, 0);
});
const trendDirection = computed(() => {
const data = filteredData.value;
if (data.length < 4) return "stable";
const half = Math.floor(data.length / 2);
const firstHalf = data.slice(0, half).reduce((acc, d) => acc + d.count, 0) / half;
const secondHalf = data.slice(half).reduce((acc, d) => acc + d.count, 0) / (data.length - half);
if (secondHalf > firstHalf * 1.05) return "up";
if (secondHalf < firstHalf * 0.95) return "down";
return "stable";
});
const trendPercent = computed(() => {
const data = filteredData.value;
if (data.length < 4) return 0;
const half = Math.floor(data.length / 2);
const firstHalf = data.slice(0, half).reduce((acc, d) => acc + d.count, 0) / half;
if (firstHalf === 0) return 100;
const secondHalf = data.slice(half).reduce((acc, d) => acc + d.count, 0) / (data.length - half);
return Math.round(Math.abs((secondHalf - firstHalf) / firstHalf) * 100);
});
function formatDate(dateStr) {
try {
const d = new Date(dateStr);
return `${d.getMonth() + 1}/${d.getDate()}`;
} catch {
return dateStr;
}
}
function updateChart() {
hoveredBar.value = null;
}
watch(() => props.data, () => {
updateChart();
});
return (_ctx, _push, _parent, _attrs) => {
_push(`<div${ssrRenderAttrs(mergeProps({ class: "bg-slate-800 rounded-xl border border-slate-700 p-5" }, _attrs))}><div class="flex items-center justify-between mb-4"><div><h3 class="text-sm font-semibold text-white">${ssrInterpolate(_ctx.$t("users.trend_title"))}</h3><p class="text-xs text-slate-400 mt-0.5">${ssrInterpolate(_ctx.$t("users.trend_subtitle"))}</p></div><select class="px-2.5 py-1.5 text-xs bg-slate-700 border border-slate-600 rounded-lg text-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition"><option value="7"${ssrIncludeBooleanAttr(Array.isArray(selectedPeriod.value) ? ssrLooseContain(selectedPeriod.value, "7") : ssrLooseEqual(selectedPeriod.value, "7")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.trend_7d"))}</option><option value="14"${ssrIncludeBooleanAttr(Array.isArray(selectedPeriod.value) ? ssrLooseContain(selectedPeriod.value, "14") : ssrLooseEqual(selectedPeriod.value, "14")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.trend_14d"))}</option><option value="30"${ssrIncludeBooleanAttr(Array.isArray(selectedPeriod.value) ? ssrLooseContain(selectedPeriod.value, "30") : ssrLooseEqual(selectedPeriod.value, "30")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.trend_30d"))}</option><option value="90"${ssrIncludeBooleanAttr(Array.isArray(selectedPeriod.value) ? ssrLooseContain(selectedPeriod.value, "90") : ssrLooseEqual(selectedPeriod.value, "90")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.trend_90d"))}</option></select></div>`);
if (__props.loading) {
_push(`<div class="flex items-center justify-center py-12"><svg class="w-6 h-6 text-indigo-400 animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg></div>`);
} else if (filteredData.value.length === 0) {
_push(`<div class="flex flex-col items-center justify-center py-12 text-center"><svg class="w-10 h-10 text-slate-600 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg><p class="text-slate-500 text-xs">${ssrInterpolate(_ctx.$t("users.trend_no_data"))}</p></div>`);
} else {
_push(`<div class="relative"><svg${ssrRenderAttr("viewBox", `0 0 ${svgWidth} ${svgHeight}`)} class="w-full h-auto overflow-visible" preserveAspectRatio="xMidYMid meet"><!--[-->`);
ssrRenderList(gridLines.value, (y, yi) => {
_push(`<line${ssrRenderAttr("x1", padding.left)}${ssrRenderAttr("y1", y)}${ssrRenderAttr("x2", svgWidth - padding.right)}${ssrRenderAttr("y2", y)} stroke="currentColor" class="text-slate-700/50" stroke-width="1" stroke-dasharray="4,4"></line>`);
});
_push(`<!--]--><!--[-->`);
ssrRenderList(yLabels.value, (label, li) => {
_push(`<text${ssrRenderAttr("x", padding.left - 8)}${ssrRenderAttr("y", label.y + 4)} text-anchor="end" class="fill-slate-500" font-size="10">${ssrInterpolate(label.text)}</text>`);
});
_push(`<!--]--><!--[-->`);
ssrRenderList(visibleBars.value, (item, idx) => {
_push(`<g class="chart-bar-group"><rect${ssrRenderAttr("x", item.x)}${ssrRenderAttr("y", item.y)}${ssrRenderAttr("width", barWidth.value)}${ssrRenderAttr("height", item.height)}${ssrRenderAttr("rx", 3)}${ssrRenderAttr("ry", 3)}${ssrRenderAttr("fill", item.color)}${ssrRenderAttr("opacity", hoveredBar.value === idx ? 0.9 : 0.7)} class="transition-all duration-200 cursor-pointer"></rect>`);
if (hoveredBar.value === idx) {
_push(`<g><rect${ssrRenderAttr("x", item.tooltipX)}${ssrRenderAttr("y", item.tooltipY - 28)}${ssrRenderAttr("width", item.tooltipWidth)} height="22" rx="4" fill="#1e293b" stroke="#475569" stroke-width="1"></rect><text${ssrRenderAttr("x", item.tooltipX + item.tooltipWidth / 2)}${ssrRenderAttr("y", item.tooltipY - 13)} text-anchor="middle" fill="#e2e8f0" font-size="11" font-weight="600">${ssrInterpolate(item.tooltipText)}</text></g>`);
} else {
_push(`<!---->`);
}
_push(`</g>`);
});
_push(`<!--]--><!--[-->`);
ssrRenderList(xLabels.value, (item, xi) => {
_push(`<text${ssrRenderAttr("x", item.x + barWidth.value / 2)}${ssrRenderAttr("y", svgHeight - padding.bottom + 16)} text-anchor="middle" class="fill-slate-500" font-size="9">${ssrInterpolate(item.label)}</text>`);
});
_push(`<!--]--></svg><div class="flex items-center justify-between mt-4 pt-3 border-t border-slate-700/50"><div class="flex items-center gap-4 text-xs"><div class="flex items-center gap-1.5"><span class="w-2.5 h-2.5 rounded-sm bg-indigo-500/70"></span><span class="text-slate-400">${ssrInterpolate(_ctx.$t("users.trend_daily"))}</span></div><div><span class="text-slate-400">${ssrInterpolate(_ctx.$t("users.trend_avg"))}:</span><span class="text-white font-semibold ml-1">${ssrInterpolate(averageCount.value)}</span></div><div><span class="text-slate-400">${ssrInterpolate(_ctx.$t("users.trend_total"))}:</span><span class="text-white font-semibold ml-1">${ssrInterpolate(totalCount.value)}</span></div></div>`);
if (trendDirection.value !== "stable") {
_push(`<div class="flex items-center gap-1 text-xs">`);
if (trendDirection.value === "up") {
_push(`<svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg>`);
} else {
_push(`<svg class="w-3.5 h-3.5 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 17h8m0 0V9m0 8l-8-8-4 4-6-6"></path></svg>`);
}
_push(`<span class="${ssrRenderClass([trendDirection.value === "up" ? "text-emerald-400" : "text-red-400", "font-medium"])}">${ssrInterpolate(trendPercent.value)}% </span><span class="text-slate-500">${ssrInterpolate(_ctx.$t("users.trend_vs_prev"))}</span></div>`);
} else {
_push(`<!---->`);
}
_push(`</div></div>`);
}
_push(`</div>`);
};
}
});
const _sfc_setup$1 = _sfc_main$1.setup;
_sfc_main$1.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/charts/UserTrendChart.vue");
return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
};
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "index",
__ssrInlineRender: true,
setup(__props) {
const router = useRouter();
const auth = useAuthStore();
const canViewUsers = computed(() => {
var _a, _b;
if (auth.isAdmin) return true;
return !!((_b = (_a = auth.user) == null ? void 0 : _a.system_capabilities) == null ? void 0 : _b["users:view"]);
});
const canEditUsers = computed(() => {
var _a, _b;
if (auth.isAdmin) return true;
return !!((_b = (_a = auth.user) == null ? void 0 : _a.system_capabilities) == null ? void 0 : _b["users:edit"]);
});
computed(() => {
var _a, _b;
if (auth.isAdmin) return true;
return !!((_b = (_a = auth.user) == null ? void 0 : _a.system_capabilities) == null ? void 0 : _b["users:ban"]);
});
computed(() => {
var _a, _b;
if (auth.isAdmin) return true;
return !!((_b = (_a = auth.user) == null ? void 0 : _a.system_capabilities) == null ? void 0 : _b["users:delete"]);
});
if (!canViewUsers.value && !auth.isAdmin) {
router.push("/");
}
const loading = ref(true);
const error = ref(null);
const users = ref([]);
const totalCount = ref(0);
const skip = ref(0);
const limit = ref(50);
const searchQuery = ref("");
const statusFilter = ref("all");
const roleFilter = ref("all");
const planFilter = ref("all");
const stats = ref({
total_users: 0,
active_users: 0,
deleted_users: 0,
banned_users: 0,
new_users_today: 0,
new_users_this_week: 0,
new_users_this_month: 0,
users_by_role: {},
users_by_plan: {},
users_by_language: {},
users_with_person: 0,
users_without_person: 0,
registration_trend: [],
active_organizations_count: 0,
total_memberships: 0
});
const selectedUserIds = ref([]);
const notification = ref(null);
let searchDebounceTimer = null;
function debouncedFetchUsers() {
if (searchDebounceTimer) {
clearTimeout(searchDebounceTimer);
}
searchDebounceTimer = setTimeout(() => {
skip.value = 0;
fetchUsers();
}, 400);
}
watch(searchQuery, () => {
debouncedFetchUsers();
});
watch(statusFilter, () => {
skip.value = 0;
fetchUsers();
});
watch(roleFilter, () => {
skip.value = 0;
fetchUsers();
});
watch(planFilter, () => {
skip.value = 0;
fetchUsers();
});
const isFilterActive = computed(() => {
return searchQuery.value !== "" || statusFilter.value !== "all" || roleFilter.value !== "all" || planFilter.value !== "all";
});
const allSelected = computed(() => {
return users.value.length > 0 && selectedUserIds.value.length === users.value.length;
});
function getAuthHeaders() {
const tokenCookie = useCookie("access_token");
const token = tokenCookie.value;
return token ? { Authorization: `Bearer ${token}` } : {};
}
async function fetchUsers() {
var _a;
loading.value = true;
error.value = null;
try {
const params = { skip: skip.value, limit: limit.value };
if (searchQuery.value) {
params.search_term = searchQuery.value;
}
if (statusFilter.value !== "all") {
switch (statusFilter.value) {
case "active":
params.is_active = true;
break;
case "inactive":
params.is_active = false;
break;
case "deleted":
params.is_deleted = true;
break;
case "banned":
params.is_active = false;
break;
}
}
if (roleFilter.value !== "all") {
params.role = roleFilter.value;
}
if (planFilter.value !== "all") {
params.plan_filter = planFilter.value;
}
const response = await $fetch("/api/v1/admin/users", {
params,
headers: getAuthHeaders()
});
users.value = response.users;
totalCount.value = response.total;
} catch (err) {
console.error("[Users] Failed to fetch users:", err);
error.value = ((_a = err == null ? void 0 : err.data) == null ? void 0 : _a.detail) || (err == null ? void 0 : err.message) || "Failed to load users";
} finally {
loading.value = false;
}
}
function roleBadgeClass(role) {
const r = role.toLowerCase();
if (r === "superadmin") return "bg-purple-500/20 text-purple-300";
if (r === "admin") return "bg-indigo-500/20 text-indigo-300";
if (r === "staff") return "bg-cyan-500/20 text-cyan-300";
return "bg-slate-500/20 text-slate-300";
}
function roleLabel(role) {
const r = role.toLowerCase();
if (r === "superadmin") return "Superadmin";
if (r === "admin") return "Admin";
if (r === "staff") return "Staff";
return "User";
}
function statusBadgeClass(user) {
if (user.is_deleted) return "bg-red-500/10 text-red-400";
if (!user.is_active) return "bg-amber-500/10 text-amber-400";
return "bg-emerald-500/10 text-emerald-400";
}
function statusDotClass(user) {
if (user.is_deleted) return "bg-red-400";
if (!user.is_active) return "bg-amber-400";
return "bg-emerald-400";
}
function statusLabel(user) {
if (user.is_deleted) return "Deleted";
if (!user.is_active) return "Inactive";
return "Active";
}
function planBadgeClass(plan) {
const p = (plan || "").toLowerCase();
if (p.includes("enterprise") || p.includes("vip")) return "bg-purple-500/20 text-purple-300";
if (p.includes("premium") || p.includes("pro")) return "bg-amber-500/20 text-amber-300";
return "bg-slate-500/20 text-slate-300";
}
function formatDate(dateStr) {
if (!dateStr) return "\u2014";
try {
const d = new Date(dateStr);
return d.toLocaleDateString("hu-HU", {
year: "numeric",
month: "short",
day: "numeric"
});
} catch {
return dateStr;
}
}
return (_ctx, _push, _parent, _attrs) => {
const _component_NuxtLink = __nuxt_component_0;
_push(`<div${ssrRenderAttrs(_attrs)}><div class="mb-8"><h1 class="text-2xl font-bold text-white">${ssrInterpolate(_ctx.$t("users.title"))}</h1><p class="text-slate-400 mt-1">${ssrInterpolate(_ctx.$t("users.subtitle"))}</p></div><div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-6"><div class="relative flex-1 max-w-md"><svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg><input${ssrRenderAttr("value", searchQuery.value)} type="text"${ssrRenderAttr("placeholder", _ctx.$t("common.search_placeholder"))} class="w-full pl-10 pr-4 py-2.5 bg-slate-800 border border-slate-700 rounded-lg text-sm text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500 transition"></div><div class="flex items-center gap-2 flex-wrap"><select class="px-3 py-2.5 bg-slate-800 border border-slate-700 rounded-lg text-sm text-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition"><option value="all"${ssrIncludeBooleanAttr(Array.isArray(statusFilter.value) ? ssrLooseContain(statusFilter.value, "all") : ssrLooseEqual(statusFilter.value, "all")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.all_statuses"))}</option><option value="active"${ssrIncludeBooleanAttr(Array.isArray(statusFilter.value) ? ssrLooseContain(statusFilter.value, "active") : ssrLooseEqual(statusFilter.value, "active")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.status_active"))}</option><option value="inactive"${ssrIncludeBooleanAttr(Array.isArray(statusFilter.value) ? ssrLooseContain(statusFilter.value, "inactive") : ssrLooseEqual(statusFilter.value, "inactive")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.status_inactive"))}</option><option value="deleted"${ssrIncludeBooleanAttr(Array.isArray(statusFilter.value) ? ssrLooseContain(statusFilter.value, "deleted") : ssrLooseEqual(statusFilter.value, "deleted")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.status_deleted"))}</option><option value="banned"${ssrIncludeBooleanAttr(Array.isArray(statusFilter.value) ? ssrLooseContain(statusFilter.value, "banned") : ssrLooseEqual(statusFilter.value, "banned")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.status_banned"))}</option></select><select class="px-3 py-2.5 bg-slate-800 border border-slate-700 rounded-lg text-sm text-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition"><option value="all"${ssrIncludeBooleanAttr(Array.isArray(roleFilter.value) ? ssrLooseContain(roleFilter.value, "all") : ssrLooseEqual(roleFilter.value, "all")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.all_roles"))}</option><option value="user"${ssrIncludeBooleanAttr(Array.isArray(roleFilter.value) ? ssrLooseContain(roleFilter.value, "user") : ssrLooseEqual(roleFilter.value, "user")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.role_user"))}</option><option value="admin"${ssrIncludeBooleanAttr(Array.isArray(roleFilter.value) ? ssrLooseContain(roleFilter.value, "admin") : ssrLooseEqual(roleFilter.value, "admin")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.role_admin"))}</option><option value="staff"${ssrIncludeBooleanAttr(Array.isArray(roleFilter.value) ? ssrLooseContain(roleFilter.value, "staff") : ssrLooseEqual(roleFilter.value, "staff")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.role_staff"))}</option><option value="superadmin"${ssrIncludeBooleanAttr(Array.isArray(roleFilter.value) ? ssrLooseContain(roleFilter.value, "superadmin") : ssrLooseEqual(roleFilter.value, "superadmin")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.role_superadmin"))}</option></select><select class="px-3 py-2.5 bg-slate-800 border border-slate-700 rounded-lg text-sm text-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition"><option value="all"${ssrIncludeBooleanAttr(Array.isArray(planFilter.value) ? ssrLooseContain(planFilter.value, "all") : ssrLooseEqual(planFilter.value, "all")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.all_plans"))}</option><option value="FREE"${ssrIncludeBooleanAttr(Array.isArray(planFilter.value) ? ssrLooseContain(planFilter.value, "FREE") : ssrLooseEqual(planFilter.value, "FREE")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.plan_free"))}</option><option value="PREMIUM"${ssrIncludeBooleanAttr(Array.isArray(planFilter.value) ? ssrLooseContain(planFilter.value, "PREMIUM") : ssrLooseEqual(planFilter.value, "PREMIUM")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.plan_premium"))}</option><option value="ENTERPRISE"${ssrIncludeBooleanAttr(Array.isArray(planFilter.value) ? ssrLooseContain(planFilter.value, "ENTERPRISE") : ssrLooseEqual(planFilter.value, "ENTERPRISE")) ? " selected" : ""}>${ssrInterpolate(_ctx.$t("users.plan_enterprise"))}</option></select></div></div>`);
if (loading.value) {
_push(`<div class="flex items-center justify-center py-20"><div class="flex flex-col items-center gap-3"><svg class="w-8 h-8 text-indigo-400 animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg><p class="text-slate-400 text-sm">${ssrInterpolate(_ctx.$t("common.loading"))}</p></div></div>`);
} else if (error.value) {
_push(`<div class="flex flex-col items-center justify-center py-20 text-center"><svg class="w-12 h-12 text-red-500 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path></svg><p class="text-red-400 text-sm mb-2">${ssrInterpolate(error.value)}</p><button class="px-4 py-2 text-sm bg-indigo-600/20 text-indigo-300 hover:bg-indigo-600/30 rounded-lg transition">${ssrInterpolate(_ctx.$t("common.retry"))}</button></div>`);
} else {
_push(`<!--[--><div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4 mb-8"><div class="bg-slate-800 rounded-xl border border-slate-700 p-4"><div class="flex items-center gap-3"><div class="p-2 rounded-lg bg-indigo-500/10 text-indigo-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path></svg></div><div><p class="text-xs text-slate-400 uppercase tracking-wider">${ssrInterpolate(_ctx.$t("users.total_users"))}</p><p class="text-2xl font-bold text-white">${ssrInterpolate(stats.value.total_users)}</p></div></div></div><div class="bg-slate-800 rounded-xl border border-slate-700 p-4"><div class="flex items-center gap-3"><div class="p-2 rounded-lg bg-emerald-500/10 text-emerald-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg></div><div><p class="text-xs text-slate-400 uppercase tracking-wider">${ssrInterpolate(_ctx.$t("users.active_users"))}</p><p class="text-2xl font-bold text-emerald-400">${ssrInterpolate(stats.value.active_users)}</p></div></div></div><div class="bg-slate-800 rounded-xl border border-slate-700 p-4"><div class="flex items-center gap-3"><div class="p-2 rounded-lg bg-red-500/10 text-red-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg></div><div><p class="text-xs text-slate-400 uppercase tracking-wider">${ssrInterpolate(_ctx.$t("users.deleted_users"))}</p><p class="text-2xl font-bold text-red-400">${ssrInterpolate(stats.value.deleted_users)}</p></div></div></div><div class="bg-slate-800 rounded-xl border border-slate-700 p-4"><div class="flex items-center gap-3"><div class="p-2 rounded-lg bg-amber-500/10 text-amber-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"></path></svg></div><div><p class="text-xs text-slate-400 uppercase tracking-wider">${ssrInterpolate(_ctx.$t("users.banned_users"))}</p><p class="text-2xl font-bold text-amber-400">${ssrInterpolate(stats.value.banned_users)}</p></div></div></div><div class="bg-slate-800 rounded-xl border border-slate-700 p-4"><div class="flex items-center gap-3"><div class="p-2 rounded-lg bg-cyan-500/10 text-cyan-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg></div><div><p class="text-xs text-slate-400 uppercase tracking-wider">${ssrInterpolate(_ctx.$t("users.new_today"))}</p><p class="text-2xl font-bold text-cyan-400">${ssrInterpolate(stats.value.new_users_today)}</p></div></div></div></div><div class="mb-8">`);
_push(ssrRenderComponent(_sfc_main$1, {
data: stats.value.registration_trend,
loading: loading.value
}, null, _parent));
_push(`</div>`);
if (isFilterActive.value) {
_push(`<div class="mb-4 text-xs text-slate-500">${ssrInterpolate(_ctx.$t("users.filtered_results"))}: <span class="text-slate-300 font-semibold">${ssrInterpolate(users.value.length)}</span> / ${ssrInterpolate(totalCount.value)} db </div>`);
} else {
_push(`<!---->`);
}
if (selectedUserIds.value.length > 0 && canEditUsers.value) {
_push(`<div class="mb-4 px-4 py-3 bg-indigo-900/30 border border-indigo-700/50 rounded-xl flex items-center justify-between"><p class="text-sm text-indigo-300">${ssrInterpolate(_ctx.$t("users.selected_count", { count: selectedUserIds.value.length }))}</p><div class="flex items-center gap-2"><button class="px-3 py-1.5 text-xs font-medium bg-emerald-600/20 text-emerald-300 hover:bg-emerald-600/30 rounded-lg transition">${ssrInterpolate(_ctx.$t("users.bulk_activate"))}</button><button class="px-3 py-1.5 text-xs font-medium bg-red-600/20 text-red-300 hover:bg-red-600/30 rounded-lg transition">${ssrInterpolate(_ctx.$t("users.bulk_deactivate"))}</button><button class="px-3 py-1.5 text-xs font-medium bg-slate-600/20 text-slate-300 hover:bg-slate-600/30 rounded-lg transition">${ssrInterpolate(_ctx.$t("users.clear_selection"))}</button></div></div>`);
} else {
_push(`<!---->`);
}
_push(`<div class="bg-slate-800/50 border border-slate-700 rounded-xl overflow-hidden"><div class="overflow-x-auto"><table class="w-full text-sm"><thead><tr class="border-b border-slate-700 bg-slate-800/80"><th class="text-left py-3.5 px-4 w-10"><input type="checkbox"${ssrIncludeBooleanAttr(allSelected.value) ? " checked" : ""} class="rounded border-slate-600 bg-slate-700 text-indigo-600 focus:ring-indigo-500/50"></th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">ID</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("common.email"))}</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("common.name"))}</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("users.role"))}</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("common.status"))}</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("users.package"))}</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("users.registration"))}</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("users.language"))}</th><th class="text-left py-3.5 px-4 text-xs font-semibold uppercase tracking-wider text-slate-400">${ssrInterpolate(_ctx.$t("common.actions"))}</th></tr></thead><tbody><!--[-->`);
ssrRenderList(users.value, (user) => {
_push(`<tr class="border-b border-slate-700/50 hover:bg-slate-700/30 transition"><td class="py-3.5 px-4"><input type="checkbox"${ssrIncludeBooleanAttr(selectedUserIds.value.includes(user.id)) ? " checked" : ""} class="rounded border-slate-600 bg-slate-700 text-indigo-600 focus:ring-indigo-500/50"></td><td class="py-3.5 px-4 text-slate-400 font-mono text-xs">${ssrInterpolate(user.id)}</td><td class="py-3.5 px-4">`);
_push(ssrRenderComponent(_component_NuxtLink, {
to: "/users/" + user.id,
class: "text-sm font-medium text-white hover:underline cursor-pointer hover:text-indigo-400 transition-colors"
}, {
default: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
_push2(`${ssrInterpolate(user.email)}`);
} else {
return [
createTextVNode(toDisplayString(user.email), 1)
];
}
}),
_: 2
}, _parent));
_push(`</td><td class="py-3.5 px-4"><span class="text-slate-300">${ssrInterpolate(user.person_name || "\u2014")}</span></td><td class="py-3.5 px-4"><span class="${ssrRenderClass([roleBadgeClass(user.role), "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"])}">${ssrInterpolate(roleLabel(user.role))}</span></td><td class="py-3.5 px-4"><span class="${ssrRenderClass([statusBadgeClass(user), "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"])}"><span class="${ssrRenderClass([statusDotClass(user), "w-1.5 h-1.5 rounded-full mr-1.5"])}"></span> ${ssrInterpolate(statusLabel(user))}</span></td><td class="py-3.5 px-4"><span class="${ssrRenderClass([planBadgeClass(user.subscription_plan), "px-2.5 py-0.5 rounded-full text-xs font-medium"])}">${ssrInterpolate(user.subscription_plan || "\u2014")}</span></td><td class="py-3.5 px-4 text-slate-300 text-xs">${ssrInterpolate(formatDate(user.created_at))}</td><td class="py-3.5 px-4"><span class="text-slate-400 text-xs uppercase">${ssrInterpolate(user.preferred_language || "\u2014")}</span></td><td class="py-3.5 px-4"><div class="flex items-center gap-2">`);
_push(ssrRenderComponent(_component_NuxtLink, {
to: "/users/" + user.id,
class: "px-3 py-1.5 text-xs font-medium bg-slate-600/20 text-slate-300 hover:bg-slate-600/30 rounded-lg transition"
}, {
default: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
_push2(`${ssrInterpolate(_ctx.$t("common.view"))}`);
} else {
return [
createTextVNode(toDisplayString(_ctx.$t("common.view")), 1)
];
}
}),
_: 2
}, _parent));
if (!user.is_deleted && canEditUsers.value) {
_push(`<button class="${ssrRenderClass([user.is_active ? "bg-red-500/10 text-red-300 hover:bg-red-500/20" : "bg-emerald-500/10 text-emerald-300 hover:bg-emerald-500/20", "px-3 py-1.5 text-xs font-medium rounded-lg transition"])}">${ssrInterpolate(user.is_active ? _ctx.$t("common.deactivate") : _ctx.$t("common.activate"))}</button>`);
} else {
_push(`<!---->`);
}
_push(`</div></td></tr>`);
});
_push(`<!--]--></tbody></table></div>`);
if (users.value.length === 0 && !loading.value) {
_push(`<div class="flex flex-col items-center justify-center py-16 text-center"><svg class="w-12 h-12 text-slate-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path></svg><p class="text-slate-400 text-sm">${ssrInterpolate(_ctx.$t("common.no_results"))}</p></div>`);
} else {
_push(`<!---->`);
}
_push(`</div>`);
if (totalCount.value > limit.value) {
_push(`<div class="flex items-center justify-between mt-6"><p class="text-xs text-slate-500">${ssrInterpolate(_ctx.$t("users.showing"))} ${ssrInterpolate(skip.value + 1)}\u2013${ssrInterpolate(Math.min(skip.value + limit.value, totalCount.value))} / ${ssrInterpolate(totalCount.value)}</p><div class="flex items-center gap-2"><button${ssrIncludeBooleanAttr(skip.value === 0) ? " disabled" : ""} class="${ssrRenderClass([skip.value > 0 ? "bg-slate-700 text-slate-300 hover:bg-slate-600" : "bg-slate-800 text-slate-600", "px-3 py-1.5 text-xs font-medium rounded-lg transition disabled:opacity-30 disabled:cursor-not-allowed"])}">${ssrInterpolate(_ctx.$t("common.prev"))}</button><button${ssrIncludeBooleanAttr(skip.value + limit.value >= totalCount.value) ? " disabled" : ""} class="${ssrRenderClass([skip.value + limit.value < totalCount.value ? "bg-slate-700 text-slate-300 hover:bg-slate-600" : "bg-slate-800 text-slate-600", "px-3 py-1.5 text-xs font-medium rounded-lg transition disabled:opacity-30 disabled:cursor-not-allowed"])}">${ssrInterpolate(_ctx.$t("common.next"))}</button></div></div>`);
} else {
_push(`<!---->`);
}
_push(`<!--]-->`);
}
if (notification.value) {
_push(`<div class="${ssrRenderClass([notification.value.type === "success" ? "bg-emerald-600 text-white" : "bg-red-600 text-white", "fixed bottom-6 right-6 px-5 py-3 rounded-xl shadow-xl text-sm font-medium z-50"])}">${ssrInterpolate(notification.value.message)}</div>`);
} else {
_push(`<!---->`);
}
_push(`</div>`);
};
}
});
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("pages/users/index.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
export { _sfc_main as default };
//# sourceMappingURL=index-DP7v0fI5.mjs.map