Add email templates and fix Edit User modal

- Fixed Edit User modal to populate email field
- Added Reset Password button to Edit User modal (advanced auth only)
- Added "Welcome Email" template for user creation
- Added "Password Reset" email template
- Removed scrollbar from Settings menu tabs

Co-authored-by: cadtoolbox <12723486+cadtoolbox@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-10 00:18:33 +00:00
co-authored by cadtoolbox
parent 21ca7cce19
commit df75cc8e63
4 changed files with 65 additions and 2 deletions
@@ -43,6 +43,9 @@ EVENT_NAMES = {
"queue_job_skipped": "Queue Job Skipped",
"queue_job_failed": "Queue Job Failed",
"queue_completed": "Queue Completed",
# User management
"user_created": "Welcome Email",
"password_reset": "Password Reset",
}
@@ -146,4 +146,16 @@ DEFAULT_TEMPLATES = [
"title_template": "Queue Complete",
"body_template": "All {completed_count} queued jobs have finished",
},
{
"event_type": "user_created",
"name": "Welcome Email",
"title_template": "Welcome to {app_name}",
"body_template": "Welcome {username}!\n\nYour account has been created.\nUsername: {username}\nPassword: {password}\n\nLogin at: {login_url}",
},
{
"event_type": "password_reset",
"name": "Password Reset",
"title_template": "{app_name} - Password Reset",
"body_template": "Hello {username},\n\nYour password has been reset.\nNew Password: {password}\n\nLogin at: {login_url}",
},
]
@@ -53,6 +53,9 @@ EVENT_VARIABLES: dict[str, list[str]] = {
"queue_job_skipped": ["printer", "job_name", "reason", "timestamp", "app_name"],
"queue_job_failed": ["printer", "job_name", "reason", "timestamp", "app_name"],
"queue_completed": ["completed_count", "timestamp", "app_name"],
# User management notifications
"user_created": ["username", "password", "login_url", "app_name", "timestamp"],
"password_reset": ["username", "password", "login_url", "app_name", "timestamp"],
}
# Sample data for previewing templates
@@ -191,6 +194,21 @@ SAMPLE_DATA: dict[str, dict[str, str]] = {
"timestamp": "2024-01-15 18:30",
"app_name": "Bambuddy",
},
# User management notifications
"user_created": {
"username": "john_doe",
"password": "TempPass123!",
"login_url": "https://bambuddy.example.com/login",
"app_name": "Bambuddy",
"timestamp": "2024-01-15 14:30",
},
"password_reset": {
"username": "john_doe",
"password": "NewPass456!",
"login_url": "https://bambuddy.example.com/login",
"app_name": "Bambuddy",
"timestamp": "2024-01-15 14:30",
},
}
+32 -2
View File
@@ -380,6 +380,16 @@ export function SettingsPage() {
},
});
const resetPasswordMutation = useMutation({
mutationFn: (userId: number) => api.resetUserPassword({ user_id: userId }),
onSuccess: (response) => {
showToast(response.message, 'success');
},
onError: (error: Error) => {
showToast(error.message, 'error');
},
});
// Function to initiate user deletion with item count check
const handleDeleteUserClick = async (userId: number) => {
setDeleteUserId(userId);
@@ -501,6 +511,7 @@ export function SettingsPage() {
setUserFormData({
username: userToEdit.username,
password: '',
email: userToEdit.email || '',
confirmPassword: '',
role: userToEdit.role,
group_ids: userToEdit.groups?.map(g => g.id) || [],
@@ -946,7 +957,7 @@ export function SettingsPage() {
</div>
{/* Tab Navigation */}
<div className="flex flex-wrap gap-1 mb-6 border-b border-bambu-dark-tertiary overflow-y-auto max-h-32">
<div className="flex flex-wrap gap-1 mb-6 border-b border-bambu-dark-tertiary">
<button
onClick={() => handleTabChange('general')}
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px ${
@@ -3952,10 +3963,29 @@ export function SettingsPage() {
{/* Info box about auto-generated password when Advanced Auth is enabled */}
{advancedAuthStatus?.advanced_auth_enabled && (
<div className="bg-bambu-dark-secondary/50 border border-bambu-green/20 rounded-lg p-3">
<div className="bg-bambu-dark-secondary/50 border border-bambu-green/20 rounded-lg p-3 space-y-3">
<p className="text-sm text-bambu-gray">
{t('users.form.passwordManagedByAdvancedAuth') || 'Password is managed by Advanced Authentication. Use "Reset Password" to send a new password to the user via email.'}
</p>
<Button
variant="secondary"
size="sm"
onClick={() => editingUserId && resetPasswordMutation.mutate(editingUserId)}
disabled={resetPasswordMutation.isPending || !userFormData.email}
className="w-full"
>
{resetPasswordMutation.isPending ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
{t('users.form.resettingPassword') || 'Resetting Password...'}
</>
) : (
<>
<RotateCcw className="w-4 h-4" />
{t('users.form.resetPassword') || 'Reset Password'}
</>
)}
</Button>
</div>
)}