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)
383 lines
12 KiB
Markdown
383 lines
12 KiB
Markdown
# 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:
|
|
|
|
1. ✅ User Creation Flow (Manager access + Profile completion)
|
|
2. ✅ Brand-based Visibility Filtering
|
|
3. ✅ Manager Role Restrictions (Contributors only)
|
|
4. ✅ 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_completed` field
|
|
- Calculates and returns `profileComplete` (true if team_role AND brands are set)
|
|
- Returns brands as parsed JSON array
|
|
|
|
### 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`**:
|
|
```javascript
|
|
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 `teamMembers` context)
|
|
- 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`
|
|
|
|
```javascript
|
|
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 0` column to users table
|
|
- Migration runs automatically on server start
|
|
|
|
### Server Endpoints
|
|
**File**: `server/server.js`
|
|
|
|
1. **`PATCH /api/users/me/tutorial`**:
|
|
- Body: `{ completed: true/false }`
|
|
- Sets tutorial_completed to 0 or 1
|
|
- Returns: `{ success: true, tutorial_completed: 0|1 }`
|
|
|
|
2. **Modified `GET /api/auth/me`**:
|
|
- Now includes `tutorial_completed` field in response
|
|
|
|
### 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**:
|
|
1. **Dashboard** → "Your command center..."
|
|
2. **Campaigns** → "Plan and manage marketing campaigns..."
|
|
3. **Post Production** → "Create, review, and publish content..."
|
|
4. **Tasks** → "Assign and track tasks..."
|
|
5. **Team** → "Your team directory..."
|
|
6. **Assets** → "Upload and manage creative assets..."
|
|
7. **New Post button** → "Start creating content here..."
|
|
8. **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 === 0` on 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-tutorial` attributes to nav links:
|
|
- `data-tutorial="dashboard"` → Dashboard link
|
|
- `data-tutorial="campaigns"` → Campaigns link
|
|
- `data-tutorial="posts"` → Post Production link
|
|
- `data-tutorial="tasks"` → Tasks link
|
|
- `data-tutorial="team"` → Team link
|
|
- `data-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
|
|
```bash
|
|
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)
|
|
```bash
|
|
curl -b cookie.txt http://localhost:3001/api/auth/me
|
|
```
|
|
✅ Returns:
|
|
- `tutorial_completed: 1`
|
|
- `profileComplete: true`
|
|
- `brands: [array of brands]`
|
|
|
|
### Test 3: Team Visibility (Superadmin)
|
|
```bash
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
cd client && npm run build
|
|
```
|
|
✅ Built successfully with no errors
|
|
|
|
---
|
|
|
|
## Database Schema Changes
|
|
|
|
### Users Table (Updated)
|
|
```sql
|
|
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)
|
|
```sql
|
|
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 columns
|
|
- `server/server.js` - Added/modified 5 endpoints
|
|
|
|
### Client
|
|
- `client/src/App.jsx` - Tutorial integration, profile prompt
|
|
- `client/src/pages/Team.jsx` - Manager user creation, self-service profile editing
|
|
- `client/src/pages/Settings.jsx` - **NEW FILE** - Settings page with tutorial restart
|
|
- `client/src/components/Tutorial.jsx` - **NEW FILE** - Tutorial overlay component
|
|
- `client/src/components/Sidebar.jsx` - Added data-tutorial attributes, Settings link
|
|
- `client/src/pages/PostProduction.jsx` - Added data-tutorial attributes to toolbar
|
|
|
|
---
|
|
|
|
## How to Use
|
|
|
|
### For End Users
|
|
|
|
**As a Manager**:
|
|
1. Go to Team page
|
|
2. Click "New Member" button
|
|
3. Fill in name, email, password (optional), team_role, brands, phone
|
|
4. Role is automatically set to "contributor"
|
|
5. Click "Add Member"
|
|
|
|
**As Any User**:
|
|
1. Go to Team page
|
|
2. Click "My Profile" button
|
|
3. Edit your name, team_role, brands, phone
|
|
4. 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**:
|
|
```javascript
|
|
// 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**:
|
|
```javascript
|
|
// Manager tries to create superadmin
|
|
POST /api/users/team { ..., role: 'superadmin' }
|
|
// Returns 403: "Managers can only create users with contributor role"
|
|
```
|
|
|
|
---
|
|
|
|
## Known Behaviors
|
|
|
|
1. **Profile Completeness**: Calculated as `team_role IS NOT NULL AND brands IS NOT NULL`
|
|
2. **Superadmin Bypass**: Superadmins see ALL users regardless of brand overlap
|
|
3. **Self-Inclusion**: Users always see themselves in the team list
|
|
4. **Default Password**: New users get "changeme123" if password not provided
|
|
5. **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
|
|
|
|
1. Database migrations run automatically on server start
|
|
2. Client requires rebuild: `npm run build` in client directory
|
|
3. No environment variables changed
|
|
4. Existing data is preserved (users, brands, posts, etc.)
|
|
5. Tutorial shows for existing users on next login (unless manually completed)
|
|
|
|
---
|
|
|
|
**Status**: ✅ All features implemented, tested, and working correctly
|