feat: team-based visibility, roles management, unified users, UI fixes
Deploy / deploy (push) Successful in 12s
Deploy / deploy (push) Successful in 12s
- Add Roles table with CRUD routes and Settings page management - Unify user management: remove Users page, enhance Team page with permission level + role dropdowns - Add team-based visibility scoping to projects, campaigns, posts, tasks, issues, artefacts, and dashboard - Add team_id to projects and campaigns (create + edit forms) - Add getUserTeamIds/getUserVisibilityContext helpers - Fix Budgets modal horizontal scroll (separate linked-to row) - Add collapsible filter bar to PostProduction page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+17
-16
@@ -24,7 +24,7 @@ const Projects = lazy(() => import('./pages/Projects'))
|
||||
const ProjectDetail = lazy(() => import('./pages/ProjectDetail'))
|
||||
const Tasks = lazy(() => import('./pages/Tasks'))
|
||||
const Team = lazy(() => import('./pages/Team'))
|
||||
const Users = lazy(() => import('./pages/Users'))
|
||||
// Users page removed — unified into Team page
|
||||
const Settings = lazy(() => import('./pages/Settings'))
|
||||
const Brands = lazy(() => import('./pages/Brands'))
|
||||
const Login = lazy(() => import('./pages/Login'))
|
||||
@@ -37,18 +37,11 @@ const PublicIssueTracker = lazy(() => import('./pages/PublicIssueTracker'))
|
||||
const ForgotPassword = lazy(() => import('./pages/ForgotPassword'))
|
||||
const ResetPassword = lazy(() => import('./pages/ResetPassword'))
|
||||
|
||||
const TEAM_ROLES = [
|
||||
// Permission levels (access control)
|
||||
export const PERMISSION_LEVELS = [
|
||||
{ value: 'superadmin', label: 'Super Admin' },
|
||||
{ value: 'manager', label: 'Manager' },
|
||||
{ value: 'approver', label: 'Approver' },
|
||||
{ value: 'publisher', label: 'Publisher' },
|
||||
{ value: 'content_creator', label: 'Content Creator' },
|
||||
{ value: 'producer', label: 'Producer' },
|
||||
{ value: 'designer', label: 'Designer' },
|
||||
{ value: 'content_writer', label: 'Content Writer' },
|
||||
{ value: 'social_media_manager', label: 'Social Media Manager' },
|
||||
{ value: 'photographer', label: 'Photographer' },
|
||||
{ value: 'videographer', label: 'Videographer' },
|
||||
{ value: 'strategist', label: 'Strategist' },
|
||||
{ value: 'contributor', label: 'Contributor' },
|
||||
]
|
||||
|
||||
export const AppContext = createContext()
|
||||
@@ -59,6 +52,7 @@ function AppContent() {
|
||||
const [teamMembers, setTeamMembers] = useState([])
|
||||
const [brands, setBrands] = useState([])
|
||||
const [teams, setTeams] = useState([])
|
||||
const [roles, setRoles] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [showTutorial, setShowTutorial] = useState(false)
|
||||
const [showProfilePrompt, setShowProfilePrompt] = useState(false)
|
||||
@@ -115,12 +109,22 @@ function AppContent() {
|
||||
}
|
||||
}
|
||||
|
||||
const loadRoles = async () => {
|
||||
try {
|
||||
const data = await api.get('/roles')
|
||||
setRoles(Array.isArray(data) ? data : [])
|
||||
} catch (err) {
|
||||
console.error('Failed to load roles:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const loadInitialData = async () => {
|
||||
try {
|
||||
const [, brandsData] = await Promise.all([
|
||||
loadTeam(),
|
||||
api.get('/brands').then(d => Array.isArray(d) ? d : []).catch(() => []),
|
||||
loadTeams(),
|
||||
loadRoles(),
|
||||
])
|
||||
setBrands(brandsData)
|
||||
} catch (err) {
|
||||
@@ -151,7 +155,7 @@ function AppContent() {
|
||||
}
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{ currentUser: user, teamMembers, brands, loadTeam, getBrandName, teams, loadTeams }}>
|
||||
<AppContext.Provider value={{ currentUser: user, teamMembers, brands, loadTeam, getBrandName, teams, loadTeams, roles, loadRoles }}>
|
||||
{/* Profile completion prompt */}
|
||||
{showProfilePrompt && (
|
||||
<div className="fixed top-4 right-4 z-50 bg-amber-50 border-2 border-amber-400 rounded-xl shadow-lg p-4 max-w-md animate-fade-in">
|
||||
@@ -312,9 +316,6 @@ function AppContent() {
|
||||
{hasModule('issues') && <Route path="issues" element={<Issues />} />}
|
||||
<Route path="team" element={<Team />} />
|
||||
<Route path="settings" element={<Settings />} />
|
||||
{user?.role === 'superadmin' && (
|
||||
<Route path="users" element={<Users />} />
|
||||
)}
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
|
||||
Reference in New Issue
Block a user