diff --git a/backend/routes/admin.js b/backend/routes/admin.js index 14d2d6b..efb7e68 100644 --- a/backend/routes/admin.js +++ b/backend/routes/admin.js @@ -153,6 +153,78 @@ router.post('/admin/users', requireAdmin, async (req, res) => { } }); +// PUT /api/admin/users/:id - update a user +router.put('/admin/users/:id', requireAdmin, async (req, res) => { + try { + const id = parseInt(req.params.id, 10); + if (!Number.isFinite(id)) + return res.status(400).json({ error: 'Invalid user id' }); + + const user = await User.findByPk(id); + if (!user) return res.status(404).json({ error: 'User not found' }); + + const { email, password, name, surname, role } = req.body || {}; + + // Update email if provided + if (email !== undefined && email !== null) { + if (typeof email !== 'string' || !email.includes('@')) { + return res.status(400).json({ error: 'Invalid email' }); + } + user.email = email; + } + + // Update password if provided + if (password && password.trim() !== '') { + if (typeof password !== 'string' || password.length < 6) { + return res + .status(400) + .json({ error: 'Password must be at least 6 characters' }); + } + user.password = password; + } + + // Update name and surname - handle empty strings properly + if (name !== undefined) user.name = name || null; + if (surname !== undefined) user.surname = surname || null; + + await user.save(); + + // Update role if provided + if (role !== undefined) { + const makeAdmin = role === 'admin'; + const [userRole] = await Role.findOrCreate({ + where: { user_id: user.id }, + defaults: { user_id: user.id, is_admin: makeAdmin }, + }); + if (userRole.is_admin !== makeAdmin) { + userRole.is_admin = makeAdmin; + await userRole.save(); + } + } + + // Fetch updated role + const userRole = await Role.findOne({ where: { user_id: user.id } }); + + res.json({ + id: user.id, + email: user.email, + name: user.name, + surname: user.surname, + created_at: user.created_at, + role: userRole?.is_admin ? 'admin' : 'user', + }); + } catch (err) { + logError('Error updating user:', err); + // Unique constraint + if (err?.name === 'SequelizeUniqueConstraintError') { + return res.status(409).json({ error: 'Email already exists' }); + } + res.status(400).json({ + error: 'There was a problem updating the user.', + }); + } +}); + // DELETE /api/admin/users/:id - delete a user, prevent self-delete router.delete('/admin/users/:id', requireAdmin, async (req, res) => { try { diff --git a/frontend/components/Admin/AdminUsersPage.tsx b/frontend/components/Admin/AdminUsersPage.tsx index 8da9cc1..e274d47 100644 --- a/frontend/components/Admin/AdminUsersPage.tsx +++ b/frontend/components/Admin/AdminUsersPage.tsx @@ -1,7 +1,13 @@ -import React, { useEffect, useMemo, useState, useRef } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; -import { ChevronDownIcon, CheckIcon } from '@heroicons/react/24/outline'; +import { + ChevronDownIcon, + CheckIcon, + PencilIcon, + TrashIcon, +} from '@heroicons/react/24/outline'; +import ConfirmDialog from '../Shared/ConfirmDialog'; interface AdminUserItem { id: number; @@ -64,6 +70,49 @@ const createAdminUser = async ( return await res.json(); }; +const updateAdminUser = async ( + id: number, + email: string, + t: any, + name?: string, + surname?: string, + role?: 'admin' | 'user', + password?: string +): Promise => { + const body: any = { email, name, surname, role }; + if (password) body.password = password; + + const res = await fetch(`/api/admin/users/${id}`, { + method: 'PUT', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify(body), + }); + if (res.status === 401) + throw new Error( + t('admin.authenticationRequired', 'Authentication required') + ); + if (res.status === 403) throw new Error(t('admin.forbidden', 'Forbidden')); + if (res.status === 409) + throw new Error(t('admin.emailAlreadyExists', 'Email already exists')); + if (res.status === 404) + throw new Error(t('admin.userNotFound', 'User not found')); + if (!res.ok) { + let message = t('admin.failedToUpdateUser', 'Failed to update user'); + try { + const body = await res.json(); + if (body?.error) message = body.error; + } catch { + // ignore non-JSON error bodies + } + throw new Error(message); + } + return await res.json(); +}; + const deleteAdminUser = async (id: number, t: any): Promise => { const res = await fetch(`/api/admin/users/${id}`, { method: 'DELETE', @@ -91,7 +140,9 @@ const AddUserModal: React.FC<{ isOpen: boolean; onClose: () => void; onCreated: (user: AdminUserItem) => void; -}> = ({ isOpen, onClose, onCreated }) => { + onUpdated: (user: AdminUserItem) => void; + editingUser?: AdminUserItem | null; +}> = ({ isOpen, onClose, onCreated, onUpdated, editingUser }) => { const { t } = useTranslation(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -110,15 +161,23 @@ const AddUserModal: React.FC<{ useEffect(() => { if (isOpen) { - setEmail(''); - setPassword(''); - setName(''); - setSurname(''); - setRole('user'); + if (editingUser) { + setEmail(editingUser.email); + setPassword(''); + setName(editingUser.name || ''); + setSurname(editingUser.surname || ''); + setRole(editingUser.role); + } else { + setEmail(''); + setPassword(''); + setName(''); + setSurname(''); + setRole('user'); + } setError(null); setIsRoleDropdownOpen(false); } - }, [isOpen]); + }, [isOpen, editingUser]); // Close dropdown when clicking outside useEffect(() => { @@ -144,7 +203,7 @@ const AddUserModal: React.FC<{ const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(null); - if (!email || !password) { + if (!email) { setError(t('errors.required', 'This field is required')); return; } @@ -152,22 +211,45 @@ const AddUserModal: React.FC<{ setError(t('errors.invalidEmail', 'Invalid email address')); return; } + // Password is required for new users, optional for updates + if (!editingUser && !password) { + setError(t('errors.required', 'This field is required')); + return; + } setSubmitting(true); try { - const user = await createAdminUser( - email, - password, - t, - name, - surname, - role - ); - onCreated(user); + if (editingUser) { + const user = await updateAdminUser( + editingUser.id, + email, + t, + name, + surname, + role, + password || undefined + ); + onUpdated(user); + } else { + const user = await createAdminUser( + email, + password, + t, + name, + surname, + role + ); + onCreated(user); + } onClose(); } catch (err: any) { setError( err.message || - t('admin.failedToCreateUser', 'Failed to create user') + (editingUser + ? t('admin.failedToUpdateUser', 'Failed to update user') + : t( + 'admin.failedToCreateUser', + 'Failed to create user' + )) ); } finally { setSubmitting(false); @@ -184,7 +266,9 @@ const AddUserModal: React.FC<{ onClick={(e) => e.stopPropagation()} >

- {t('admin.addUser', 'Add user')} + {editingUser + ? t('admin.editUser', 'Edit user') + : t('admin.addUser', 'Add user')}

@@ -224,13 +308,23 @@ const AddUserModal: React.FC<{
setPassword(e.target.value)} - required + required={!editingUser} minLength={6} />
@@ -321,7 +415,9 @@ const AddUserModal: React.FC<{ > {submitting ? t('common.saving', 'Saving...') - : t('common.create', 'Create')} + : editingUser + ? t('common.save', 'Save') + : t('common.create', 'Create')}
@@ -335,12 +431,13 @@ const AdminUsersPage: React.FC = () => { const [users, setUsers] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); - const [selectedIds, setSelectedIds] = useState>(new Set()); const [addOpen, setAddOpen] = useState(false); + const [editingUser, setEditingUser] = useState(null); + const [userToDelete, setUserToDelete] = useState( + null + ); const navigate = useNavigate(); - const selectedCount = selectedIds.size; - const load = async () => { setLoading(true); setError(null); @@ -363,57 +460,24 @@ const AdminUsersPage: React.FC = () => { load(); }, []); - const toggleSelect = (id: number) => { - setSelectedIds((prev) => { - const next = new Set(prev); - if (next.has(id)) next.delete(id); - else next.add(id); - return next; - }); - }; + const handleDeleteUser = async () => { + if (!userToDelete) return; - const toggleSelectAll = () => { - if (!users) return; - setSelectedIds((prev) => { - if (prev.size === users.length) return new Set(); - return new Set(users.map((u) => u.id)); - }); - }; - - const removeSelected = async () => { - if (!users || selectedIds.size === 0) return; - const toDelete = Array.from(selectedIds); - const remaining: AdminUserItem[] = []; - const byId = new Map(users.map((u) => [u.id, u] as const)); - for (const id of toDelete) { - try { - await deleteAdminUser(id, t); - } catch (err: any) { - // Keep the user if deletion failed and surface error inline later - console.error( - t('admin.failedToDeleteUser', 'Failed to delete user'), - id, - err?.message - ); - remaining.push(byId.get(id)!); - } + try { + await deleteAdminUser(userToDelete.id, t); + setUsers((prev) => + prev ? prev.filter((u) => u.id !== userToDelete.id) : null + ); + setUserToDelete(null); + } catch (err: any) { + setError( + err.message || + t('admin.failedToDeleteUser', 'Failed to delete user') + ); + setUserToDelete(null); } - const next = users.filter((u) => !toDelete.includes(u.id)); - // If any failed, keep them - const nextWithFailures = remaining.length - ? next.concat( - remaining.filter((r) => !next.find((n) => n.id === r.id)) - ) - : next; - setUsers(nextWithFailures); - setSelectedIds(new Set()); }; - const headerCheckboxChecked = useMemo(() => { - if (!users || users.length === 0) return false; - return selectedIds.size === users.length; - }, [users, selectedIds]); - return (
@@ -421,21 +485,15 @@ const AdminUsersPage: React.FC = () => {

{t('admin.userManagement', 'User Management')}

-
- - -
+
{error && ( @@ -448,14 +506,6 @@ const AdminUsersPage: React.FC = () => { - @@ -471,6 +521,9 @@ const AdminUsersPage: React.FC = () => { + @@ -504,16 +557,6 @@ const AdminUsersPage: React.FC = () => { key={u.id} className="hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors duration-150" > - @@ -537,6 +580,35 @@ const AdminUsersPage: React.FC = () => { : t('admin.user', 'user')} + ))} @@ -545,11 +617,35 @@ const AdminUsersPage: React.FC = () => { setAddOpen(false)} + onClose={() => { + setAddOpen(false); + setEditingUser(null); + }} onCreated={(user) => setUsers((prev) => (prev ? [user, ...prev] : [user])) } + onUpdated={(user) => + setUsers((prev) => + prev + ? prev.map((u) => (u.id === user.id ? user : u)) + : [user] + ) + } + editingUser={editingUser} /> + + {userToDelete && ( + setUserToDelete(null)} + /> + )} ); diff --git a/public/locales/ar/translation.json b/public/locales/ar/translation.json index 7f0766d..c9be932 100644 --- a/public/locales/ar/translation.json +++ b/public/locales/ar/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "فشل في إنشاء المستخدم", "badRequest": "طلب غير صحيح", "userNotFound": "المستخدم غير موجود", - "failedToDeleteUser": "فشل في حذف المستخدم" + "failedToDeleteUser": "فشل في حذف المستخدم", + "editUser": "تعديل المستخدم", + "actions": "الإجراءات", + "passwordOptional": "اتركه فارغًا للاحتفاظ بالحالي", + "failedToUpdateUser": "فشل في تحديث المستخدم", + "confirmDelete": "هل أنت متأكد أنك تريد حذف هذا المستخدم؟", + "deleteUser": "حذف المستخدم", + "confirmDeleteUser": "هل أنت متأكد أنك تريد حذف {{email}}؟ لا يمكن التراجع عن هذا الإجراء." }, "shares": { "shareProject": "مشاركة المشروع", diff --git a/public/locales/bg/translation.json b/public/locales/bg/translation.json index ecaeb65..5176754 100644 --- a/public/locales/bg/translation.json +++ b/public/locales/bg/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Неуспешно създаване на потребител", "badRequest": "Невалидна заявка", "userNotFound": "Потребителят не е намерен", - "failedToDeleteUser": "Неуспешно изтриване на потребител" + "failedToDeleteUser": "Неуспешно изтриване на потребител", + "editUser": "Редактиране на потребител", + "actions": "Действия", + "passwordOptional": "Оставете празно, за да запазите текущата", + "failedToUpdateUser": "Неуспешно обновяване на потребителя", + "confirmDelete": "Сигурни ли сте, че искате да изтриете този потребител?", + "deleteUser": "Изтриване на потребител", + "confirmDeleteUser": "Сигурни ли сте, че искате да изтриете {{email}}? Това действие не може да бъде отменено." }, "shares": { "shareProject": "Сподели проект", diff --git a/public/locales/da/translation.json b/public/locales/da/translation.json index 7201319..1e8be59 100644 --- a/public/locales/da/translation.json +++ b/public/locales/da/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Kunne ikke oprette bruger", "badRequest": "Ugyldig anmodning", "userNotFound": "Bruger ikke fundet", - "failedToDeleteUser": "Kunne ikke slette bruger" + "failedToDeleteUser": "Kunne ikke slette bruger", + "editUser": "Rediger bruger", + "actions": "Handlinger", + "passwordOptional": "Lad være tom for at bevare nuværende", + "failedToUpdateUser": "Kunne ikke opdatere bruger", + "confirmDelete": "Er du sikker på, at du vil slette denne bruger?", + "deleteUser": "Slet bruger", + "confirmDeleteUser": "Er du sikker på, at du vil slette {{email}}? Denne handling kan ikke fortrydes." }, "shares": { "shareProject": "Del projekt", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 03e1f81..087195f 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -963,7 +963,14 @@ "failedToCreateUser": "Benutzer konnte nicht erstellt werden", "badRequest": "Ungültige Anfrage", "userNotFound": "Benutzer nicht gefunden", - "failedToDeleteUser": "Benutzer konnte nicht gelöscht werden" + "failedToDeleteUser": "Benutzer konnte nicht gelöscht werden", + "editUser": "Benutzer bearbeiten", + "actions": "Aktionen", + "passwordOptional": "Feld leer lassen, um das aktuelle Passwort beizubehalten", + "failedToUpdateUser": "Fehler beim Aktualisieren des Benutzers", + "confirmDelete": "Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?", + "deleteUser": "Benutzer löschen", + "confirmDeleteUser": "Sind Sie sicher, dass Sie {{email}} löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden." }, "shares": { "shareProject": "Projekt teilen", diff --git a/public/locales/el/translation.json b/public/locales/el/translation.json index 0d54354..93fe385 100644 --- a/public/locales/el/translation.json +++ b/public/locales/el/translation.json @@ -940,15 +940,18 @@ "manageUsers": "Διαχείριση χρηστών", "userManagement": "Διαχείριση Χρηστών", "addUser": "Προσθήκη χρήστη", + "editUser": "Επεξεργασία χρήστη", "remove": "Αφαίρεση", "email": "Ηλεκτρονικό ταχυδρομείο", "created": "Δημιουργήθηκε", "role": "Ρόλος", + "actions": "Ενέργειες", "loadingUsers": "Φόρτωση χρηστών...", "noUsers": "Δεν υπάρχουν χρήστες", "admin": "διαχειριστής", "user": "χρήστης", "password": "Κωδικός πρόσβασης", + "passwordOptional": "Αφήστε το κενό για να διατηρήσετε τον τρέχοντα", "name": "Όνομα", "surname": "Επώνυμο", "authenticationRequired": "Απαιτείται αυθεντικοποίηση", @@ -956,9 +959,13 @@ "failedToLoadUsers": "Αποτυχία φόρτωσης χρηστών", "emailAlreadyExists": "Το email υπάρχει ήδη", "failedToCreateUser": "Αποτυχία δημιουργίας χρήστη", + "failedToUpdateUser": "Αποτυχία ενημέρωσης χρήστη", "badRequest": "Κακή αίτηση", "userNotFound": "Ο χρήστης δεν βρέθηκε", - "failedToDeleteUser": "Αποτυχία διαγραφής χρήστη" + "failedToDeleteUser": "Αποτυχία διαγραφής χρήστη", + "confirmDelete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτόν τον χρήστη;", + "deleteUser": "Διαγραφή Χρήστη", + "confirmDeleteUser": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το {{email}}; Αυτή η ενέργεια δεν μπορεί να αναιρεθεί." }, "shares": { "shareProject": "Κοινή χρήση έργου", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index bed8fd0..54e3d6f 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -937,25 +937,32 @@ "manageUsers": "Manage users", "userManagement": "User Management", "addUser": "Add user", + "editUser": "Edit user", "remove": "Remove", "email": "Email", "name": "Name", "surname": "Surname", "created": "Created", "role": "Role", + "actions": "Actions", "loadingUsers": "Loading users...", "noUsers": "No users", "admin": "admin", "user": "user", "password": "Password", + "passwordOptional": "Leave blank to keep current", "authenticationRequired": "Authentication required", "forbidden": "Forbidden", "failedToLoadUsers": "Failed to load users", "emailAlreadyExists": "Email already exists", "failedToCreateUser": "Failed to create user", + "failedToUpdateUser": "Failed to update user", "badRequest": "Bad request", "userNotFound": "User not found", - "failedToDeleteUser": "Failed to delete user" + "failedToDeleteUser": "Failed to delete user", + "confirmDelete": "Are you sure you want to delete this user?", + "deleteUser": "Delete User", + "confirmDeleteUser": "Are you sure you want to delete {{email}}? This action cannot be undone." }, "shares": { "shareProject": "Share project", diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 85583d9..45261b5 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -955,7 +955,14 @@ "failedToCreateUser": "Error al crear usuario", "badRequest": "Solicitud incorrecta", "userNotFound": "Usuario no encontrado", - "failedToDeleteUser": "Error al eliminar usuario" + "failedToDeleteUser": "Error al eliminar usuario", + "editUser": "Editar usuario", + "actions": "Acciones", + "passwordOptional": "Dejar en blanco para mantener el actual", + "failedToUpdateUser": "Error al actualizar el usuario", + "confirmDelete": "¿Está seguro de que desea eliminar este usuario?", + "deleteUser": "Eliminar usuario", + "confirmDeleteUser": "¿Está seguro de que desea eliminar {{email}}? Esta acción no se puede deshacer." }, "shares": { "shareProject": "Compartir proyecto", diff --git a/public/locales/fi/translation.json b/public/locales/fi/translation.json index b3d492c..2a31a15 100644 --- a/public/locales/fi/translation.json +++ b/public/locales/fi/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Käyttäjän luominen epäonnistui", "badRequest": "Virheellinen pyyntö", "userNotFound": "Käyttäjää ei löytynyt", - "failedToDeleteUser": "Käyttäjän poistaminen epäonnistui" + "failedToDeleteUser": "Käyttäjän poistaminen epäonnistui", + "editUser": "Muokkaa käyttäjää", + "actions": "Toiminnot", + "passwordOptional": "Jätä tyhjäksi säilyttääksesi nykyisen", + "failedToUpdateUser": "Käyttäjän päivittäminen epäonnistui", + "confirmDelete": "Oletko varma, että haluat poistaa tämän käyttäjän?", + "deleteUser": "Poista käyttäjä", + "confirmDeleteUser": "Oletko varma, että haluat poistaa {{email}}? Tätä toimintoa ei voi peruuttaa." }, "shares": { "shareProject": "Jaa projekti", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index cc11ece..017b7b3 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Échec de la création de l'utilisateur", "badRequest": "Mauvaise requête", "userNotFound": "Utilisateur non trouvé", - "failedToDeleteUser": "Échec de la suppression de l'utilisateur" + "failedToDeleteUser": "Échec de la suppression de l'utilisateur", + "editUser": "Modifier l'utilisateur", + "actions": "Actions", + "passwordOptional": "Laissez vide pour conserver l'actuel", + "failedToUpdateUser": "Échec de la mise à jour de l'utilisateur", + "confirmDelete": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?", + "deleteUser": "Supprimer l'utilisateur", + "confirmDeleteUser": "Êtes-vous sûr de vouloir supprimer {{email}} ? Cette action ne peut pas être annulée." }, "shares": { "shareProject": "Partager le projet", diff --git a/public/locales/id/translation.json b/public/locales/id/translation.json index 44918e1..cca05b6 100644 --- a/public/locales/id/translation.json +++ b/public/locales/id/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Gagal membuat pengguna", "badRequest": "Permintaan tidak valid", "userNotFound": "Pengguna tidak ditemukan", - "failedToDeleteUser": "Gagal menghapus pengguna" + "failedToDeleteUser": "Gagal menghapus pengguna", + "editUser": "Edit pengguna", + "actions": "Tindakan", + "passwordOptional": "Biarkan kosong untuk mempertahankan yang sekarang", + "failedToUpdateUser": "Gagal memperbarui pengguna", + "confirmDelete": "Apakah Anda yakin ingin menghapus pengguna ini?", + "deleteUser": "Hapus Pengguna", + "confirmDeleteUser": "Apakah Anda yakin ingin menghapus {{email}}? Tindakan ini tidak dapat dibatalkan." }, "shares": { "shareProject": "Bagikan proyek", diff --git a/public/locales/it/translation.json b/public/locales/it/translation.json index d85aa63..238f92b 100644 --- a/public/locales/it/translation.json +++ b/public/locales/it/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Impossibile creare l'utente", "badRequest": "Richiesta non valida", "userNotFound": "Utente non trovato", - "failedToDeleteUser": "Impossibile eliminare l'utente" + "failedToDeleteUser": "Impossibile eliminare l'utente", + "editUser": "Modifica utente", + "actions": "Azioni", + "passwordOptional": "Lascia vuoto per mantenere quello attuale", + "failedToUpdateUser": "Impossibile aggiornare l'utente", + "confirmDelete": "Sei sicuro di voler eliminare questo utente?", + "deleteUser": "Elimina utente", + "confirmDeleteUser": "Sei sicuro di voler eliminare {{email}}? Questa azione non può essere annullata." }, "shares": { "shareProject": "Condividi progetto", diff --git a/public/locales/jp/translation.json b/public/locales/jp/translation.json index 4368a36..e6e6507 100644 --- a/public/locales/jp/translation.json +++ b/public/locales/jp/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "ユーザーの作成に失敗しました", "badRequest": "不正なリクエスト", "userNotFound": "ユーザーが見つかりません", - "failedToDeleteUser": "ユーザーの削除に失敗しました" + "failedToDeleteUser": "ユーザーの削除に失敗しました", + "editUser": "ユーザーを編集", + "actions": "アクション", + "passwordOptional": "現在のままにするには空白のままにしてください", + "failedToUpdateUser": "ユーザーの更新に失敗しました", + "confirmDelete": "このユーザーを削除してもよろしいですか?", + "deleteUser": "ユーザーを削除", + "confirmDeleteUser": "{{email}} を削除してもよろしいですか?この操作は元に戻せません。" }, "shares": { "shareProject": "プロジェクトを共有", diff --git a/public/locales/ko/translation.json b/public/locales/ko/translation.json index 6307a81..a349560 100644 --- a/public/locales/ko/translation.json +++ b/public/locales/ko/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "사용자 생성 실패", "badRequest": "잘못된 요청", "userNotFound": "사용자를 찾을 수 없습니다", - "failedToDeleteUser": "사용자 삭제 실패" + "failedToDeleteUser": "사용자 삭제 실패", + "editUser": "사용자 편집", + "actions": "작업", + "passwordOptional": "현재 비밀번호를 유지하려면 비워 두세요", + "failedToUpdateUser": "사용자 업데이트에 실패했습니다", + "confirmDelete": "이 사용자를 삭제하시겠습니까?", + "deleteUser": "사용자 삭제", + "confirmDeleteUser": "{{email}}을(를) 삭제하시겠습니까? 이 작업은 취소할 수 없습니다." }, "shares": { "shareProject": "프로젝트 공유", diff --git a/public/locales/nl/translation.json b/public/locales/nl/translation.json index 626417c..29ee0f8 100644 --- a/public/locales/nl/translation.json +++ b/public/locales/nl/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Kon gebruiker niet aanmaken", "badRequest": "Ongeldig verzoek", "userNotFound": "Gebruiker niet gevonden", - "failedToDeleteUser": "Kon gebruiker niet verwijderen" + "failedToDeleteUser": "Kon gebruiker niet verwijderen", + "editUser": "Bewerk gebruiker", + "actions": "Acties", + "passwordOptional": "Laat leeg om huidige te behouden", + "failedToUpdateUser": "Bijwerken van gebruiker mislukt", + "confirmDelete": "Weet u zeker dat u deze gebruiker wilt verwijderen?", + "deleteUser": "Verwijder gebruiker", + "confirmDeleteUser": "Weet u zeker dat u {{email}} wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt." }, "shares": { "shareProject": "Project delen", diff --git a/public/locales/no/translation.json b/public/locales/no/translation.json index f73a013..529a018 100644 --- a/public/locales/no/translation.json +++ b/public/locales/no/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Kunne ikke opprette bruker", "badRequest": "Feil forespørsel", "userNotFound": "Bruker ikke funnet", - "failedToDeleteUser": "Kunne ikke slette bruker" + "failedToDeleteUser": "Kunne ikke slette bruker", + "editUser": "Rediger bruker", + "actions": "Handlinger", + "passwordOptional": "La stå blankt for å beholde nåværende", + "failedToUpdateUser": "Kunne ikke oppdatere bruker", + "confirmDelete": "Er du sikker på at du vil slette denne brukeren?", + "deleteUser": "Slett bruker", + "confirmDeleteUser": "Er du sikker på at du vil slette {{email}}? Denne handlingen kan ikke angres." }, "shares": { "shareProject": "Del prosjekt", diff --git a/public/locales/pl/translation.json b/public/locales/pl/translation.json index 4f33e85..30d94ef 100644 --- a/public/locales/pl/translation.json +++ b/public/locales/pl/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Nie udało się utworzyć użytkownika", "badRequest": "Złe żądanie", "userNotFound": "Użytkownik nie znaleziony", - "failedToDeleteUser": "Nie udało się usunąć użytkownika" + "failedToDeleteUser": "Nie udało się usunąć użytkownika", + "editUser": "Edytuj użytkownika", + "actions": "Akcje", + "passwordOptional": "Pozostaw puste, aby zachować obecne", + "failedToUpdateUser": "Nie udało się zaktualizować użytkownika", + "confirmDelete": "Czy na pewno chcesz usunąć tego użytkownika?", + "deleteUser": "Usuń użytkownika", + "confirmDeleteUser": "Czy na pewno chcesz usunąć {{email}}? Ta akcja nie może być cofnięta." }, "shares": { "shareProject": "Udostępnij projekt", diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json index bdf7508..edee77a 100644 --- a/public/locales/pt/translation.json +++ b/public/locales/pt/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Falha ao criar usuário", "badRequest": "Requisição inválida", "userNotFound": "Usuário não encontrado", - "failedToDeleteUser": "Falha ao deletar usuário" + "failedToDeleteUser": "Falha ao deletar usuário", + "editUser": "Editar usuário", + "actions": "Ações", + "passwordOptional": "Deixe em branco para manter o atual", + "failedToUpdateUser": "Falha ao atualizar o usuário", + "confirmDelete": "Você tem certeza de que deseja excluir este usuário?", + "deleteUser": "Excluir Usuário", + "confirmDeleteUser": "Você tem certeza de que deseja excluir {{email}}? Esta ação não pode ser desfeita." }, "shares": { "shareProject": "Compartilhar projeto", diff --git a/public/locales/ro/translation.json b/public/locales/ro/translation.json index 1173aac..7342b61 100644 --- a/public/locales/ro/translation.json +++ b/public/locales/ro/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Crearea utilizatorului a eșuat", "badRequest": "Cerere incorectă", "userNotFound": "Utilizatorul nu a fost găsit", - "failedToDeleteUser": "Ștergerea utilizatorului a eșuat" + "failedToDeleteUser": "Ștergerea utilizatorului a eșuat", + "editUser": "Editează utilizator", + "actions": "Acțiuni", + "passwordOptional": "Lăsați gol pentru a păstra actual", + "failedToUpdateUser": "Actualizarea utilizatorului a eșuat", + "confirmDelete": "Ești sigur că vrei să ștergi acest utilizator?", + "deleteUser": "Șterge utilizator", + "confirmDeleteUser": "Ești sigur că vrei să ștergi {{email}}? Această acțiune nu poate fi anulată." }, "shares": { "shareProject": "Partajați proiectul", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 7dd156a..c075298 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Не удалось создать пользователя", "badRequest": "Неверный запрос", "userNotFound": "Пользователь не найден", - "failedToDeleteUser": "Не удалось удалить пользователя" + "failedToDeleteUser": "Не удалось удалить пользователя", + "editUser": "Редактировать пользователя", + "actions": "Действия", + "passwordOptional": "Оставьте пустым, чтобы сохранить текущий", + "failedToUpdateUser": "Не удалось обновить пользователя", + "confirmDelete": "Вы уверены, что хотите удалить этого пользователя?", + "deleteUser": "Удалить пользователя", + "confirmDeleteUser": "Вы уверены, что хотите удалить {{email}}? Это действие нельзя отменить." }, "shares": { "shareProject": "Поделиться проектом", diff --git a/public/locales/sl/translation.json b/public/locales/sl/translation.json index 90ff0db..124a1e6 100644 --- a/public/locales/sl/translation.json +++ b/public/locales/sl/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Ustvarjanje uporabnika je spodletelo", "badRequest": "Neveljavna zahteva", "userNotFound": "Uporabnik ni bil najden", - "failedToDeleteUser": "Brisanje uporabnika je spodletelo" + "failedToDeleteUser": "Brisanje uporabnika je spodletelo", + "editUser": "Uredi uporabnika", + "actions": "Dejanja", + "passwordOptional": "Pustite prazno, da obdržite trenutno", + "failedToUpdateUser": "Posodobitev uporabnika ni uspela", + "confirmDelete": "Ali ste prepričani, da želite izbrisati tega uporabnika?", + "deleteUser": "Izbriši uporabnika", + "confirmDeleteUser": "Ali ste prepričani, da želite izbrisati {{email}}? Te akcije ni mogoče razveljaviti." }, "shares": { "shareProject": "Deli projekt", diff --git a/public/locales/sv/translation.json b/public/locales/sv/translation.json index 5ca4c46..875ae1f 100644 --- a/public/locales/sv/translation.json +++ b/public/locales/sv/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Misslyckades med att skapa användare", "badRequest": "Felaktig begäran", "userNotFound": "Användare hittades inte", - "failedToDeleteUser": "Misslyckades med att ta bort användare" + "failedToDeleteUser": "Misslyckades med att ta bort användare", + "editUser": "Redigera användare", + "actions": "Åtgärder", + "passwordOptional": "Lämna tomt för att behålla nuvarande", + "failedToUpdateUser": "Misslyckades med att uppdatera användare", + "confirmDelete": "Är du säker på att du vill ta bort denna användare?", + "deleteUser": "Ta bort användare", + "confirmDeleteUser": "Är du säker på att du vill ta bort {{email}}? Denna åtgärd kan inte ångras." }, "shares": { "shareProject": "Dela projekt", diff --git a/public/locales/tr/translation.json b/public/locales/tr/translation.json index 6fed0cf..c14cf5a 100644 --- a/public/locales/tr/translation.json +++ b/public/locales/tr/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Kullanıcı oluşturulamadı", "badRequest": "Geçersiz istek", "userNotFound": "Kullanıcı bulunamadı", - "failedToDeleteUser": "Kullanıcı silinemedi" + "failedToDeleteUser": "Kullanıcı silinemedi", + "editUser": "Kullanıcıyı Düzenle", + "actions": "Eylemler", + "passwordOptional": "Mevcut şifreyi korumak için boş bırakın", + "failedToUpdateUser": "Kullanıcıyı güncellemeye başarısız olundu", + "confirmDelete": "Bu kullanıcıyı silmek istediğinize emin misiniz?", + "deleteUser": "Kullanıcıyı Sil", + "confirmDeleteUser": "{{email}}'yi silmek istediğinize emin misiniz? Bu işlem geri alınamaz." }, "shares": { "shareProject": "Projeyi paylaş", diff --git a/public/locales/ua/translation.json b/public/locales/ua/translation.json index bb6ed9b..56a9857 100644 --- a/public/locales/ua/translation.json +++ b/public/locales/ua/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Не вдалося створити користувача", "badRequest": "Неправильний запит", "userNotFound": "Користувача не знайдено", - "failedToDeleteUser": "Не вдалося видалити користувача" + "failedToDeleteUser": "Не вдалося видалити користувача", + "editUser": "Редагувати користувача", + "actions": "Дії", + "passwordOptional": "Залиште порожнім, щоб зберегти поточний", + "failedToUpdateUser": "Не вдалося оновити користувача", + "confirmDelete": "Ви впевнені, що хочете видалити цього користувача?", + "deleteUser": "Видалити користувача", + "confirmDeleteUser": "Ви впевнені, що хочете видалити {{email}}? Цю дію не можна скасувати." }, "shares": { "shareProject": "Поділитися проектом", diff --git a/public/locales/vi/translation.json b/public/locales/vi/translation.json index 9f045f3..1e0935a 100644 --- a/public/locales/vi/translation.json +++ b/public/locales/vi/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "Không thể tạo người dùng", "badRequest": "Yêu cầu không hợp lệ", "userNotFound": "Không tìm thấy người dùng", - "failedToDeleteUser": "Không thể xóa người dùng" + "failedToDeleteUser": "Không thể xóa người dùng", + "editUser": "Chỉnh sửa người dùng", + "actions": "Hành động", + "passwordOptional": "Để trống để giữ nguyên", + "failedToUpdateUser": "Cập nhật người dùng không thành công", + "confirmDelete": "Bạn có chắc chắn muốn xóa người dùng này không?", + "deleteUser": "Xóa người dùng", + "confirmDeleteUser": "Bạn có chắc chắn muốn xóa {{email}}? Hành động này không thể hoàn tác." }, "shares": { "shareProject": "Chia sẻ dự án", diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index dd673e4..0451d95 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -954,7 +954,14 @@ "failedToCreateUser": "创建用户失败", "badRequest": "错误的请求", "userNotFound": "用户未找到", - "failedToDeleteUser": "删除用户失败" + "failedToDeleteUser": "删除用户失败", + "editUser": "编辑用户", + "actions": "操作", + "passwordOptional": "留空以保持当前密码", + "failedToUpdateUser": "更新用户失败", + "confirmDelete": "您确定要删除此用户吗?", + "deleteUser": "删除用户", + "confirmDeleteUser": "您确定要删除 {{email}} 吗?此操作无法撤销。" }, "shares": { "shareProject": "分享项目",
- - {t('admin.email', 'Email')} {t('admin.role', 'Role')} + {t('admin.actions', 'Actions')} +
- - toggleSelect(u.id) - } - className="w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2 bg-white dark:bg-gray-700" - /> - {u.email} +
+ + +
+