feat: implement relational lead origins with team assignments
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m51s
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m51s
- Dropped simple origins table in favor of origin_groups and origin_items to match the Funnels architecture. - Added origin_group_id to teams table to assign specific origins to specific teams. - Updated /admin/origins page to support creating origin groups, adding origin items to them, and assigning teams to groups. - Updated Dashboard and UserDetail pages to dynamically load the exact origin items belonging to the active team/user.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { getAttendances, getUserById, getFunnels } from '../services/dataService';
|
||||
import { Attendance, User, FunnelStage, DashboardFilter, FunnelStageDef } from '../types';
|
||||
import { getAttendances, getUserById, getFunnels, getOrigins } from '../services/dataService';
|
||||
import { Attendance, User, FunnelStage, DashboardFilter, FunnelStageDef, OriginItemDef } from '../types';
|
||||
import { ArrowLeft, Mail, Phone, Clock, MessageSquare, ChevronLeft, ChevronRight, Eye, Filter } from 'lucide-react';
|
||||
import { DateRangePicker } from '../components/DateRangePicker';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const UserDetail: React.FC = () => {
|
||||
const [user, setUser] = useState<User | undefined>();
|
||||
const [attendances, setAttendances] = useState<Attendance[]>([]);
|
||||
const [funnelDefs, setFunnelDefs] = useState<FunnelStageDef[]>([]);
|
||||
const [originDefs, setOriginDefs] = useState<OriginItemDef[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [filters, setFilters] = useState<DashboardFilter>({
|
||||
@@ -32,12 +33,13 @@ export const UserDetail: React.FC = () => {
|
||||
setUser(u);
|
||||
|
||||
if (u && tenantId) {
|
||||
const [data, fetchedFunnels] = await Promise.all([
|
||||
const [data, fetchedFunnels, fetchedOrigins] = await Promise.all([
|
||||
getAttendances(tenantId, {
|
||||
...filters,
|
||||
userId: id
|
||||
}),
|
||||
getFunnels(tenantId)
|
||||
getFunnels(tenantId),
|
||||
getOrigins(tenantId)
|
||||
]);
|
||||
setAttendances(data);
|
||||
|
||||
@@ -48,6 +50,13 @@ export const UserDetail: React.FC = () => {
|
||||
if (matchedFunnel) activeFunnel = matchedFunnel;
|
||||
}
|
||||
setFunnelDefs(activeFunnel && activeFunnel.stages ? activeFunnel.stages.sort((a: any, b: any) => a.order_index - b.order_index) : []);
|
||||
|
||||
let activeOriginGroup = fetchedOrigins[0];
|
||||
if (targetTeamId) {
|
||||
const matchedOrigin = fetchedOrigins.find(o => o.teamIds?.includes(targetTeamId));
|
||||
if (matchedOrigin) activeOriginGroup = matchedOrigin;
|
||||
}
|
||||
setOriginDefs(activeOriginGroup && activeOriginGroup.items ? activeOriginGroup.items : []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading user details", error);
|
||||
@@ -154,19 +163,18 @@ export const UserDetail: React.FC = () => {
|
||||
<option key={stage} value={stage}>{stage}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
<select
|
||||
className="bg-zinc-50 dark:bg-dark-bg border border-zinc-200 dark:border-dark-border px-3 py-2 rounded-lg text-sm text-zinc-700 dark:text-zinc-200 outline-none focus:ring-2 focus:ring-brand-yellow/20 cursor-pointer hover:border-zinc-300 dark:hover:border-zinc-700 transition-all"
|
||||
value={filters.origin}
|
||||
onChange={(e) => handleFilterChange('origin', e.target.value)}
|
||||
>
|
||||
<option value="all">Todas Origens</option>
|
||||
<option value="WhatsApp">WhatsApp</option>
|
||||
<option value="Instagram">Instagram</option>
|
||||
<option value="Website">Website</option>
|
||||
<option value="LinkedIn">LinkedIn</option>
|
||||
<option value="Indicação">Indicação</option>
|
||||
</select>
|
||||
</div>
|
||||
{originDefs.length > 0 ? originDefs.map(o => (
|
||||
<option key={o.id} value={o.name}>{o.name}</option>
|
||||
)) : ['WhatsApp', 'Instagram', 'Website', 'LinkedIn', 'Indicação'].map(o => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select> </div>
|
||||
|
||||
{/* KPI Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
|
||||
Reference in New Issue
Block a user