feat(timeline): add pulse animation for in-flight drag mutations
Allocation bars that have active optimistic overrides (post-drag, awaiting server confirmation) now pulse subtly via animate-pulse. The pending set is derived from the existing optimisticAllocations map keys, requiring no additional state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
} from "@capakraken/shared";
|
||||
import { z } from "zod";
|
||||
import { findUniqueOrThrow } from "../db/helpers.js";
|
||||
import { createAuditEntry } from "../lib/audit.js";
|
||||
import { makeAuditLogger } from "../lib/audit-helpers.js";
|
||||
import type { TRPCContext } from "../trpc.js";
|
||||
import {
|
||||
lookupBestRateMatch,
|
||||
@@ -31,10 +31,6 @@ import {
|
||||
|
||||
type RateCardProcedureContext = Pick<TRPCContext, "db" | "dbUser">;
|
||||
|
||||
function withAuditUser(userId: string | undefined) {
|
||||
return userId ? { userId } : {};
|
||||
}
|
||||
|
||||
export const rateCardListInputSchema = z.object({
|
||||
isActive: z.boolean().optional(),
|
||||
search: z.string().optional(),
|
||||
@@ -144,6 +140,7 @@ export async function createRateCard(
|
||||
ctx: RateCardProcedureContext,
|
||||
input: RateCardCreateInput,
|
||||
) {
|
||||
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
|
||||
const { lines, name, currency } = input;
|
||||
|
||||
const rateCard = await ctx.db.rateCard.create({
|
||||
@@ -151,15 +148,12 @@ export async function createRateCard(
|
||||
include: rateCardCreateInclude,
|
||||
});
|
||||
|
||||
void createAuditEntry({
|
||||
db: ctx.db,
|
||||
audit({
|
||||
entityType: "RateCard",
|
||||
entityId: rateCard.id,
|
||||
entityName: rateCard.name,
|
||||
action: "CREATE",
|
||||
...withAuditUser(ctx.dbUser?.id),
|
||||
after: buildRateCardCreateAuditAfter({ name, currency, lines }),
|
||||
source: "ui",
|
||||
});
|
||||
|
||||
return rateCard;
|
||||
@@ -169,6 +163,7 @@ export async function updateRateCard(
|
||||
ctx: RateCardProcedureContext,
|
||||
input: RateCardUpdateInput,
|
||||
) {
|
||||
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
|
||||
const before = await findUniqueOrThrow(
|
||||
ctx.db.rateCard.findUnique({ where: { id: input.id } }),
|
||||
"Rate card",
|
||||
@@ -180,16 +175,13 @@ export async function updateRateCard(
|
||||
include: rateCardSummaryInclude,
|
||||
});
|
||||
|
||||
void createAuditEntry({
|
||||
db: ctx.db,
|
||||
audit({
|
||||
entityType: "RateCard",
|
||||
entityId: input.id,
|
||||
entityName: updated.name,
|
||||
action: "UPDATE",
|
||||
...withAuditUser(ctx.dbUser?.id),
|
||||
before: before as unknown as Record<string, unknown>,
|
||||
after: updated as unknown as Record<string, unknown>,
|
||||
source: "ui",
|
||||
});
|
||||
|
||||
return updated;
|
||||
@@ -199,19 +191,17 @@ export async function deactivateRateCard(
|
||||
ctx: RateCardProcedureContext,
|
||||
input: RateCardDeactivateInput,
|
||||
) {
|
||||
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
|
||||
const deactivated = await ctx.db.rateCard.update({
|
||||
where: { id: input.id },
|
||||
data: { isActive: false },
|
||||
});
|
||||
|
||||
void createAuditEntry({
|
||||
db: ctx.db,
|
||||
audit({
|
||||
entityType: "RateCard",
|
||||
entityId: input.id,
|
||||
entityName: deactivated.name,
|
||||
action: "DELETE",
|
||||
...withAuditUser(ctx.dbUser?.id),
|
||||
source: "ui",
|
||||
summary: "Deactivated rate card",
|
||||
});
|
||||
|
||||
@@ -222,6 +212,7 @@ export async function addRateCardLine(
|
||||
ctx: RateCardProcedureContext,
|
||||
input: RateCardAddLineInput,
|
||||
) {
|
||||
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
|
||||
const rateCard = await findUniqueOrThrow(
|
||||
ctx.db.rateCard.findUnique({ where: { id: input.rateCardId } }),
|
||||
"Rate card",
|
||||
@@ -232,18 +223,15 @@ export async function addRateCardLine(
|
||||
select: rateCardLineSelect,
|
||||
});
|
||||
|
||||
void createAuditEntry({
|
||||
db: ctx.db,
|
||||
audit({
|
||||
entityType: "RateCardLine",
|
||||
entityId: line.id,
|
||||
entityName: `${rateCard.name} - ${input.line.chapter ?? "line"}`,
|
||||
action: "CREATE",
|
||||
...withAuditUser(ctx.dbUser?.id),
|
||||
after: buildRateCardLineCreateAuditAfter({
|
||||
rateCardId: input.rateCardId,
|
||||
line: input.line,
|
||||
}),
|
||||
source: "ui",
|
||||
});
|
||||
|
||||
return line;
|
||||
@@ -253,6 +241,7 @@ export async function updateRateCardLine(
|
||||
ctx: RateCardProcedureContext,
|
||||
input: RateCardUpdateLineInput,
|
||||
) {
|
||||
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
|
||||
const before = await findUniqueOrThrow(
|
||||
ctx.db.rateCardLine.findUnique({ where: { id: input.lineId } }),
|
||||
"Rate card line",
|
||||
@@ -264,15 +253,12 @@ export async function updateRateCardLine(
|
||||
select: rateCardLineSelect,
|
||||
});
|
||||
|
||||
void createAuditEntry({
|
||||
db: ctx.db,
|
||||
audit({
|
||||
entityType: "RateCardLine",
|
||||
entityId: input.lineId,
|
||||
action: "UPDATE",
|
||||
...withAuditUser(ctx.dbUser?.id),
|
||||
before: before as unknown as Record<string, unknown>,
|
||||
after: updated as unknown as Record<string, unknown>,
|
||||
source: "ui",
|
||||
});
|
||||
|
||||
return updated;
|
||||
@@ -282,6 +268,7 @@ export async function deleteRateCardLine(
|
||||
ctx: RateCardProcedureContext,
|
||||
input: RateCardDeleteLineInput,
|
||||
) {
|
||||
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
|
||||
const line = await findUniqueOrThrow(
|
||||
ctx.db.rateCardLine.findUnique({ where: { id: input.lineId } }),
|
||||
"Rate card line",
|
||||
@@ -289,14 +276,11 @@ export async function deleteRateCardLine(
|
||||
|
||||
await ctx.db.rateCardLine.delete({ where: { id: input.lineId } });
|
||||
|
||||
void createAuditEntry({
|
||||
db: ctx.db,
|
||||
audit({
|
||||
entityType: "RateCardLine",
|
||||
entityId: input.lineId,
|
||||
action: "DELETE",
|
||||
...withAuditUser(ctx.dbUser?.id),
|
||||
before: line as unknown as Record<string, unknown>,
|
||||
source: "ui",
|
||||
});
|
||||
|
||||
return { deleted: true };
|
||||
@@ -306,6 +290,7 @@ export async function replaceRateCardLines(
|
||||
ctx: RateCardProcedureContext,
|
||||
input: RateCardReplaceLinesInput,
|
||||
) {
|
||||
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
|
||||
const rateCard = await findUniqueOrThrow(
|
||||
ctx.db.rateCard.findUnique({ where: { id: input.rateCardId } }),
|
||||
"Rate card",
|
||||
@@ -327,15 +312,12 @@ export async function replaceRateCardLines(
|
||||
);
|
||||
});
|
||||
|
||||
void createAuditEntry({
|
||||
db: ctx.db,
|
||||
audit({
|
||||
entityType: "RateCard",
|
||||
entityId: input.rateCardId,
|
||||
entityName: rateCard.name,
|
||||
action: "UPDATE",
|
||||
...withAuditUser(ctx.dbUser?.id),
|
||||
after: buildRateCardReplaceLinesAuditAfter(result.length),
|
||||
source: "ui",
|
||||
summary: `Replaced all lines with ${result.length} new lines`,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user