Restrict team_role and brands to admin-only editing
All checks were successful
Deploy / deploy (push) Successful in 11s
All checks were successful
Deploy / deploy (push) Successful in 11s
- Remove team_role and brands from profile completion wizard - Lock team_role and brands fields when user edits own profile - Remove team_role and brands from PATCH /users/me/profile endpoint - Profile completeness now checks name instead of team_role Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -200,17 +200,6 @@ function AppContent() {
|
||||
placeholder={t('team.fullName')}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('team.teamRole')}</label>
|
||||
<select
|
||||
value={profileForm.team_role}
|
||||
onChange={e => setProfileForm(f => ({ ...f, team_role: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
|
||||
>
|
||||
<option value="">—</option>
|
||||
{TEAM_ROLES.map(r => <option key={r.value} value={r.value}>{r.label}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('team.phone')} {t('team.optional')}</label>
|
||||
<input
|
||||
@@ -220,16 +209,6 @@ function AppContent() {
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('team.brands')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={profileForm.brands}
|
||||
onChange={e => setProfileForm(f => ({ ...f, brands: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
|
||||
placeholder={t('team.brandsHelp')}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-3 pt-4 border-t border-border">
|
||||
<button
|
||||
onClick={() => setShowProfileModal(false)}
|
||||
@@ -241,15 +220,9 @@ function AppContent() {
|
||||
onClick={async () => {
|
||||
setProfileSaving(true)
|
||||
try {
|
||||
const brandsArr = profileForm.brands
|
||||
.split(',')
|
||||
.map(b => b.trim())
|
||||
.filter(Boolean)
|
||||
await api.patch('/users/me/profile', {
|
||||
name: profileForm.name,
|
||||
team_role: profileForm.team_role,
|
||||
phone: profileForm.phone || null,
|
||||
brands: brandsArr,
|
||||
})
|
||||
await checkAuth()
|
||||
setShowProfileModal(false)
|
||||
@@ -260,7 +233,7 @@ function AppContent() {
|
||||
setProfileSaving(false)
|
||||
}
|
||||
}}
|
||||
disabled={!profileForm.name || !profileForm.team_role || profileSaving}
|
||||
disabled={!profileForm.name || profileSaving}
|
||||
className="px-5 py-2 bg-brand-primary text-white rounded-lg text-sm font-medium hover:bg-brand-primary-light disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
|
||||
>
|
||||
{profileSaving ? t('common.loading') : t('team.saveProfile')}
|
||||
|
||||
@@ -235,7 +235,14 @@ export default function TeamMemberPanel({ member, isEditingSelf, onClose, onSave
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('team.teamRole')}</label>
|
||||
{userRole === 'manager' && isCreateMode && !isEditingSelf ? (
|
||||
{isEditingSelf ? (
|
||||
<input
|
||||
type="text"
|
||||
value={ROLES.find(r => r.value === form.role)?.label || form.role || '—'}
|
||||
disabled
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg bg-surface-tertiary text-text-tertiary cursor-not-allowed"
|
||||
/>
|
||||
) : userRole === 'manager' && isCreateMode ? (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
@@ -269,6 +276,11 @@ export default function TeamMemberPanel({ member, isEditingSelf, onClose, onSave
|
||||
|
||||
<div ref={brandsDropdownRef} className="relative">
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('team.brands')}</label>
|
||||
{isEditingSelf ? (
|
||||
<div className="w-full px-3 py-2 text-sm border border-border rounded-lg bg-surface-tertiary text-text-tertiary cursor-not-allowed">
|
||||
{(form.brands || []).length === 0 ? '—' : (form.brands || []).join(', ')}
|
||||
</div>
|
||||
) : <>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowBrandsDropdown(prev => !prev)}
|
||||
@@ -328,6 +340,7 @@ export default function TeamMemberPanel({ member, isEditingSelf, onClose, onSave
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>}
|
||||
</div>
|
||||
|
||||
{/* Modules toggle */}
|
||||
|
||||
@@ -43,8 +43,6 @@ export default function Team() {
|
||||
if (isEditingSelf) {
|
||||
await api.patch('/users/me/profile', {
|
||||
name: data.name,
|
||||
team_role: data.role,
|
||||
brands: data.brands,
|
||||
phone: data.phone,
|
||||
})
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user