feat: timeline multi-select, demand popover, resource hover card, merged tooltips, dark mode fixes
Major timeline enhancements: - Right-click drag multi-selection with floating action bar (batch delete/assign) - DemandPopover for demand strip details (replaces broken "Loading" modal) - ResourceHoverCard on name hover showing skills, rates, role, chapter - Merged heatmap+vacation tooltips into unified TimelineTooltip component - Fixed overbooking blink animation (date normalization, z-index ordering) - Fixed dark mode sticky column bleed-through in project view - System roles admin page, notification task management, performance review docs Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -15,16 +15,47 @@ export interface TRPCContext {
|
||||
session: Session | null;
|
||||
db: typeof prisma;
|
||||
dbUser: { id: string; systemRole: string; permissionOverrides: unknown } | null;
|
||||
roleDefaults: Record<string, PermissionKey[]> | null;
|
||||
}
|
||||
|
||||
// Cache role defaults for 60 seconds to avoid DB hit on every request
|
||||
let _roleDefaultsCache: Record<string, PermissionKey[]> | null = null;
|
||||
let _roleDefaultsCacheTime = 0;
|
||||
const ROLE_DEFAULTS_TTL = 60_000;
|
||||
|
||||
export async function loadRoleDefaults(): Promise<Record<string, PermissionKey[]>> {
|
||||
const now = Date.now();
|
||||
if (_roleDefaultsCache && now - _roleDefaultsCacheTime < ROLE_DEFAULTS_TTL) {
|
||||
return _roleDefaultsCache;
|
||||
}
|
||||
const configs = await prisma.systemRoleConfig.findMany({
|
||||
select: { role: true, defaultPermissions: true },
|
||||
});
|
||||
const map: Record<string, PermissionKey[]> = {};
|
||||
for (const c of configs) {
|
||||
map[c.role] = c.defaultPermissions as PermissionKey[];
|
||||
}
|
||||
_roleDefaultsCache = map;
|
||||
_roleDefaultsCacheTime = now;
|
||||
return map;
|
||||
}
|
||||
|
||||
/** Invalidate the role defaults cache (call after updating SystemRoleConfig) */
|
||||
export function invalidateRoleDefaultsCache(): void {
|
||||
_roleDefaultsCache = null;
|
||||
_roleDefaultsCacheTime = 0;
|
||||
}
|
||||
|
||||
export function createTRPCContext(opts: {
|
||||
session: Session | null;
|
||||
dbUser?: { id: string; systemRole: string; permissionOverrides: unknown } | null;
|
||||
roleDefaults?: Record<string, PermissionKey[]> | null;
|
||||
}): TRPCContext {
|
||||
return {
|
||||
session: opts.session,
|
||||
db: prisma,
|
||||
dbUser: opts.dbUser ?? null,
|
||||
roleDefaults: opts.roleDefaults ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,7 +117,8 @@ export const managerProcedure = protectedProcedure.use(({ ctx, next }) => {
|
||||
}
|
||||
const permissions = resolvePermissions(
|
||||
user.systemRole as SystemRole,
|
||||
user.permissionOverrides as import("@planarchy/shared").PermissionOverrides | null
|
||||
user.permissionOverrides as import("@planarchy/shared").PermissionOverrides | null,
|
||||
ctx.roleDefaults ?? undefined,
|
||||
);
|
||||
return next({ ctx: { ...ctx, user, permissions } });
|
||||
});
|
||||
@@ -104,7 +136,8 @@ export const controllerProcedure = protectedProcedure.use(({ ctx, next }) => {
|
||||
}
|
||||
const permissions = resolvePermissions(
|
||||
user.systemRole as SystemRole,
|
||||
user.permissionOverrides as import("@planarchy/shared").PermissionOverrides | null
|
||||
user.permissionOverrides as import("@planarchy/shared").PermissionOverrides | null,
|
||||
ctx.roleDefaults ?? undefined,
|
||||
);
|
||||
return next({ ctx: { ...ctx, user, permissions } });
|
||||
});
|
||||
@@ -117,7 +150,7 @@ export const adminProcedure = protectedProcedure.use(({ ctx, next }) => {
|
||||
if (!user || user.systemRole !== SystemRole.ADMIN) {
|
||||
throw new TRPCError({ code: "FORBIDDEN", message: "Admin role required" });
|
||||
}
|
||||
const permissions = resolvePermissions(SystemRole.ADMIN, null);
|
||||
const permissions = resolvePermissions(SystemRole.ADMIN, null, ctx.roleDefaults ?? undefined);
|
||||
return next({ ctx: { ...ctx, user, permissions } });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user