Rename default staff roles: Manager, Support Staff, Staff

- Renamed "Full Access Staff" to "Manager"
- Renamed "Front Desk" to "Support Staff"
- Renamed "Limited Staff" to "Staff"
- Updated permissions: Support Staff now has access to messages and payments
- Updated all references in code, tests, help docs, and translations
- Added migration 0016 to rename existing roles in database

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-17 21:52:57 -05:00
parent 9a013ad968
commit 9848268d34
8 changed files with 128 additions and 54 deletions

View File

@@ -1979,7 +1979,7 @@
"deleteRole": "Delete Role",
"roleName": "Role Name",
"roleDescription": "Description",
"roleNamePlaceholder": "e.g., Front Desk",
"roleNamePlaceholder": "e.g., Support Staff",
"roleDescriptionPlaceholder": "Brief description of this role's responsibilities",
"permissions": "Permissions",
"menuAccess": "Menu Access",

View File

@@ -84,28 +84,28 @@ const HelpSettingsStaffRoles: React.FC = () => {
<div className="flex flex-col gap-2 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<div className="flex items-center gap-2">
<Shield size={20} className="text-green-500" />
<h4 className="font-medium text-gray-900 dark:text-white">Full Access Staff</h4>
<h4 className="font-medium text-gray-900 dark:text-white">Manager</h4>
</div>
<p className="text-sm text-gray-500 dark:text-gray-400">
Access to all menu items and all dangerous permissions. Best for managers and supervisors.
</p>
</div>
<div className="flex flex-col gap-2 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<div className="flex items-center gap-2">
<Shield size={20} className="text-blue-500" />
<h4 className="font-medium text-gray-900 dark:text-white">Limited Staff</h4>
</div>
<p className="text-sm text-gray-500 dark:text-gray-400">
Access to Scheduler, Customers, and Messages only. No dangerous permissions. Best for general staff.
Full access to all features and settings. Best for supervisors and team leads.
</p>
</div>
<div className="flex flex-col gap-2 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<div className="flex items-center gap-2">
<Shield size={20} className="text-purple-500" />
<h4 className="font-medium text-gray-900 dark:text-white">Front Desk</h4>
<h4 className="font-medium text-gray-900 dark:text-white">Support Staff</h4>
</div>
<p className="text-sm text-gray-500 dark:text-gray-400">
Access to Scheduler, Services, Customers, and Messages. No delete permissions. Best for reception.
Customer-facing operations including scheduling, customers, tickets, messages, and payments.
</p>
</div>
<div className="flex flex-col gap-2 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<div className="flex items-center gap-2">
<Shield size={20} className="text-blue-500" />
<h4 className="font-medium text-gray-900 dark:text-white">Staff</h4>
</div>
<p className="text-sm text-gray-500 dark:text-gray-400">
Basic access to own schedule and availability. Best for general staff members.
</p>
</div>
</div>
@@ -406,7 +406,7 @@ const HelpSettingsStaffRoles: React.FC = () => {
<div>
<h4 className="font-medium text-gray-900 dark:text-white">Start with Default Roles</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">
Use the built-in roles (Full Access, Limited, Front Desk) as templates when creating custom roles
Use the built-in roles (Manager, Support Staff, Staff) as templates when creating custom roles
</p>
</div>
</li>

View File

@@ -0,0 +1,72 @@
"""
Migration to rename default staff roles to more intuitive names.
Renames:
- "Full Access Staff" -> "Manager"
- "Front Desk" -> "Support Staff"
- "Limited Staff" -> "Staff"
"""
from django.db import migrations
ROLE_RENAMES = {
'Full Access Staff': 'Manager',
'Front Desk': 'Support Staff',
'Limited Staff': 'Staff',
}
ROLE_DESCRIPTIONS = {
'Manager': 'Full access to all features and settings',
'Support Staff': 'Customer-facing operations and scheduling',
'Staff': 'Basic access to own schedule and availability',
}
def rename_staff_roles(apps, schema_editor):
"""Rename default staff roles to new names."""
StaffRole = apps.get_model('users', 'StaffRole')
for old_name, new_name in ROLE_RENAMES.items():
StaffRole.objects.filter(
name=old_name,
is_default=True
).update(
name=new_name,
description=ROLE_DESCRIPTIONS.get(new_name, '')
)
def reverse_migration(apps, schema_editor):
"""Reverse: rename roles back to original names."""
StaffRole = apps.get_model('users', 'StaffRole')
reverse_renames = {v: k for k, v in ROLE_RENAMES.items()}
original_descriptions = {
'Full Access Staff': 'Complete access to all features (similar to manager)',
'Front Desk': 'Access to scheduling, customers, and basic operations',
'Limited Staff': 'Basic access to own schedule only',
}
for new_name, old_name in reverse_renames.items():
StaffRole.objects.filter(
name=new_name,
is_default=True
).update(
name=old_name,
description=original_descriptions.get(old_name, '')
)
class Migration(migrations.Migration):
dependencies = [
('users', '0015_update_staff_invitation_role_choices'),
]
operations = [
migrations.RunPython(
rename_staff_roles,
reverse_code=reverse_migration,
),
]

View File

@@ -26,7 +26,7 @@ class User(AbstractUser):
# Tenant-level roles (access within single tenant)
TENANT_OWNER = 'TENANT_OWNER', _('Tenant Owner')
# TENANT_MANAGER removed - use TENANT_STAFF with "Full Access Staff" role instead
# TENANT_MANAGER removed - use TENANT_STAFF with "Manager" role instead
TENANT_STAFF = 'TENANT_STAFF', _('Tenant Staff')
# Customer role (end users of the tenant)

View File

@@ -261,12 +261,12 @@ def get_default_permissions_for_role(role_name: str) -> dict:
# Default role configurations
# These are created for each tenant during migration and on new tenant creation
DEFAULT_ROLES = {
'Full Access Staff': {
'description': 'Complete access to all features (similar to manager)',
'Manager': {
'description': 'Full access to all features and settings',
'permissions': {k: True for k in ALL_PERMISSIONS.keys()},
},
'Front Desk': {
'description': 'Access to scheduling, customers, and basic operations',
'Support Staff': {
'description': 'Customer-facing operations and scheduling',
'permissions': {
'can_access_dashboard': True,
'can_access_scheduler': True,
@@ -274,11 +274,13 @@ DEFAULT_ROLES = {
'can_access_my_availability': True,
'can_access_customers': True,
'can_access_tickets': True,
'can_access_messages': True,
'can_access_payments': True,
'can_cancel_appointments': True,
},
},
'Limited Staff': {
'description': 'Basic access to own schedule only',
'Staff': {
'description': 'Basic access to own schedule and availability',
'permissions': {
'can_access_dashboard': True,
'can_access_my_schedule': True,

View File

@@ -285,27 +285,27 @@ class TestDefaultRoles:
"""Default roles are defined"""
from smoothschedule.identity.users.staff_permissions import DEFAULT_ROLES
assert 'Full Access Staff' in DEFAULT_ROLES
assert 'Front Desk' in DEFAULT_ROLES
assert 'Limited Staff' in DEFAULT_ROLES
assert 'Manager' in DEFAULT_ROLES
assert 'Support Staff' in DEFAULT_ROLES
assert 'Staff' in DEFAULT_ROLES
def test_full_access_has_all_permissions(self):
"""Full Access Staff has all permissions enabled"""
def test_manager_has_all_permissions(self):
"""Manager has all permissions enabled"""
from smoothschedule.identity.users.staff_permissions import DEFAULT_ROLES, ALL_PERMISSIONS
full_access = DEFAULT_ROLES['Full Access Staff']
permissions = full_access['permissions']
manager = DEFAULT_ROLES['Manager']
permissions = manager['permissions']
for key in ALL_PERMISSIONS.keys():
assert key in permissions, f"Missing permission: {key}"
assert permissions[key] is True, f"Permission not enabled: {key}"
def test_limited_staff_has_basic_permissions(self):
"""Limited Staff has only basic permissions"""
def test_staff_has_basic_permissions(self):
"""Staff has only basic permissions"""
from smoothschedule.identity.users.staff_permissions import DEFAULT_ROLES
limited = DEFAULT_ROLES['Limited Staff']
permissions = limited['permissions']
staff = DEFAULT_ROLES['Staff']
permissions = staff['permissions']
# Should have basic permissions
assert permissions.get('can_access_dashboard') is True

View File

@@ -274,18 +274,18 @@ class Command(BaseCommand):
if created:
manager.set_password("test123")
manager.save()
# Assign Full Access Staff role
full_access_role = StaffRole.objects.filter(
# Assign Manager role
manager_role = StaffRole.objects.filter(
tenant=tenant,
name="Full Access Staff"
name="Manager"
).first()
if full_access_role and manager.staff_role != full_access_role:
manager.staff_role = full_access_role
if manager_role and manager.staff_role != manager_role:
manager.staff_role = manager_role
manager.save(update_fields=['staff_role'])
users["manager"] = manager
if not self.quiet:
status = self.style.SUCCESS("CREATED") if created else self.style.WARNING("EXISTS")
self.stdout.write(f" {status} {manager.email} (Full Access Staff)")
self.stdout.write(f" {status} {manager.email} (Manager)")
# Staff members (stylists and spa therapists)
staff_data = [
@@ -716,17 +716,17 @@ class Command(BaseCommand):
"""Assign staff roles to demo staff members."""
staff_users = tenant_users.get("staff", [])
# Role assignments: first gets Full Access, some get Front Desk, rest get Limited
# Role assignments: mix of Manager, Support Staff, and Staff roles
role_assignments = {
0: "Full Access Staff", # Sophia
1: "Front Desk", # Emma
2: "Limited Staff", # Olivia
3: "Front Desk", # Isabella
4: "Limited Staff", # Mia
0: "Manager", # Sophia - Senior Stylist gets manager role
1: "Support Staff", # Emma - handles front desk duties
2: "Staff", # Olivia - basic access
3: "Support Staff", # Isabella - handles customers
4: "Staff", # Mia - basic access
}
for i, user in enumerate(staff_users):
role_name = role_assignments.get(i, "Limited Staff")
role_name = role_assignments.get(i, "Staff")
try:
# Get tenant from user
if user.tenant:

View File

@@ -259,7 +259,7 @@ class Command(BaseCommand):
"last_name": "Manager",
"tenant": tenant,
"phone": "555-100-0002",
"_assign_full_access": True, # Flag to assign Full Access Staff role
"_assign_manager_role": True, # Flag to assign Manager role
},
{
"username": "staff@demo.com",
@@ -277,7 +277,7 @@ class Command(BaseCommand):
manager_user = None # Track manager user separately
for user_data in tenant_users:
password = user_data.pop("password")
assign_full_access = user_data.pop("_assign_full_access", False)
assign_manager_role = user_data.pop("_assign_manager_role", False)
user, created = User.objects.get_or_create(
username=user_data["username"],
defaults=user_data,
@@ -289,14 +289,14 @@ class Command(BaseCommand):
else:
status = self.style.WARNING("EXISTS")
# Assign Full Access Staff role if flagged
if assign_full_access:
full_access_role = StaffRole.objects.filter(
# Assign Manager role if flagged
if assign_manager_role:
manager_role = StaffRole.objects.filter(
tenant=tenant,
name="Full Access Staff"
name="Manager"
).first()
if full_access_role and user.staff_role != full_access_role:
user.staff_role = full_access_role
if manager_role and user.staff_role != manager_role:
user.staff_role = manager_role
user.save(update_fields=['staff_role'])
manager_user = user # Track for resource creation