mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
Merge Tag v0.2.16.0
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 0.2.16
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
|
||||||
|
* move delete user button to user form
|
||||||
|
* disable deleting user on demo instance
|
||||||
|
|
||||||
## Version 0.2.15
|
## Version 0.2.15
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createUser, updateUser, updateUserPassword, uploadAvatar } from '@/app/actions/data';
|
import { createUser, updateUser, updateUserPassword, uploadAvatar } from '@/app/actions/data';
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger,
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { serverSettingsAtom, usersAtom } from '@/lib/atoms';
|
import { serverSettingsAtom, usersAtom } from '@/lib/atoms';
|
||||||
import { useHelpers } from '@/lib/client-helpers';
|
import { useHelpers } from '@/lib/client-helpers';
|
||||||
@@ -57,6 +68,69 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
|
|||||||
);
|
);
|
||||||
const isEditing = !!user;
|
const isEditing = !!user;
|
||||||
|
|
||||||
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
|
const handleDeleteUser = async () => {
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
if (serverSettings.isDemo) {
|
||||||
|
toast({
|
||||||
|
title: t('errorTitle'),
|
||||||
|
description: t('toastDemoDeleteDisabled'),
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUser && currentUser.id === user.id) {
|
||||||
|
toast({
|
||||||
|
title: t('errorTitle'),
|
||||||
|
description: t('toastCannotDeleteSelf'),
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsDeleting(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/user/delete', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ userId: user.id }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
setUsersData(prev => ({
|
||||||
|
...prev,
|
||||||
|
users: prev.users.filter(u => u.id !== user.id),
|
||||||
|
}));
|
||||||
|
toast({
|
||||||
|
title: t('toastUserDeletedTitle'),
|
||||||
|
description: t('toastUserDeletedDescription', { username: user.username }),
|
||||||
|
variant: 'default'
|
||||||
|
});
|
||||||
|
onSuccess();
|
||||||
|
} else {
|
||||||
|
const errorData = await response.json();
|
||||||
|
toast({
|
||||||
|
title: t('errorTitle'),
|
||||||
|
description: errorData.error || t('genericError'),
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: t('errorTitle'),
|
||||||
|
description: t('networkError'),
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsDeleting(false);
|
||||||
|
setShowDeleteConfirm(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -274,6 +348,38 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2 pt-2">
|
<div className="flex justify-end gap-2 pt-2">
|
||||||
|
{isEditing && (
|
||||||
|
<AlertDialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
||||||
|
<AlertDialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="destructive"
|
||||||
|
className="mr-auto"
|
||||||
|
disabled={serverSettings.isDemo || isDeleting}
|
||||||
|
>
|
||||||
|
{isDeleting ? t('deletingButtonText') : t('deleteAccountButton')}
|
||||||
|
</Button>
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>{t('areYouSure')}</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
{t('deleteUserConfirmation', { username: user.username })}
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel disabled={isDeleting}>{t('cancel')}</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={handleDeleteUser}
|
||||||
|
disabled={isDeleting}
|
||||||
|
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||||
|
>
|
||||||
|
{isDeleting ? t('deletingButtonText') : t('confirmDeleteButtonText')}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -33,62 +33,19 @@ function UserCard({
|
|||||||
onEdit,
|
onEdit,
|
||||||
showEdit,
|
showEdit,
|
||||||
isCurrentUser,
|
isCurrentUser,
|
||||||
currentLoggedInUserId, // For "don't delete self" check
|
|
||||||
onUserDeleted // Callback to update usersAtom
|
|
||||||
}: {
|
}: {
|
||||||
user: User,
|
user: User,
|
||||||
onSelect: () => void,
|
onSelect: () => void,
|
||||||
onEdit: () => void,
|
onEdit: () => void,
|
||||||
showEdit: boolean,
|
showEdit: boolean,
|
||||||
isCurrentUser: boolean,
|
isCurrentUser: boolean,
|
||||||
currentLoggedInUserId?: string,
|
|
||||||
onUserDeleted: (userId: string) => void,
|
|
||||||
}) {
|
}) {
|
||||||
const t = useTranslations('UserSelectModal');
|
const t = useTranslations('UserSelectModal');
|
||||||
const tWarning = useTranslations('Warning');
|
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
||||||
const [isDeleting, setIsDeleting] = useState(false);
|
|
||||||
|
|
||||||
const handleDeleteUser = async () => {
|
|
||||||
setIsDeleting(true);
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/user/delete', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ userId: user.id }),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
toast({
|
|
||||||
title: t('deleteUserSuccessTitle'),
|
|
||||||
description: t('deleteUserSuccessDescription', { username: user.username }),
|
|
||||||
});
|
|
||||||
onUserDeleted(user.id);
|
|
||||||
} else {
|
|
||||||
const errorData = await response.json();
|
|
||||||
toast({
|
|
||||||
title: t('deleteUserErrorTitle'),
|
|
||||||
description: errorData.error || t('genericError'),
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
toast({
|
|
||||||
title: t('deleteUserErrorTitle'),
|
|
||||||
description: t('networkError'),
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setIsDeleting(false);
|
|
||||||
setShowDeleteConfirm(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={user.id} className="relative group">
|
<div key={user.id} className="relative group">
|
||||||
<button
|
<button
|
||||||
onClick={onSelect}
|
onClick={onSelect}
|
||||||
disabled={isDeleting} // Disable main button while deleting this user
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-center gap-2 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors w-full",
|
"flex flex-col items-center gap-2 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors w-full",
|
||||||
isCurrentUser && "ring-2 ring-primary"
|
isCurrentUser && "ring-2 ring-primary"
|
||||||
@@ -116,48 +73,12 @@ function UserCard({
|
|||||||
e.stopPropagation(); // Prevent card selection
|
e.stopPropagation(); // Prevent card selection
|
||||||
onEdit();
|
onEdit();
|
||||||
}}
|
}}
|
||||||
disabled={isDeleting}
|
|
||||||
className="p-1 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 transition-colors"
|
className="p-1 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 transition-colors"
|
||||||
title={t('editUserTooltip')}
|
title={t('editUserTooltip')}
|
||||||
>
|
>
|
||||||
<UserRoundPen className="h-4 w-4" />
|
<UserRoundPen className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{showEdit && user.id !== currentLoggedInUserId && (
|
|
||||||
<AlertDialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
|
||||||
<AlertDialogTrigger asChild>
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation(); // Prevent card selection
|
|
||||||
setShowDeleteConfirm(true);
|
|
||||||
}}
|
|
||||||
disabled={isDeleting}
|
|
||||||
className="p-1 rounded-full bg-red-200 hover:bg-red-300 dark:bg-red-700 dark:hover:bg-red-600 transition-colors text-red-600 dark:text-red-300"
|
|
||||||
title={t('deleteUserTooltip')}
|
|
||||||
>
|
|
||||||
<Trash2 className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</AlertDialogTrigger>
|
|
||||||
<AlertDialogContent>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle>{tWarning('areYouSure')}</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
{t('deleteUserConfirmation', { username: user.username })}
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogCancel onClick={(e) => { e.stopPropagation(); setShowDeleteConfirm(false);}} disabled={isDeleting}>{tWarning('cancel')}</AlertDialogCancel>
|
|
||||||
<AlertDialogAction
|
|
||||||
onClick={(e) => { e.stopPropagation(); handleDeleteUser();}}
|
|
||||||
disabled={isDeleting}
|
|
||||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
||||||
>
|
|
||||||
{isDeleting ? t('deletingButtonText') : t('confirmDeleteButtonText')}
|
|
||||||
</AlertDialogAction>
|
|
||||||
</AlertDialogFooter>
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -187,14 +108,12 @@ function UserSelectionView({
|
|||||||
onUserSelect,
|
onUserSelect,
|
||||||
onEditUser,
|
onEditUser,
|
||||||
onCreateUser,
|
onCreateUser,
|
||||||
onUserDeleted, // Pass through the delete handler
|
|
||||||
}: {
|
}: {
|
||||||
users: User[],
|
users: User[],
|
||||||
currentUserFromHook?: SafeUser,
|
currentUserFromHook?: SafeUser,
|
||||||
onUserSelect: (userId: string) => void,
|
onUserSelect: (userId: string) => void,
|
||||||
onEditUser: (userId: string) => void,
|
onEditUser: (userId: string) => void,
|
||||||
onCreateUser: () => void,
|
onCreateUser: () => void,
|
||||||
onUserDeleted: (userId: string) => void,
|
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-3 gap-4 p-2 max-h-80 overflow-y-auto">
|
<div className="grid grid-cols-3 gap-4 p-2 max-h-80 overflow-y-auto">
|
||||||
@@ -208,8 +127,6 @@ function UserSelectionView({
|
|||||||
onEdit={() => onEditUser(user.id)}
|
onEdit={() => onEditUser(user.id)}
|
||||||
showEdit={!!currentUserFromHook?.isAdmin}
|
showEdit={!!currentUserFromHook?.isAdmin}
|
||||||
isCurrentUser={false} // This card isn't the currently logged-in user for switching TO
|
isCurrentUser={false} // This card isn't the currently logged-in user for switching TO
|
||||||
currentLoggedInUserId={currentUserFromHook?.id} // For the "don't delete self" check
|
|
||||||
onUserDeleted={onUserDeleted}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{currentUserFromHook?.isAdmin && <AddUserButton onClick={onCreateUser} />}
|
{currentUserFromHook?.isAdmin && <AddUserButton onClick={onCreateUser} />}
|
||||||
@@ -227,12 +144,6 @@ export default function UserSelectModal({ onClose }: { onClose: () => void }) {
|
|||||||
const users = usersData.users;
|
const users = usersData.users;
|
||||||
const { currentUser } = useHelpers();
|
const { currentUser } = useHelpers();
|
||||||
|
|
||||||
const handleUserDeleted = (userIdToDelete: string) => {
|
|
||||||
setUsersData(prevData => ({
|
|
||||||
...prevData,
|
|
||||||
users: prevData.users.filter(u => u.id !== userIdToDelete)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUserSelect = (userId: string) => {
|
const handleUserSelect = (userId: string) => {
|
||||||
setSelectedUser(userId);
|
setSelectedUser(userId);
|
||||||
@@ -278,7 +189,6 @@ export default function UserSelectModal({ onClose }: { onClose: () => void }) {
|
|||||||
onUserSelect={handleUserSelect}
|
onUserSelect={handleUserSelect}
|
||||||
onEditUser={handleEditUser}
|
onEditUser={handleEditUser}
|
||||||
onCreateUser={handleCreateUser}
|
onCreateUser={handleCreateUser}
|
||||||
onUserDeleted={handleUserDeleted}
|
|
||||||
/>
|
/>
|
||||||
) : isCreating || isEditing ? (
|
) : isCreating || isEditing ? (
|
||||||
<UserForm
|
<UserForm
|
||||||
|
|||||||
@@ -270,6 +270,12 @@
|
|||||||
"actionUpdate": "aktualisieren",
|
"actionUpdate": "aktualisieren",
|
||||||
"actionCreate": "erstellen",
|
"actionCreate": "erstellen",
|
||||||
"errorFailedUserAction": "Fehler beim {action} des Benutzers",
|
"errorFailedUserAction": "Fehler beim {action} des Benutzers",
|
||||||
|
"toastDemoDeleteDisabled": "Löschen ist in der Demo-Instanz deaktiviert",
|
||||||
|
"toastCannotDeleteSelf": "Sie können Ihr eigenes Konto nicht löschen",
|
||||||
|
"confirmDeleteUser": "Sind Sie sicher, dass Sie den Benutzer {username} löschen möchten?",
|
||||||
|
"toastUserDeletedTitle": "Benutzer gelöscht",
|
||||||
|
"toastUserDeletedDescription": "Benutzer {username} wurde erfolgreich gelöscht",
|
||||||
|
"toastDeleteUserFailed": "Fehler beim Löschen des Benutzers: {error}",
|
||||||
"errorTitle": "Fehler",
|
"errorTitle": "Fehler",
|
||||||
"errorFileSizeLimit": "Die Dateigröße muss kleiner als 5MB sein",
|
"errorFileSizeLimit": "Die Dateigröße muss kleiner als 5MB sein",
|
||||||
"toastAvatarUploadedTitle": "Avatar hochgeladen",
|
"toastAvatarUploadedTitle": "Avatar hochgeladen",
|
||||||
@@ -287,7 +293,13 @@
|
|||||||
"disablePasswordLabel": "Passwort deaktivieren",
|
"disablePasswordLabel": "Passwort deaktivieren",
|
||||||
"cancelButton": "Abbrechen",
|
"cancelButton": "Abbrechen",
|
||||||
"saveChangesButton": "Änderungen speichern",
|
"saveChangesButton": "Änderungen speichern",
|
||||||
"createUserButton": "Benutzer erstellen"
|
"createUserButton": "Benutzer erstellen",
|
||||||
|
"deleteAccountButton": "Konto löschen",
|
||||||
|
"deletingButtonText": "Wird gelöscht...",
|
||||||
|
"areYouSure": "Sind Sie sicher?",
|
||||||
|
"deleteUserConfirmation": "Sind Sie sicher, dass Sie den Benutzer {username} löschen möchten?",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"confirmDeleteButtonText": "Löschen"
|
||||||
},
|
},
|
||||||
"ViewToggle": {
|
"ViewToggle": {
|
||||||
"habitsLabel": "Gewohnheiten",
|
"habitsLabel": "Gewohnheiten",
|
||||||
|
|||||||
@@ -287,7 +287,8 @@
|
|||||||
"disablePasswordLabel": "Disable password",
|
"disablePasswordLabel": "Disable password",
|
||||||
"cancelButton": "Cancel",
|
"cancelButton": "Cancel",
|
||||||
"saveChangesButton": "Save Changes",
|
"saveChangesButton": "Save Changes",
|
||||||
"createUserButton": "Create User"
|
"createUserButton": "Create User",
|
||||||
|
"deleteAccountButton": "Delete Account"
|
||||||
},
|
},
|
||||||
"ViewToggle": {
|
"ViewToggle": {
|
||||||
"habitsLabel": "Habits",
|
"habitsLabel": "Habits",
|
||||||
|
|||||||
@@ -270,6 +270,12 @@
|
|||||||
"actionUpdate": "actualizar",
|
"actionUpdate": "actualizar",
|
||||||
"actionCreate": "crear",
|
"actionCreate": "crear",
|
||||||
"errorFailedUserAction": "Error al {action} usuario",
|
"errorFailedUserAction": "Error al {action} usuario",
|
||||||
|
"toastDemoDeleteDisabled": "La eliminación está deshabilitada en la instancia demo",
|
||||||
|
"toastCannotDeleteSelf": "No puedes eliminar tu propia cuenta",
|
||||||
|
"confirmDeleteUser": "¿Estás seguro de que deseas eliminar al usuario {username}?",
|
||||||
|
"toastUserDeletedTitle": "Usuario eliminado",
|
||||||
|
"toastUserDeletedDescription": "El usuario {username} ha sido eliminado correctamente",
|
||||||
|
"toastDeleteUserFailed": "Error al eliminar el usuario: {error}",
|
||||||
"errorTitle": "Error",
|
"errorTitle": "Error",
|
||||||
"errorFileSizeLimit": "El tamaño del archivo debe ser menor a 5MB",
|
"errorFileSizeLimit": "El tamaño del archivo debe ser menor a 5MB",
|
||||||
"toastAvatarUploadedTitle": "Avatar subido",
|
"toastAvatarUploadedTitle": "Avatar subido",
|
||||||
@@ -287,7 +293,13 @@
|
|||||||
"disablePasswordLabel": "Desactivar contraseña",
|
"disablePasswordLabel": "Desactivar contraseña",
|
||||||
"cancelButton": "Cancelar",
|
"cancelButton": "Cancelar",
|
||||||
"saveChangesButton": "Guardar cambios",
|
"saveChangesButton": "Guardar cambios",
|
||||||
"createUserButton": "Crear usuario"
|
"createUserButton": "Crear usuario",
|
||||||
|
"deleteAccountButton": "Eliminar cuenta",
|
||||||
|
"deletingButtonText": "Eliminando...",
|
||||||
|
"areYouSure": "¿Estás seguro?",
|
||||||
|
"deleteUserConfirmation": "¿Estás seguro de que deseas eliminar al usuario {username}?",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"confirmDeleteButtonText": "Eliminar"
|
||||||
},
|
},
|
||||||
"ViewToggle": {
|
"ViewToggle": {
|
||||||
"habitsLabel": "Hábitos",
|
"habitsLabel": "Hábitos",
|
||||||
|
|||||||
@@ -270,6 +270,12 @@
|
|||||||
"actionUpdate": "mise à jour",
|
"actionUpdate": "mise à jour",
|
||||||
"actionCreate": "création",
|
"actionCreate": "création",
|
||||||
"errorFailedUserAction": "Échec de la {action} de l'utilisateur",
|
"errorFailedUserAction": "Échec de la {action} de l'utilisateur",
|
||||||
|
"toastDemoDeleteDisabled": "La suppression est désactivée dans la version de démonstration",
|
||||||
|
"toastCannotDeleteSelf": "Vous ne pouvez pas supprimer votre propre compte",
|
||||||
|
"confirmDeleteUser": "Êtes-vous sûr de vouloir supprimer l'utilisateur {username}?",
|
||||||
|
"toastUserDeletedTitle": "Utilisateur supprimé",
|
||||||
|
"toastUserDeletedDescription": "L'utilisateur {username} a été supprimé avec succès",
|
||||||
|
"toastDeleteUserFailed": "Échec de la suppression de l'utilisateur : {error}",
|
||||||
"errorTitle": "Erreur",
|
"errorTitle": "Erreur",
|
||||||
"errorFileSizeLimit": "La taille du fichier doit être inférieure à 5MB",
|
"errorFileSizeLimit": "La taille du fichier doit être inférieure à 5MB",
|
||||||
"toastAvatarUploadedTitle": "Avatar téléchargé",
|
"toastAvatarUploadedTitle": "Avatar téléchargé",
|
||||||
@@ -287,7 +293,13 @@
|
|||||||
"disablePasswordLabel": "Désactiver le mot de passe",
|
"disablePasswordLabel": "Désactiver le mot de passe",
|
||||||
"cancelButton": "Annuler",
|
"cancelButton": "Annuler",
|
||||||
"saveChangesButton": "Sauvegarder les modifications",
|
"saveChangesButton": "Sauvegarder les modifications",
|
||||||
"createUserButton": "Créer un utilisateur"
|
"createUserButton": "Créer un utilisateur",
|
||||||
|
"deleteAccountButton": "Supprimer le compte",
|
||||||
|
"deletingButtonText": "Suppression en cours...",
|
||||||
|
"areYouSure": "Êtes-vous sûr ?",
|
||||||
|
"deleteUserConfirmation": "Êtes-vous sûr de vouloir supprimer l'utilisateur {username} ?",
|
||||||
|
"cancel": "Annuler",
|
||||||
|
"confirmDeleteButtonText": "Supprimer"
|
||||||
},
|
},
|
||||||
"ViewToggle": {
|
"ViewToggle": {
|
||||||
"habitsLabel": "Habitudes",
|
"habitsLabel": "Habitudes",
|
||||||
|
|||||||
@@ -270,6 +270,12 @@
|
|||||||
"actionUpdate": "更新",
|
"actionUpdate": "更新",
|
||||||
"actionCreate": "作成",
|
"actionCreate": "作成",
|
||||||
"errorFailedUserAction": "ユーザーの{action}に失敗しました",
|
"errorFailedUserAction": "ユーザーの{action}に失敗しました",
|
||||||
|
"toastDemoDeleteDisabled": "デモインスタンスでは削除が無効になっています",
|
||||||
|
"toastCannotDeleteSelf": "自分のアカウントは削除できません",
|
||||||
|
"confirmDeleteUser": "ユーザー {username} を削除してもよろしいですか?",
|
||||||
|
"toastUserDeletedTitle": "ユーザーが削除されました",
|
||||||
|
"toastUserDeletedDescription": "ユーザー {username} は正常に削除されました",
|
||||||
|
"toastDeleteUserFailed": "ユーザーの削除に失敗しました: {error}",
|
||||||
"errorTitle": "エラー",
|
"errorTitle": "エラー",
|
||||||
"errorFileSizeLimit": "ファイルサイズは5MB以下である必要があります",
|
"errorFileSizeLimit": "ファイルサイズは5MB以下である必要があります",
|
||||||
"toastAvatarUploadedTitle": "アバターをアップロードしました",
|
"toastAvatarUploadedTitle": "アバターをアップロードしました",
|
||||||
@@ -287,7 +293,13 @@
|
|||||||
"disablePasswordLabel": "パスワードを無効化",
|
"disablePasswordLabel": "パスワードを無効化",
|
||||||
"cancelButton": "キャンセル",
|
"cancelButton": "キャンセル",
|
||||||
"saveChangesButton": "変更を保存",
|
"saveChangesButton": "変更を保存",
|
||||||
"createUserButton": "ユーザーを作成"
|
"createUserButton": "ユーザーを作成",
|
||||||
|
"deleteAccountButton": "アカウントを削除",
|
||||||
|
"deletingButtonText": "削除中...",
|
||||||
|
"areYouSure": "本当によろしいですか?",
|
||||||
|
"deleteUserConfirmation": "ユーザー {username} を削除してもよろしいですか?",
|
||||||
|
"cancel": "キャンセル",
|
||||||
|
"confirmDeleteButtonText": "削除"
|
||||||
},
|
},
|
||||||
"ViewToggle": {
|
"ViewToggle": {
|
||||||
"habitsLabel": "習慣",
|
"habitsLabel": "習慣",
|
||||||
|
|||||||
@@ -270,6 +270,12 @@
|
|||||||
"actionUpdate": "обновить",
|
"actionUpdate": "обновить",
|
||||||
"actionCreate": "создать",
|
"actionCreate": "создать",
|
||||||
"errorFailedUserAction": "Не удалось {action} пользователя",
|
"errorFailedUserAction": "Не удалось {action} пользователя",
|
||||||
|
"toastDemoDeleteDisabled": "Удаление отключено в демо-версии",
|
||||||
|
"toastCannotDeleteSelf": "Вы не можете удалить свою учетную запись",
|
||||||
|
"confirmDeleteUser": "Вы уверены, что хотите удалить пользователя {username}?",
|
||||||
|
"toastUserDeletedTitle": "Пользователь удален",
|
||||||
|
"toastUserDeletedDescription": "Пользователь {username} успешно удален",
|
||||||
|
"toastDeleteUserFailed": "Не удалось удалить пользователя: {error}",
|
||||||
"errorTitle": "Ошибка",
|
"errorTitle": "Ошибка",
|
||||||
"errorFileSizeLimit": "Размер файла должен быть менее 5 МБ",
|
"errorFileSizeLimit": "Размер файла должен быть менее 5 МБ",
|
||||||
"toastAvatarUploadedTitle": "Аватар загружен",
|
"toastAvatarUploadedTitle": "Аватар загружен",
|
||||||
@@ -287,7 +293,13 @@
|
|||||||
"disablePasswordLabel": "Отключить пароль",
|
"disablePasswordLabel": "Отключить пароль",
|
||||||
"cancelButton": "Отмена",
|
"cancelButton": "Отмена",
|
||||||
"saveChangesButton": "Сохранить изменения",
|
"saveChangesButton": "Сохранить изменения",
|
||||||
"createUserButton": "Создать пользователя"
|
"createUserButton": "Создать пользователя",
|
||||||
|
"deleteAccountButton": "Удалить аккаунт",
|
||||||
|
"deletingButtonText": "Удаление...",
|
||||||
|
"areYouSure": "Вы уверены?",
|
||||||
|
"deleteUserConfirmation": "Вы уверены, что хотите удалить пользователя {username}?",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"confirmDeleteButtonText": "Удалить"
|
||||||
},
|
},
|
||||||
"ViewToggle": {
|
"ViewToggle": {
|
||||||
"habitsLabel": "Привычки",
|
"habitsLabel": "Привычки",
|
||||||
|
|||||||
@@ -270,6 +270,12 @@
|
|||||||
"actionUpdate": "更新",
|
"actionUpdate": "更新",
|
||||||
"actionCreate": "创建",
|
"actionCreate": "创建",
|
||||||
"errorFailedUserAction": "用户 {action} 失败",
|
"errorFailedUserAction": "用户 {action} 失败",
|
||||||
|
"toastDemoDeleteDisabled": "在演示实例中删除已禁用",
|
||||||
|
"toastCannotDeleteSelf": "您不能删除自己的帐户",
|
||||||
|
"confirmDeleteUser": "您确定要删除用户 {username} 吗?",
|
||||||
|
"toastUserDeletedTitle": "用户已删除",
|
||||||
|
"toastUserDeletedDescription": "用户 {username} 已成功删除",
|
||||||
|
"toastDeleteUserFailed": "删除用户失败: {error}",
|
||||||
"errorTitle": "错误",
|
"errorTitle": "错误",
|
||||||
"errorFileSizeLimit": "文件大小必须小于 5MB",
|
"errorFileSizeLimit": "文件大小必须小于 5MB",
|
||||||
"toastAvatarUploadedTitle": "头像已上传",
|
"toastAvatarUploadedTitle": "头像已上传",
|
||||||
@@ -287,7 +293,13 @@
|
|||||||
"disablePasswordLabel": "禁用密码",
|
"disablePasswordLabel": "禁用密码",
|
||||||
"cancelButton": "取消",
|
"cancelButton": "取消",
|
||||||
"saveChangesButton": "保存更改",
|
"saveChangesButton": "保存更改",
|
||||||
"createUserButton": "创建用户"
|
"createUserButton": "创建用户",
|
||||||
|
"deleteAccountButton": "删除账户",
|
||||||
|
"deletingButtonText": "正在删除...",
|
||||||
|
"areYouSure": "您确定吗?",
|
||||||
|
"deleteUserConfirmation": "您确定要删除用户 {username} 吗?",
|
||||||
|
"cancel": "取消",
|
||||||
|
"confirmDeleteButtonText": "删除"
|
||||||
},
|
},
|
||||||
"ViewToggle": {
|
"ViewToggle": {
|
||||||
"habitsLabel": "习惯",
|
"habitsLabel": "习惯",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "habittrove",
|
"name": "habittrove",
|
||||||
"version": "0.2.15",
|
"version": "0.2.16",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
|
|||||||
Reference in New Issue
Block a user