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(`
${ssrInterpolate(_ctx.$t("users.trend_title"))}
${ssrInterpolate(_ctx.$t("users.trend_subtitle"))}
`);
if (__props.loading) {
_push(`
`);
} else if (filteredData.value.length === 0) {
_push(`
${ssrInterpolate(_ctx.$t("users.trend_no_data"))}
`);
} else {
_push(`
${ssrInterpolate(_ctx.$t("users.trend_daily"))}
${ssrInterpolate(_ctx.$t("users.trend_avg"))}:${ssrInterpolate(averageCount.value)}
${ssrInterpolate(_ctx.$t("users.trend_total"))}:${ssrInterpolate(totalCount.value)}
`);
if (trendDirection.value !== "stable") {
_push(`
`);
if (trendDirection.value === "up") {
_push(`
`);
} else {
_push(`
`);
}
_push(`
${ssrInterpolate(trendPercent.value)}% ${ssrInterpolate(_ctx.$t("users.trend_vs_prev"))}`);
} else {
_push(``);
}
_push(`
`);
}
_push(`
`);
};
}
});
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(`${ssrInterpolate(_ctx.$t("users.title"))}
${ssrInterpolate(_ctx.$t("users.subtitle"))}
`);
if (loading.value) {
_push(`
${ssrInterpolate(_ctx.$t("common.loading"))}
`);
} else if (error.value) {
_push(`
${ssrInterpolate(error.value)}
`);
} else {
_push(`
${ssrInterpolate(_ctx.$t("users.total_users"))}
${ssrInterpolate(stats.value.total_users)}
${ssrInterpolate(_ctx.$t("users.active_users"))}
${ssrInterpolate(stats.value.active_users)}
${ssrInterpolate(_ctx.$t("users.deleted_users"))}
${ssrInterpolate(stats.value.deleted_users)}
${ssrInterpolate(_ctx.$t("users.banned_users"))}
${ssrInterpolate(stats.value.banned_users)}
${ssrInterpolate(_ctx.$t("users.new_today"))}
${ssrInterpolate(stats.value.new_users_today)}
`);
_push(ssrRenderComponent(_sfc_main$1, {
data: stats.value.registration_trend,
loading: loading.value
}, null, _parent));
_push(`
`);
if (isFilterActive.value) {
_push(`
${ssrInterpolate(_ctx.$t("users.filtered_results"))}: ${ssrInterpolate(users.value.length)} / ${ssrInterpolate(totalCount.value)} db
`);
} else {
_push(``);
}
if (selectedUserIds.value.length > 0 && canEditUsers.value) {
_push(`
${ssrInterpolate(_ctx.$t("users.selected_count", { count: selectedUserIds.value.length }))}
`);
} else {
_push(``);
}
_push(`
| ID | ${ssrInterpolate(_ctx.$t("common.email"))} | ${ssrInterpolate(_ctx.$t("common.name"))} | ${ssrInterpolate(_ctx.$t("users.role"))} | ${ssrInterpolate(_ctx.$t("common.status"))} | ${ssrInterpolate(_ctx.$t("users.package"))} | ${ssrInterpolate(_ctx.$t("users.registration"))} | ${ssrInterpolate(_ctx.$t("users.language"))} | ${ssrInterpolate(_ctx.$t("common.actions"))} |
`);
ssrRenderList(users.value, (user) => {
_push(` | ${ssrInterpolate(user.id)} | `);
_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(` | ${ssrInterpolate(user.person_name || "\u2014")} | ${ssrInterpolate(roleLabel(user.role))} | ${ssrInterpolate(statusLabel(user))} | ${ssrInterpolate(user.subscription_plan || "\u2014")} | ${ssrInterpolate(formatDate(user.created_at))} | ${ssrInterpolate(user.preferred_language || "\u2014")} | `);
_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(``);
} else {
_push(``);
}
_push(` |
`);
});
_push(`
`);
if (users.value.length === 0 && !loading.value) {
_push(`
${ssrInterpolate(_ctx.$t("common.no_results"))}
`);
} else {
_push(``);
}
_push(`
`);
if (totalCount.value > limit.value) {
_push(`
${ssrInterpolate(_ctx.$t("users.showing"))} ${ssrInterpolate(skip.value + 1)}\u2013${ssrInterpolate(Math.min(skip.value + limit.value, totalCount.value))} / ${ssrInterpolate(totalCount.value)}
`);
} else {
_push(``);
}
_push(``);
}
if (notification.value) {
_push(`
${ssrInterpolate(notification.value.message)}
`);
} else {
_push(``);
}
_push(`
`);
};
}
});
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