feat: implement customizable funnel stages per tenant
- Modified attendances.funnel_stage in DB from ENUM to VARCHAR. - Created tenant_funnels table and backend API routes to manage custom stages. - Added /admin/funnels page for Admins/Managers to create, edit, order, and color-code their funnel stages. - Updated Dashboard, UserDetail, and AttendanceDetail to fetch and render dynamic funnel stages instead of hardcoded enums. - Added defensive checks and logging to GET /users/:idOrSlug to fix sporadic 500 errors during impersonation handoffs.
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { getAttendanceById, getUserById } from '../services/dataService';
|
||||
import { Attendance, User, FunnelStage } from '../types';
|
||||
import { getAttendanceById, getUserById, getFunnels } from '../services/dataService';
|
||||
import { Attendance, User, FunnelStage, FunnelStageDef } from '../types';
|
||||
import { ArrowLeft, CheckCircle2, AlertCircle, Clock, Calendar, MessageSquare, ShoppingBag, Award, TrendingUp } from 'lucide-react';
|
||||
|
||||
export const AttendanceDetail: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const [data, setData] = useState<Attendance | undefined>();
|
||||
const [agent, setAgent] = useState<User | undefined>();
|
||||
const [funnelDefs, setFunnelDefs] = useState<FunnelStageDef[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -15,8 +16,15 @@ export const AttendanceDetail: React.FC = () => {
|
||||
if (id) {
|
||||
setLoading(true);
|
||||
try {
|
||||
const att = await getAttendanceById(id);
|
||||
const tenantId = localStorage.getItem('ctms_tenant_id') || '';
|
||||
const [att, fDefs] = await Promise.all([
|
||||
getAttendanceById(id),
|
||||
getFunnels(tenantId)
|
||||
]);
|
||||
|
||||
setData(att);
|
||||
setFunnelDefs(fDefs);
|
||||
|
||||
if (att) {
|
||||
const u = await getUserById(att.user_id);
|
||||
setAgent(u);
|
||||
@@ -34,7 +42,10 @@ export const AttendanceDetail: React.FC = () => {
|
||||
if (loading) return <div className="p-12 text-center text-zinc-400 dark:text-dark-muted transition-colors">Carregando detalhes...</div>;
|
||||
if (!data) return <div className="p-12 text-center text-zinc-500 dark:text-dark-muted transition-colors">Registro de atendimento não encontrado</div>;
|
||||
|
||||
const getStageColor = (stage: FunnelStage) => {
|
||||
const getStageColor = (stage: string) => {
|
||||
const def = funnelDefs.find(f => f.name === stage);
|
||||
if (def) return def.color_class;
|
||||
|
||||
switch (stage) {
|
||||
case FunnelStage.WON: return 'text-green-700 bg-green-50 border-green-200 dark:text-green-400 dark:bg-green-900/30 dark:border-green-800';
|
||||
case FunnelStage.LOST: return 'text-red-700 bg-red-50 border-red-200 dark:text-red-400 dark:bg-red-900/30 dark:border-red-800';
|
||||
|
||||
Reference in New Issue
Block a user