feat: add advanced analytics and territory management system
- Add comprehensive analytics components with export functionality - Implement territory management with manager performance tracking - Add seatmap components for venue layout management - Create customer management features with modal interface - Add advanced hooks for dashboard flags and territory data - Implement seat selection and venue management utilities - Add type definitions for ticketing and seatmap systems 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
345
reactrebuild0825/README-TERRITORY-MANAGERS.md
Normal file
345
reactrebuild0825/README-TERRITORY-MANAGERS.md
Normal file
@@ -0,0 +1,345 @@
|
||||
# Territory Managers
|
||||
|
||||
This document explains the Territory Manager system implemented in Black Canyon Tickets React Rebuild. This system provides role-based access control that restricts users to specific geographic or organizational territories.
|
||||
|
||||
## Overview
|
||||
|
||||
The Territory Manager system introduces a new user role with limited access permissions based on assigned territories. This enables organizations to segment their operations geographically or by business unit while maintaining centralized management.
|
||||
|
||||
## User Roles
|
||||
|
||||
### Role Hierarchy
|
||||
1. **Super Admin** - Platform-wide access across all organizations
|
||||
2. **Org Admin** - Full access within their organization, can assign territories
|
||||
3. **Territory Manager** - Limited to assigned territories within their organization
|
||||
4. **Staff** - Organization-wide read access (can be narrowed in future)
|
||||
|
||||
### Territory Manager Capabilities
|
||||
- View and manage events only in assigned territories
|
||||
- Create new events (must assign to accessible territory)
|
||||
- View tickets and customers for accessible events
|
||||
- Cannot modify events outside their territories
|
||||
- Cannot assign territories to other users
|
||||
|
||||
## Architecture
|
||||
|
||||
### Data Model
|
||||
|
||||
#### Firestore Collections
|
||||
```typescript
|
||||
// territories/{territoryId}
|
||||
interface Territory {
|
||||
id: string;
|
||||
orgId: string;
|
||||
name: string; // "West Northwest"
|
||||
code: string; // "WNW"
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// events/{eventId}
|
||||
interface Event {
|
||||
// ... existing fields
|
||||
organizationId: string;
|
||||
territoryId: string; // Required field
|
||||
}
|
||||
|
||||
// ticket_types/{ticketTypeId}
|
||||
interface TicketType {
|
||||
// ... existing fields
|
||||
territoryId: string; // Inherited from event
|
||||
}
|
||||
|
||||
// tickets/{ticketId}
|
||||
interface Ticket {
|
||||
// ... existing fields
|
||||
territoryId: string; // Inherited from event
|
||||
}
|
||||
|
||||
// users/{uid} - Mirror of custom claims for UI
|
||||
interface User {
|
||||
orgId: string;
|
||||
role: 'superadmin' | 'orgAdmin' | 'territoryManager' | 'staff';
|
||||
territoryIds: string[];
|
||||
}
|
||||
```
|
||||
|
||||
#### Firebase Custom Claims
|
||||
```typescript
|
||||
interface CustomClaims {
|
||||
orgId: string;
|
||||
role: 'superadmin' | 'orgAdmin' | 'territoryManager' | 'staff';
|
||||
territoryIds: string[]; // Empty array for full access roles
|
||||
}
|
||||
```
|
||||
|
||||
### Security Implementation
|
||||
|
||||
#### Firestore Security Rules
|
||||
Access is controlled at the database level using custom claims:
|
||||
|
||||
```javascript
|
||||
// Events collection
|
||||
allow read: if canReadTerritory(resource.data.orgId, resource.data.territoryId);
|
||||
allow write: if territoryOK(request.resource.data.orgId, request.resource.data.territoryId);
|
||||
|
||||
function territoryOK(resOrgId, resTerritoryId) {
|
||||
return inOrg(resOrgId) && (
|
||||
request.auth.token.role in ['superadmin', 'orgAdmin'] ||
|
||||
(request.auth.token.role == 'territoryManager' &&
|
||||
(resTerritoryId in request.auth.token.territoryIds))
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### API Authorization
|
||||
Cloud Functions validate claims before processing requests:
|
||||
|
||||
```typescript
|
||||
// functions/src/claims.ts
|
||||
function canManageClaims(user: AuthorizedUser, targetOrgId: string): boolean {
|
||||
if (user.role === 'superadmin') return true;
|
||||
if (user.role === 'orgAdmin' && user.orgId === targetOrgId) return true;
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
## Frontend Implementation
|
||||
|
||||
### Components
|
||||
|
||||
#### TerritoryFilter
|
||||
Role-based filtering component:
|
||||
```typescript
|
||||
// Territory managers: fixed to assigned territories
|
||||
// Admins: multi-select all org territories
|
||||
// Persists selection in URL params and localStorage
|
||||
<TerritoryFilter
|
||||
selectedTerritoryIds={selectedIds}
|
||||
onSelectionChange={setSelectedIds}
|
||||
/>
|
||||
```
|
||||
|
||||
#### UserTerritoryManager
|
||||
Admin interface for assigning territories:
|
||||
```typescript
|
||||
// Only visible to superadmin and orgAdmin
|
||||
// Updates Firebase custom claims
|
||||
// Provides visual feedback for claim changes
|
||||
<UserTerritoryManager />
|
||||
```
|
||||
|
||||
#### Event Creation
|
||||
Territory selection is mandatory:
|
||||
```typescript
|
||||
// EventDetailsStep includes territory dropdown
|
||||
// Auto-selects for territory managers with single territory
|
||||
// Validates territory access before save
|
||||
```
|
||||
|
||||
### Hooks
|
||||
|
||||
#### useClaims
|
||||
Access Firebase custom claims:
|
||||
```typescript
|
||||
const { claims, loading, error, refreshClaims } = useClaims();
|
||||
// claims.orgId, claims.role, claims.territoryIds
|
||||
```
|
||||
|
||||
#### useTerritoryEvents
|
||||
Territory-filtered event access:
|
||||
```typescript
|
||||
const {
|
||||
events, // Filtered by territory access
|
||||
canAccessEvent, // Check event permissions
|
||||
canModifyEvent, // Check edit permissions
|
||||
createEvent // Validates territory on create
|
||||
} = useTerritoryEvents();
|
||||
```
|
||||
|
||||
#### useTerritoryFilter
|
||||
Filter state management:
|
||||
```typescript
|
||||
const {
|
||||
selectedTerritoryIds,
|
||||
isActive,
|
||||
canModifySelection, // False for territory managers
|
||||
setSelectedTerritories
|
||||
} = useTerritoryFilter();
|
||||
```
|
||||
|
||||
## Usage Guide
|
||||
|
||||
### For Administrators
|
||||
|
||||
#### Assigning Territories
|
||||
1. Navigate to Admin panel
|
||||
2. Use **UserTerritoryManager** component
|
||||
3. Select user and role
|
||||
4. Choose territories (required for territory managers)
|
||||
5. Save - user must re-login to see changes
|
||||
|
||||
#### Creating Territories
|
||||
```typescript
|
||||
// Add to MOCK_TERRITORIES for development
|
||||
// In production, create via admin interface
|
||||
const territory = {
|
||||
id: 'territory_004',
|
||||
orgId: 'org_001',
|
||||
name: 'Southwest Region',
|
||||
code: 'SW',
|
||||
description: 'Arizona, Nevada operations'
|
||||
};
|
||||
```
|
||||
|
||||
### For Territory Managers
|
||||
|
||||
#### Event Management
|
||||
- Events list automatically filtered to assigned territories
|
||||
- Create events by selecting accessible territory
|
||||
- Edit/delete only events in assigned territories
|
||||
- Territory filter is read-only
|
||||
|
||||
#### Dashboard Views
|
||||
- Revenue and analytics scoped to accessible territories
|
||||
- Customer data limited to accessible events
|
||||
- Reporting reflects territorial scope
|
||||
|
||||
### For Developers
|
||||
|
||||
#### Testing Territory Access
|
||||
Run comprehensive test suite:
|
||||
```bash
|
||||
npm run test:territory # Territory-specific tests
|
||||
npx playwright test tests/territory-access.spec.ts
|
||||
```
|
||||
|
||||
#### Adding New Territory-Scoped Features
|
||||
1. Update data models to include `territoryId`
|
||||
2. Apply filtering in query hooks
|
||||
3. Add territory validation to mutations
|
||||
4. Update Firestore security rules
|
||||
5. Add tests for access control
|
||||
|
||||
## API Reference
|
||||
|
||||
### Cloud Functions
|
||||
|
||||
#### Update User Claims
|
||||
```http
|
||||
POST /api/admin/users/:uid/claims
|
||||
Authorization: Bearer <firebase-id-token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"orgId": "org_001",
|
||||
"role": "territoryManager",
|
||||
"territoryIds": ["territory_001", "territory_002"]
|
||||
}
|
||||
```
|
||||
|
||||
#### Get User Claims (Debug)
|
||||
```http
|
||||
GET /api/admin/users/:uid/claims
|
||||
Authorization: Bearer <firebase-id-token>
|
||||
```
|
||||
|
||||
### Frontend API
|
||||
|
||||
#### Territory Filtering
|
||||
```typescript
|
||||
// Apply territory filter to queries
|
||||
const events = useTerritoryEvents();
|
||||
const filteredEvents = events.getFilteredEvents();
|
||||
|
||||
// Check specific access
|
||||
const canAccess = events.canAccessEvent(event);
|
||||
const canModify = events.canModifyEvent(event);
|
||||
```
|
||||
|
||||
#### Claims Management
|
||||
```typescript
|
||||
// Access current user claims
|
||||
const { claims } = useClaims();
|
||||
if (claims?.role === 'territoryManager') {
|
||||
// Territory manager specific logic
|
||||
}
|
||||
|
||||
// Refresh claims after admin changes
|
||||
await refreshClaims();
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Custom Claims Best Practices
|
||||
- Claims are authoritative - UI mirrors but never overrides
|
||||
- Claims update immediately in security rules
|
||||
- UI requires re-login to reflect claim changes
|
||||
- Validate claims in all API endpoints
|
||||
|
||||
### Access Control Validation
|
||||
- Database rules enforce access at data layer
|
||||
- Frontend hooks provide optimistic filtering
|
||||
- API endpoints validate claims before operations
|
||||
- Test both UI and database rule enforcement
|
||||
|
||||
### Territory Assignment Security
|
||||
- Only superadmin/orgAdmin can assign territories
|
||||
- Territory managers cannot escalate privileges
|
||||
- Cross-organization access strictly prohibited
|
||||
- Audit trail maintained in users collection
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### User Claims Not Updating
|
||||
- Claims update immediately in Firestore security rules
|
||||
- UI updates require user to re-login
|
||||
- Check ID token refresh in browser dev tools
|
||||
- Verify Cloud Function deployment
|
||||
|
||||
#### Territory Filter Not Working
|
||||
- Check URL parameters: `?territories=territory_001,territory_002`
|
||||
- Verify localStorage: `territory-filter-${orgId}`
|
||||
- Ensure user has access to selected territories
|
||||
- Check browser console for access errors
|
||||
|
||||
#### Events Not Visible
|
||||
- Verify event has correct `territoryId`
|
||||
- Check user's assigned territories in claims
|
||||
- Confirm organization ID matches
|
||||
- Test with admin account for comparison
|
||||
|
||||
### Debug Commands
|
||||
```typescript
|
||||
// Check current claims (browser console)
|
||||
firebase.auth().currentUser?.getIdTokenResult()
|
||||
.then(result => console.log(result.claims));
|
||||
|
||||
// Verify territory access
|
||||
const { claims } = useClaims();
|
||||
const { accessibleTerritoryIds } = useAccessibleTerritories();
|
||||
console.log({ claims, accessibleTerritoryIds });
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
- Dynamic territory creation via UI
|
||||
- Territory-based email notifications
|
||||
- Advanced reporting with territory breakdowns
|
||||
- Bulk territory assignment tools
|
||||
- Territory hierarchy (regions > territories)
|
||||
|
||||
### Possible Extensions
|
||||
- Time-based territory access
|
||||
- Territory sharing between users
|
||||
- Territory-specific branding
|
||||
- Integration with external mapping systems
|
||||
- Mobile app territory awareness
|
||||
|
||||
## Related Documentation
|
||||
- [Firebase Custom Claims Documentation](https://firebase.google.com/docs/auth/admin/custom-claims)
|
||||
- [Firestore Security Rules Guide](https://firebase.google.com/docs/firestore/security/get-started)
|
||||
- [CLAUDE.md](./CLAUDE.md) - Project overview and development guide
|
||||
- [REBUILD_PLAN.md](./REBUILD_PLAN.md) - Current project status
|
||||
Reference in New Issue
Block a user