feat: watcher/CC system, SLA engine, and rich text comments
- Watcher system: ticket_watchers table, watch/unwatch endpoints, notifications to watchers on comments and updates, watcher/cc recipient sources in SendEmail scrip action, watch toggle and watcher avatars in ticket detail UI - SLA engine: sla_policies table, SLA deadline columns on tickets, CRUD routes, OnSlaBreach scrip condition, scheduler SLA calculation, deadlines set on create/reply, cleared on resolve, SLA indicators on ticket list and detail, SLA Policies tab in admin - Rich text: marked-based markdown rendering with XSS safety, Write/Preview toggle in comment composer, styled prose output
This commit is contained in:
@@ -18,6 +18,7 @@ export interface ConditionConfig {
|
||||
new_value?: unknown;
|
||||
value?: unknown;
|
||||
link_type?: unknown;
|
||||
breach_type?: unknown;
|
||||
}
|
||||
|
||||
export interface ConditionEvaluator {
|
||||
@@ -97,6 +98,27 @@ export class OnLinkCreate implements ConditionEvaluator {
|
||||
}
|
||||
}
|
||||
|
||||
export class OnSlaBreach implements ConditionEvaluator {
|
||||
evaluate(ticket: Ticket, _transactions: Transaction[], _context?: ConditionEvaluateContext, config?: ConditionConfig): boolean {
|
||||
const breachType = config?.breach_type ?? 'any';
|
||||
|
||||
// Check if ticket has sla_breached set
|
||||
const breached = (ticket as any).sla_breached as string | null;
|
||||
|
||||
if (breachType === 'any') {
|
||||
return breached === 'response' || breached === 'resolution' || breached === 'both';
|
||||
}
|
||||
if (breachType === 'response') {
|
||||
return breached === 'response' || breached === 'both';
|
||||
}
|
||||
if (breachType === 'resolution') {
|
||||
return breached === 'resolution' || breached === 'both';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class OnOverdue implements ConditionEvaluator {
|
||||
evaluate(_ticket: Ticket, _transactions: Transaction[], context?: ConditionEvaluateContext, config?: ConditionConfig): boolean {
|
||||
const fieldKey = config?.field_key ?? config?.field_id ?? config?.field;
|
||||
@@ -130,6 +152,7 @@ const conditionRegistry: Record<string, ConditionEvaluator> = {
|
||||
OnCustomFieldChange: new OnCustomFieldChange(),
|
||||
OnLinkCreate: new OnLinkCreate(),
|
||||
OnOverdue: new OnOverdue(),
|
||||
OnSlaBreach: new OnSlaBreach(),
|
||||
};
|
||||
|
||||
export function getConditionEvaluator(type: string): ConditionEvaluator | null {
|
||||
|
||||
Reference in New Issue
Block a user