feat: Refactor order flow initiation logic and enhance UI button styles for better user experience
This commit is contained in:
@@ -386,14 +386,14 @@
|
|||||||
|
|
||||||
<div class="flex flex-none items-center gap-1">
|
<div class="flex flex-none items-center gap-1">
|
||||||
<button
|
<button
|
||||||
class="btn btn-circle btn-ghost btn-sm hover:bg-primary/10 hover:text-primary transition-colors"
|
class="btn btn-ghost btn-sm hover:bg-primary/10 hover:text-primary transition-colors"
|
||||||
onclick={() => openEditEtapa(etapa)}
|
onclick={() => openEditEtapa(etapa)}
|
||||||
title="Editar etapa"
|
title="Editar etapa"
|
||||||
>
|
>
|
||||||
<Edit size={16} />
|
<Edit size={16} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-circle btn-ghost btn-sm text-base-content/30 hover:bg-error/10 hover:text-error transition-colors"
|
class="btn btn-error btn-sm text-base-content/30 hover:bg-error/10 hover:text-error transition-colors"
|
||||||
onclick={() => handleDeleteEtapa(etapa)}
|
onclick={() => handleDeleteEtapa(etapa)}
|
||||||
title="Excluir etapa"
|
title="Excluir etapa"
|
||||||
>
|
>
|
||||||
@@ -495,7 +495,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
class="btn btn-ghost btn-circle btn-sm text-base-content/30 hover:text-accent hover:bg-accent/10 opacity-0 transition-all group-hover:opacity-100"
|
class="btn btn-ghost btn-sm text-base-content/30 hover:text-accent hover:bg-accent/10 opacity-0 transition-all group-hover:opacity-100"
|
||||||
onclick={() => handleSetPadrao(transicao._id)}
|
onclick={() => handleSetPadrao(transicao._id)}
|
||||||
title="Marcar como avanço padrão"
|
title="Marcar como avanço padrão"
|
||||||
>
|
>
|
||||||
@@ -506,7 +506,7 @@
|
|||||||
<div class="divider divider-horizontal mx-0 h-6 opacity-30"></div>
|
<div class="divider divider-horizontal mx-0 h-6 opacity-30"></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-ghost btn-circle btn-sm text-base-content/30 hover:text-error hover:bg-error/10 transition-all"
|
class="btn btn-sm btn-error text-base-content/30 hover:text-error hover:bg-error/10 transition-all"
|
||||||
onclick={() => handleDeleteTransicao(transicao)}
|
onclick={() => handleDeleteTransicao(transicao)}
|
||||||
title="Excluir transição"
|
title="Excluir transição"
|
||||||
>
|
>
|
||||||
@@ -540,7 +540,7 @@
|
|||||||
{editingEtapaId ? 'Editar Etapa' : 'Nova Etapa'}
|
{editingEtapaId ? 'Editar Etapa' : 'Nova Etapa'}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-circle btn-ghost btn-sm" onclick={closeEtapaModal}>
|
<button class="btn btn-circle btn-sm" onclick={closeEtapaModal}>
|
||||||
<X size={20} />
|
<X size={20} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -697,7 +697,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<h3 class="text-lg font-black tracking-tight uppercase">Nova Transição</h3>
|
<h3 class="text-lg font-black tracking-tight uppercase">Nova Transição</h3>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-circle btn-ghost btn-sm" onclick={closeTransicaoModal}>
|
<button class="btn btn-circle btn-sm" onclick={closeTransicaoModal}>
|
||||||
<X size={20} />
|
<X size={20} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -753,9 +753,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="alert alert-info border-info/20 mt-4 flex items-start gap-3 rounded-xl">
|
||||||
class="alert alert-info bg-info/5 border-info/20 mt-4 flex items-start gap-3 rounded-xl"
|
|
||||||
>
|
|
||||||
<div class="bg-info text-info-content mt-0.5 rounded-full p-1">
|
<div class="bg-info text-info-content mt-0.5 rounded-full p-1">
|
||||||
<Star size={10} fill="currentColor" />
|
<Star size={10} fill="currentColor" />
|
||||||
</div>
|
</div>
|
||||||
@@ -780,9 +778,7 @@
|
|||||||
Criar Transição
|
Criar Transição
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-ghost btn-block btn-sm" onclick={closeTransicaoModal}>
|
<button class="btn btn-block btn-sm" onclick={closeTransicaoModal}> Descartar </button>
|
||||||
Descartar
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
2
packages/backend/convex/_generated/api.d.ts
vendored
2
packages/backend/convex/_generated/api.d.ts
vendored
@@ -37,6 +37,7 @@ import type * as contratos from "../contratos.js";
|
|||||||
import type * as crons from "../crons.js";
|
import type * as crons from "../crons.js";
|
||||||
import type * as cursos from "../cursos.js";
|
import type * as cursos from "../cursos.js";
|
||||||
import type * as dashboard from "../dashboard.js";
|
import type * as dashboard from "../dashboard.js";
|
||||||
|
import type * as debug from "../debug.js";
|
||||||
import type * as documentos from "../documentos.js";
|
import type * as documentos from "../documentos.js";
|
||||||
import type * as email from "../email.js";
|
import type * as email from "../email.js";
|
||||||
import type * as empresas from "../empresas.js";
|
import type * as empresas from "../empresas.js";
|
||||||
@@ -138,6 +139,7 @@ declare const fullApi: ApiFromModules<{
|
|||||||
crons: typeof crons;
|
crons: typeof crons;
|
||||||
cursos: typeof cursos;
|
cursos: typeof cursos;
|
||||||
dashboard: typeof dashboard;
|
dashboard: typeof dashboard;
|
||||||
|
debug: typeof debug;
|
||||||
documentos: typeof documentos;
|
documentos: typeof documentos;
|
||||||
email: typeof email;
|
email: typeof email;
|
||||||
empresas: typeof empresas;
|
empresas: typeof empresas;
|
||||||
|
|||||||
23
packages/backend/convex/debug.ts
Normal file
23
packages/backend/convex/debug.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { query } from './_generated/server';
|
||||||
|
import { v } from 'convex/values';
|
||||||
|
|
||||||
|
export const inspectOrder = query({
|
||||||
|
args: { pedidoId: v.id('pedidos') },
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
const pedido = await ctx.db.get(args.pedidoId);
|
||||||
|
const historicoEtapas = await ctx.db
|
||||||
|
.query('pedidoEtapasHistorico')
|
||||||
|
.withIndex('by_pedidoId', (q) => q.eq('pedidoId', args.pedidoId))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
const etapas = await ctx.db.query('pedidoFluxoEtapa').collect();
|
||||||
|
const transicoes = await ctx.db.query('pedidoFluxoTransicao').collect();
|
||||||
|
|
||||||
|
return {
|
||||||
|
pedido,
|
||||||
|
historicoEtapas,
|
||||||
|
etapasConfiguradas: etapas,
|
||||||
|
transicoesConfiguradas: transicoes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -20,6 +20,59 @@ async function getUsuarioAutenticado(ctx: QueryCtx | MutationCtx) {
|
|||||||
return usuario;
|
return usuario;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function iniciarFluxoPedidoInternal(
|
||||||
|
ctx: MutationCtx,
|
||||||
|
pedidoId: Id<'pedidos'>,
|
||||||
|
usuarioId: Id<'usuarios'>
|
||||||
|
) {
|
||||||
|
const pedido = await ctx.db.get(pedidoId);
|
||||||
|
if (!pedido) {
|
||||||
|
throw new Error('Pedido não encontrado');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar se já tem histórico
|
||||||
|
const historicoExistente = await ctx.db
|
||||||
|
.query('pedidoEtapasHistorico')
|
||||||
|
.withIndex('by_pedidoId', (q) => q.eq('pedidoId', pedidoId))
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (historicoExistente) {
|
||||||
|
return historicoExistente._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar primeira etapa do fluxo
|
||||||
|
const primeiraEtapa = await ctx.db.query('pedidoFluxoEtapa').withIndex('by_ordem').first();
|
||||||
|
|
||||||
|
if (!primeiraEtapa) {
|
||||||
|
console.warn('Nenhuma etapa configurada no fluxo. Timeline ficará vazia.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Criar primeiro registro
|
||||||
|
const historicoId = await ctx.db.insert('pedidoEtapasHistorico', {
|
||||||
|
pedidoId,
|
||||||
|
etapaId: primeiraEtapa._id,
|
||||||
|
inicioData: now,
|
||||||
|
atual: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Registrar no histórico
|
||||||
|
await ctx.db.insert('historicoPedidos', {
|
||||||
|
pedidoId,
|
||||||
|
usuarioId,
|
||||||
|
acao: 'inicio_fluxo',
|
||||||
|
detalhes: JSON.stringify({
|
||||||
|
etapa: primeiraEtapa.codigo,
|
||||||
|
etapaNome: primeiraEtapa.nome
|
||||||
|
}),
|
||||||
|
data: now
|
||||||
|
});
|
||||||
|
|
||||||
|
return historicoId;
|
||||||
|
}
|
||||||
|
|
||||||
async function getEtapaAtualDoPedido(ctx: QueryCtx | MutationCtx, pedidoId: Id<'pedidos'>) {
|
async function getEtapaAtualDoPedido(ctx: QueryCtx | MutationCtx, pedidoId: Id<'pedidos'>) {
|
||||||
const etapaAtual = await ctx.db
|
const etapaAtual = await ctx.db
|
||||||
.query('pedidoEtapasHistorico')
|
.query('pedidoEtapasHistorico')
|
||||||
@@ -720,55 +773,10 @@ export const iniciarFluxoPedido = mutation({
|
|||||||
args: {
|
args: {
|
||||||
pedidoId: v.id('pedidos')
|
pedidoId: v.id('pedidos')
|
||||||
},
|
},
|
||||||
returns: v.id('pedidoEtapasHistorico'),
|
returns: v.union(v.id('pedidoEtapasHistorico'), v.null()),
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
const usuario = await getUsuarioAutenticado(ctx);
|
const usuario = await getUsuarioAutenticado(ctx);
|
||||||
|
return await iniciarFluxoPedidoInternal(ctx, args.pedidoId, usuario._id);
|
||||||
const pedido = await ctx.db.get(args.pedidoId);
|
|
||||||
if (!pedido) {
|
|
||||||
throw new Error('Pedido não encontrado');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verificar se já tem histórico
|
|
||||||
const historicoExistente = await ctx.db
|
|
||||||
.query('pedidoEtapasHistorico')
|
|
||||||
.withIndex('by_pedidoId', (q) => q.eq('pedidoId', args.pedidoId))
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (historicoExistente) {
|
|
||||||
throw new Error('Este pedido já possui histórico de etapas');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buscar primeira etapa do fluxo
|
|
||||||
const primeiraEtapa = await ctx.db.query('pedidoFluxoEtapa').withIndex('by_ordem').first();
|
|
||||||
|
|
||||||
if (!primeiraEtapa) {
|
|
||||||
throw new Error('Nenhuma etapa configurada no fluxo');
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
// Criar primeiro registro
|
|
||||||
const historicoId = await ctx.db.insert('pedidoEtapasHistorico', {
|
|
||||||
pedidoId: args.pedidoId,
|
|
||||||
etapaId: primeiraEtapa._id,
|
|
||||||
inicioData: now,
|
|
||||||
atual: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Registrar no histórico
|
|
||||||
await ctx.db.insert('historicoPedidos', {
|
|
||||||
pedidoId: args.pedidoId,
|
|
||||||
usuarioId: usuario._id,
|
|
||||||
acao: 'inicio_fluxo',
|
|
||||||
detalhes: JSON.stringify({
|
|
||||||
etapa: primeiraEtapa.codigo,
|
|
||||||
etapaNome: primeiraEtapa.nome
|
|
||||||
}),
|
|
||||||
data: now
|
|
||||||
});
|
|
||||||
|
|
||||||
return historicoId;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { Doc, Id } from './_generated/dataModel';
|
|||||||
import type { MutationCtx, QueryCtx } from './_generated/server';
|
import type { MutationCtx, QueryCtx } from './_generated/server';
|
||||||
import { internalMutation, mutation, query } from './_generated/server';
|
import { internalMutation, mutation, query } from './_generated/server';
|
||||||
import { getCurrentUserFunction } from './auth';
|
import { getCurrentUserFunction } from './auth';
|
||||||
|
import { iniciarFluxoPedidoInternal } from './pedidoFlow';
|
||||||
import { getTodayYMD, isWithinRangeYMD, maxYMD } from './utils/datas';
|
import { getTodayYMD, isWithinRangeYMD, maxYMD } from './utils/datas';
|
||||||
|
|
||||||
// ========== HELPERS ==========
|
// ========== HELPERS ==========
|
||||||
@@ -1292,6 +1293,9 @@ export const create = mutation({
|
|||||||
data: Date.now()
|
data: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 5. Iniciar Fluxo se houver etapas
|
||||||
|
await iniciarFluxoPedidoInternal(ctx, pedidoId, user._id);
|
||||||
|
|
||||||
return pedidoId;
|
return pedidoId;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2079,6 +2083,9 @@ export const enviarParaAceite = mutation({
|
|||||||
data: Date.now()
|
data: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Garantir que o fluxo foi iniciado
|
||||||
|
await iniciarFluxoPedidoInternal(ctx, args.pedidoId, user._id);
|
||||||
|
|
||||||
await ctx.scheduler.runAfter(0, internal.pedidos.notifyStatusChange, {
|
await ctx.scheduler.runAfter(0, internal.pedidos.notifyStatusChange, {
|
||||||
pedidoId: args.pedidoId,
|
pedidoId: args.pedidoId,
|
||||||
oldStatus,
|
oldStatus,
|
||||||
|
|||||||
Reference in New Issue
Block a user