mirror of
https://github.com/we-promise/sure.git
synced 2026-05-29 15:34:58 +00:00
* Add invited users with delete button to admin users page Shows pending invitations per family below active users in /admin/users/. Each invitation row has a red Delete button aligned with the role column. Alt/option-clicking any Delete button changes all invitation button labels to "Delete All" and destroys all pending invitations for that family. - Add admin routes: DELETE /admin/invitations/:id and DELETE /admin/families/:id/invitations - Add Admin::InvitationsController with destroy and destroy_all actions - Load pending invitations grouped by family in users controller index - Render invitation rows in a dashed-border tbody below active user rows - Add admin-invitation-delete Stimulus controller for alt-click behavior - Add i18n strings for invitation UI and flash messages https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Fix destroy_all using params[:id] from member route The member route /admin/families/:id/invitations sets params[:id], not params[:family_id], so Family.find was always receiving nil. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Fix translation key in destroy_all to match locale t(".success_all") looked up a nonexistent key; the locale defines admin.invitations.destroy_all.success, so t(".success") is correct. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Scope bulk delete to pending invitations and allow re-inviting emails - destroy_all now uses family.invitations.pending.destroy_all so accepted and expired invitation history is preserved - Replace blanket email uniqueness validation with a custom check scoped to pending invitations only, so the same email can be invited again after an invitation is deleted or expires https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Drop unconditional unique DB index on invitations(email, family_id) The model-level uniqueness check was already scoped to pending invitations, but the blanket unique index on (email, family_id) still caused ActiveRecord::RecordNotUnique when re-inviting an email that had any historical invitation record in the same family (e.g. after an accepted invite or after an account deletion). Replace it with no DB-level unique constraint — the no_duplicate_pending_invitation_in_family model validation is the sole enforcer and correctly scopes uniqueness to pending rows only. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Replace blanket unique index with partial unique index on pending invitations Instead of dropping the DB-level uniqueness constraint entirely, replace the unconditional unique index on (email, family_id) with a partial unique index scoped to WHERE accepted_at IS NULL. This enforces the invariant at the DB layer (no two non-accepted invitations for the same email in a family) while allowing re-invites once a prior invitation has been accepted. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Fix migration version and make remove_index reversible - Change Migration[8.0] to Migration[7.2] to match the rest of the codebase - Pass column names to remove_index so Rails can reconstruct the old index on rollback https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
54 lines
2.1 KiB
YAML
54 lines
2.1 KiB
YAML
---
|
|
en:
|
|
admin:
|
|
users:
|
|
index:
|
|
title: "User Management"
|
|
description: "Manage user roles for your instance. Super admins can access SSO provider settings and user management."
|
|
section_title: "Families / Groups"
|
|
you: "(You)"
|
|
trial_ends_at: "Trial ends"
|
|
not_available: "n/a"
|
|
no_users: "No users found."
|
|
unnamed_family: "Unnamed Family/Group"
|
|
no_subscription: "No subscription"
|
|
family_summary: "%{members} members · %{accounts} accounts · %{transactions} transactions"
|
|
filters:
|
|
role: "Role"
|
|
role_all: "All roles"
|
|
trial_status: "Trial status"
|
|
trial_all: "All"
|
|
trial_expiring_soon: "Expiring in 7 days"
|
|
trial_trialing: "On trial"
|
|
submit: "Filter"
|
|
summary:
|
|
trials_expiring_7_days: "Trials expiring in next 7 days"
|
|
table:
|
|
user: "User"
|
|
trial_ends_at: "Trial ends"
|
|
family_accounts: "Family accounts"
|
|
family_transactions: "Family transactions"
|
|
last_login: "Last login"
|
|
session_count: "Session count"
|
|
never: "Never"
|
|
role: "Role"
|
|
role_descriptions_title: "Role Descriptions"
|
|
roles:
|
|
guest: "Guest"
|
|
member: "Member"
|
|
admin: "Admin"
|
|
super_admin: "Super Admin"
|
|
role_descriptions:
|
|
guest: "Assistant-first experience with intentionally restricted permissions for intro workflows."
|
|
member: "Basic user access. Can manage their own accounts, transactions, and settings."
|
|
admin: "Family administrator. Can access advanced settings like API keys, imports, and AI prompts."
|
|
super_admin: "Instance administrator. Can manage SSO providers, user roles, and impersonate users for support."
|
|
invitations:
|
|
pending_label: "Invited (pending)"
|
|
expires: "Expires %{date}"
|
|
delete: "Delete"
|
|
delete_all: "Delete All"
|
|
update:
|
|
success: "User role updated successfully."
|
|
failure: "Failed to update user role."
|