|
@@ -456,6 +456,11 @@
|
|
|
</td>
|
|
</td>
|
|
|
<td class="p-4 text-right">
|
|
<td class="p-4 text-right">
|
|
|
<div class="flex items-center justify-end gap-2">
|
|
<div class="flex items-center justify-end gap-2">
|
|
|
|
|
+ <button @click="handleResetPassword(u.id)"
|
|
|
|
|
+ class="p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-primary transition-all"
|
|
|
|
|
+ :title="t('admin.actions.resetPassword')">
|
|
|
|
|
+ <Key class="w-4 h-4" />
|
|
|
|
|
+ </button>
|
|
|
<button @click="handleUpdateUserRole(u.id, u.role === 'admin' ? 'user' : 'admin')"
|
|
<button @click="handleUpdateUserRole(u.id, u.role === 'admin' ? 'user' : 'admin')"
|
|
|
class="p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-primary transition-all"
|
|
class="p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-primary transition-all"
|
|
|
:title="t('admin.actions.toggleAdminRole')">
|
|
:title="t('admin.actions.toggleAdminRole')">
|
|
@@ -910,7 +915,7 @@ import { RouterLink, useRouter, useRoute } from "vue-router";
|
|
|
import { useI18n } from "vue-i18n";
|
|
import { useI18n } from "vue-i18n";
|
|
|
import { loadAdminTranslations } from "@/i18n";
|
|
import { loadAdminTranslations } from "@/i18n";
|
|
|
import { toast } from "vue-sonner";
|
|
import { toast } from "vue-sonner";
|
|
|
-import { Package, Clock, CheckCircle2, Truck, XCircle, AlertCircle, FileText, ExternalLink, ShieldCheck, Eye, RefreshCw, Search, Filter, Layers, Settings, Plus, Edit2, Trash2, ToggleLeft, ToggleRight, Database, Image as ImageIcon, EyeOff, Hash, MessageCircle, FileBox, Download, Newspaper, History, X, Users, Save } from "lucide-vue-next";
|
|
|
|
|
|
|
+import { Package, Clock, CheckCircle2, Truck, XCircle, AlertCircle, FileText, ExternalLink, ShieldCheck, Eye, RefreshCw, Search, Filter, Layers, Settings, Plus, Edit2, Trash2, ToggleLeft, ToggleRight, Database, Image as ImageIcon, EyeOff, Hash, MessageCircle, FileBox, Download, Newspaper, History, X, Users, Save, Key } from "lucide-vue-next";
|
|
|
import Button from "@/components/ui/button.vue";
|
|
import Button from "@/components/ui/button.vue";
|
|
|
import Header from "@/components/Header.vue";
|
|
import Header from "@/components/Header.vue";
|
|
|
import Footer from "@/components/Footer.vue";
|
|
import Footer from "@/components/Footer.vue";
|
|
@@ -1148,6 +1153,21 @@ watch(auditPage, () => {
|
|
|
if (activeTab.value === 'audit') fetchAuditLogs();
|
|
if (activeTab.value === 'audit') fetchAuditLogs();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+async function handleResetPassword(userId: number) {
|
|
|
|
|
+ const newPass = window.prompt(t("admin.questions.enterNewPassword"));
|
|
|
|
|
+ if (!newPass) return;
|
|
|
|
|
+ if (newPass.length < 6) {
|
|
|
|
|
+ toast.error(t("auth.error.passwordTooShort"));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ await adminUpdateUser(userId, { password: newPass });
|
|
|
|
|
+ toast.success(t("admin.toasts.passwordUpdated"));
|
|
|
|
|
+ } catch (err: any) {
|
|
|
|
|
+ toast.error(err.message || t("admin.toasts.genericError"));
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
watch(activeTab, fetchData, { immediate: false });
|
|
watch(activeTab, fetchData, { immediate: false });
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
if (!authStore.user || authStore.user.role !== "admin") { router.push("/auth"); return; }
|
|
if (!authStore.user || authStore.user.role !== "admin") { router.push("/auth"); return; }
|