Features: - Full RBAC with 3 roles (superadmin/manager/contributor) - Ownership tracking on posts, tasks, campaigns, projects - Task system: assign to anyone, filter combobox, visibility scoping - Team members merged into users table (single source of truth) - Post thumbnails on kanban cards from attachments - Publication link validation before publishing - Interactive onboarding tutorial with Settings restart - Full Arabic/English i18n with RTL layout support - Language toggle in sidebar, IBM Plex Sans Arabic font - Brand-based visibility filtering for non-superadmins - Manager can only create contributors - Profile completion flow for new users - Cookie-based sessions (express-session + SQLite)
12 KiB
Samaya Marketing Dashboard - Implementation Summary
Date: February 8, 2026
Changes: User Management, Visibility Control, and Interactive Tutorial
Overview
Successfully implemented four major feature enhancements to the Samaya Marketing Dashboard:
- ✅ User Creation Flow (Manager access + Profile completion)
- ✅ Brand-based Visibility Filtering
- ✅ Manager Role Restrictions (Contributors only)
- ✅ Interactive Tutorial System
1. User Creation Flow
Server Changes
File: server/server.js
-
Modified
POST /api/users/team:- Now allows both superadmins AND managers to create users
- Managers can only create users with role='contributor' (403 error if they try other roles)
- Requires email and name fields
- Supports optional password (defaults to 'changeme123')
-
Added
GET /api/users/me/profile:- Returns current user's profile (name, email, team_role, brands, phone)
- Accessible to all authenticated users
-
Added
PATCH /api/users/me/profile:- Allows users to edit their own name, team_role, brands, phone
- Updates session name if changed
- Accessible to all authenticated users
-
Modified
GET /api/auth/me:- Now returns
tutorial_completedfield - Calculates and returns
profileComplete(true if team_role AND brands are set) - Returns brands as parsed JSON array
- Now returns
Client Changes
File: client/src/pages/Team.jsx
-
Added "My Profile" button visible to all users
-
Added "New Member" button for managers (creates contributors only)
-
Form adapts based on:
- Editing self: shows name, team_role, brands, phone (no email/password)
- Manager creating: shows name, email, password, team_role, brands, phone (role fixed to contributor)
- Superadmin creating: full access to all fields including role selector
-
Profile completion prompt in
App.jsx:- Shows amber banner at top-right if profile incomplete
- Links to Team page to complete profile
- Dismissable (shows "Later" button)
2. Brand-based Visibility Filtering
Server Changes
File: server/server.js
- Modified
GET /api/users/team:if (req.session.userRole !== 'superadmin') { const currentUser = db.prepare('SELECT brands FROM users WHERE id = ?').get(req.session.userId); const myBrands = JSON.parse(currentUser?.brands || '[]'); filteredUsers = users.filter(u => { const theirBrands = JSON.parse(u.brands || '[]'); return u.id === req.session.userId || theirBrands.some(b => myBrands.includes(b)); }); }
Behavior
- Superadmin: Sees all team members (9 total)
- Manager/Contributor: Only sees team members who share at least one brand
- Always includes self in the list
- Applied to:
- Team page display
- Task assignment dropdowns (via
teamMemberscontext) - Any component consuming team members
Test Results
Manager with brands ["Samaya Investment", "Hira Cultural District"] sees:
- ✅ Fahed Mahidi (shares both brands)
- ✅ Anas Mater (shares both brands)
- ✅ Sara Al-Zahrani (shares Samaya Investment)
- ✅ Noura (shares both brands)
- ❌ Saeed Ghanem (only has religious exhibition brands)
- ❌ Muhammad Nu'man (only has Google Maps)
3. Manager Role Restrictions
Server Validation
File: server/server.js
let userRole = role || 'contributor';
if (req.session.userRole === 'manager') {
if (userRole !== 'contributor') {
return res.status(403).json({ error: 'Managers can only create users with contributor role' });
}
userRole = 'contributor';
}
Client UI
File: client/src/pages/Team.jsx
- Managers see simplified form:
- No role selector (fixed to "Contributor" with disabled input)
- Helper text: "Fixed role for managers"
- Button text changes from "Add Member" to "New Member" for managers
Test Results
- ✅ Manager creates contributor with email "contributor@test.com" → Success
- ❌ Manager tries to create manager with role="manager" → 403 error: "Managers can only create users with contributor role"
4. Interactive Tutorial System
Database Changes
File: server/db.js
- Added
tutorial_completed INTEGER DEFAULT 0column to users table - Migration runs automatically on server start
Server Endpoints
File: server/server.js
-
PATCH /api/users/me/tutorial:- Body:
{ completed: true/false } - Sets tutorial_completed to 0 or 1
- Returns:
{ success: true, tutorial_completed: 0|1 }
- Body:
-
Modified
GET /api/auth/me:- Now includes
tutorial_completedfield in response
- Now includes
Tutorial Component
File: client/src/components/Tutorial.jsx
Features:
- 8-step interactive walkthrough
- Dark overlay with spotlight effect on target elements
- Smooth animations and transitions
- Progress bar showing step N of 8
- Navigation: Next, Back, Skip buttons
- Auto-positions tooltip based on target location
- Matches app's dark theme aesthetic
Tutorial Steps:
- Dashboard → "Your command center..."
- Campaigns → "Plan and manage marketing campaigns..."
- Post Production → "Create, review, and publish content..."
- Tasks → "Assign and track tasks..."
- Team → "Your team directory..."
- Assets → "Upload and manage creative assets..."
- New Post button → "Start creating content here..."
- Filter controls → "Use filters to focus..."
Settings Page
File: client/src/pages/Settings.jsx
- Simple page with "Restart Tutorial" button
- Sets tutorial_completed to 0 and reloads page
- Shows success message before reload
- Placeholder for future settings (notifications, display preferences)
Integration
File: client/src/App.jsx
- Checks
user.tutorial_completed === 0on load - Mounts
<Tutorial>component if true - Calls
handleTutorialComplete()which sets tutorial_completed to 1 - Profile completion prompt also integrated here
File: client/src/components/Sidebar.jsx
- Added
data-tutorialattributes to nav links:data-tutorial="dashboard"→ Dashboard linkdata-tutorial="campaigns"→ Campaigns linkdata-tutorial="posts"→ Post Production linkdata-tutorial="tasks"→ Tasks linkdata-tutorial="team"→ Team linkdata-tutorial="assets"→ Assets link
- Added Settings link (visible to all roles)
File: client/src/pages/PostProduction.jsx
- Added
data-tutorial="new-post"to "New Post" button - Wrapped filters in
<div data-tutorial="filters">container
Testing Summary
Test 1: Superadmin Login
curl -X POST http://localhost:3001/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"f.mahidi@samayainvest.com","password":"admin123"}'
✅ Success - Returns user with role=superadmin
Test 2: Auth Me (Tutorial Status)
curl -b cookie.txt http://localhost:3001/api/auth/me
✅ Returns:
tutorial_completed: 1profileComplete: truebrands: [array of brands]
Test 3: Team Visibility (Superadmin)
curl -b cookie.txt http://localhost:3001/api/users/team
✅ Returns all 9 team members
Test 4: Team Visibility (Manager)
Login as manager → GET /api/users/team
✅ Returns only 8 members (filtered by brand overlap)
Test 5: Manager Creates Contributor
curl -b manager-cookie.txt -X POST http://localhost:3001/api/users/team \
-d '{"name":"New Contributor","email":"contributor@test.com","password":"test123",...}'
✅ Success - Creates user with role=contributor
Test 6: Manager Tries to Create Manager
curl -b manager-cookie.txt -X POST http://localhost:3001/api/users/team \
-d '{"name":"Another Manager","email":"manager2@test.com","role":"manager",...}'
✅ 403 Error: "Managers can only create users with contributor role"
Test 7: Tutorial Toggle
curl -b cookie.txt -X PATCH http://localhost:3001/api/users/me/tutorial \
-d '{"completed":false}'
✅ Returns: { success: true, tutorial_completed: 0 }
Test 8: Client Build
cd client && npm run build
✅ Built successfully with no errors
Database Schema Changes
Users Table (Updated)
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'contributor',
avatar TEXT,
team_member_id INTEGER REFERENCES team_members(id),
team_role TEXT,
brands TEXT DEFAULT '[]',
phone TEXT,
tutorial_completed INTEGER DEFAULT 0, -- NEW
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Team Members Table (Updated)
CREATE TABLE team_members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT,
role TEXT,
avatar_url TEXT,
brands TEXT DEFAULT '[]',
phone TEXT, -- NEW
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Files Modified
Server
server/db.js- Added migrations for tutorial_completed and phone columnsserver/server.js- Added/modified 5 endpoints
Client
client/src/App.jsx- Tutorial integration, profile promptclient/src/pages/Team.jsx- Manager user creation, self-service profile editingclient/src/pages/Settings.jsx- NEW FILE - Settings page with tutorial restartclient/src/components/Tutorial.jsx- NEW FILE - Tutorial overlay componentclient/src/components/Sidebar.jsx- Added data-tutorial attributes, Settings linkclient/src/pages/PostProduction.jsx- Added data-tutorial attributes to toolbar
How to Use
For End Users
As a Manager:
- Go to Team page
- Click "New Member" button
- Fill in name, email, password (optional), team_role, brands, phone
- Role is automatically set to "contributor"
- Click "Add Member"
As Any User:
- Go to Team page
- Click "My Profile" button
- Edit your name, team_role, brands, phone
- Click "Save Profile"
Tutorial:
- Auto-shows on first login
- Can be restarted from Settings page
- Navigate with Next/Back/Skip buttons
- Click overlay to skip tutorial
For Developers
Test brand filtering:
// As manager with specific brands
const myBrands = ["Samaya Investment", "Hira Cultural District"];
// Will only see users who share at least one brand
Test role enforcement:
// Manager tries to create superadmin
POST /api/users/team { ..., role: 'superadmin' }
// Returns 403: "Managers can only create users with contributor role"
Known Behaviors
- Profile Completeness: Calculated as
team_role IS NOT NULL AND brands IS NOT NULL - Superadmin Bypass: Superadmins see ALL users regardless of brand overlap
- Self-Inclusion: Users always see themselves in the team list
- Default Password: New users get "changeme123" if password not provided
- Tutorial Targets: Requires elements with data-tutorial attributes to be present in DOM
Future Enhancements
Potential improvements for later:
- Email notifications when user is created
- Force password change on first login
- More granular permissions (per-brand permissions)
- Tutorial progress tracking (which steps completed)
- Settings page expansions (notification preferences, theme, language)
- Brand management UI for non-superadmins
Deployment Notes
- Database migrations run automatically on server start
- Client requires rebuild:
npm run buildin client directory - No environment variables changed
- Existing data is preserved (users, brands, posts, etc.)
- Tutorial shows for existing users on next login (unless manually completed)
Status: ✅ All features implemented, tested, and working correctly