Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,142 @@
|
||||
import type { ApprovalRequest, ApprovalStatus } from "@/types/appops";
|
||||
|
||||
export interface ApprovalFollowupLink {
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface ApprovalFollowupView {
|
||||
status: ApprovalStatus;
|
||||
statusLabel: string;
|
||||
expiryLabel: string;
|
||||
auditReference: string | null;
|
||||
jobReference: string | null;
|
||||
executionCopy: string;
|
||||
canDecide: boolean;
|
||||
links: ApprovalFollowupLink[];
|
||||
}
|
||||
|
||||
export interface ApprovalEmptyState {
|
||||
title: string;
|
||||
message: string;
|
||||
actions: ApprovalFollowupLink[];
|
||||
}
|
||||
|
||||
const statusLabels: Record<ApprovalStatus, string> = {
|
||||
pending: "Pending",
|
||||
approved: "Approved",
|
||||
rejected: "Rejected",
|
||||
expired: "Expired",
|
||||
executed: "Executed",
|
||||
};
|
||||
|
||||
const normalizeStatus = (status?: string): ApprovalStatus => (
|
||||
status === "approved" ||
|
||||
status === "rejected" ||
|
||||
status === "expired" ||
|
||||
status === "executed"
|
||||
? status
|
||||
: "pending"
|
||||
);
|
||||
|
||||
const formatRelativeExpiry = (expiresAt?: string, now = new Date()): { label: string; expired: boolean } => {
|
||||
if (!expiresAt) return { label: "Geen vervaldatum", expired: false };
|
||||
const timestamp = Date.parse(expiresAt);
|
||||
if (!Number.isFinite(timestamp)) return { label: "Vervaldatum onbekend", expired: false };
|
||||
const diffMs = timestamp - now.getTime();
|
||||
const absMinutes = Math.max(1, Math.round(Math.abs(diffMs) / 60_000));
|
||||
const hours = Math.floor(absMinutes / 60);
|
||||
const minutes = absMinutes % 60;
|
||||
const duration = hours > 0 ? `${hours}u ${minutes}m` : `${absMinutes}m`;
|
||||
return diffMs < 0
|
||||
? { label: `Verlopen ${duration} geleden`, expired: true }
|
||||
: { label: `Verloopt over ${duration}`, expired: false };
|
||||
};
|
||||
|
||||
export const buildApprovalFollowupView = (approval: ApprovalRequest, now = new Date()): ApprovalFollowupView => {
|
||||
const status = normalizeStatus(approval.status);
|
||||
const expiry = formatRelativeExpiry(approval.expiresAt, now);
|
||||
const effectiveStatus: ApprovalStatus = status === "pending" && expiry.expired ? "expired" : status;
|
||||
const auditReference = approval.auditId || null;
|
||||
const jobReference = approval.jobId || null;
|
||||
const decided = effectiveStatus !== "pending";
|
||||
const canDecide = effectiveStatus === "pending";
|
||||
|
||||
const links: ApprovalFollowupLink[] = decided
|
||||
? [
|
||||
{ label: "Open Audit", href: auditReference ? `/audit?query=${encodeURIComponent(auditReference)}` : "/audit" },
|
||||
{ label: "Jobs", href: jobReference ? `/jobs?query=${encodeURIComponent(jobReference)}` : "/jobs" },
|
||||
]
|
||||
: [
|
||||
{ label: "Open Audit", href: "/audit" },
|
||||
{ label: "Command Center", href: "/command-center" },
|
||||
{ label: "Jobs", href: "/jobs" },
|
||||
];
|
||||
|
||||
return {
|
||||
status: effectiveStatus,
|
||||
statusLabel: statusLabels[effectiveStatus],
|
||||
expiryLabel: effectiveStatus === "expired" ? expiry.label : expiry.label,
|
||||
auditReference,
|
||||
jobReference,
|
||||
executionCopy: decided
|
||||
? effectiveStatus === "expired"
|
||||
? "Approval is verlopen. Er wordt geen actie uitgevoerd; maak indien nodig een nieuwe expliciete aanvraag."
|
||||
: "Beslissing opgeslagen. Controleer Audit en Jobs voor echte vervolgstatussen wanneer die beschikbaar zijn."
|
||||
: "Nog niet uitgevoerd. Goedkeuren of afwijzen schrijft audit en kan daarna een job opleveren als de actie een executor heeft.",
|
||||
canDecide,
|
||||
links,
|
||||
};
|
||||
};
|
||||
|
||||
export const filterApprovals = (approvals: ApprovalRequest[], query: string): ApprovalRequest[] => {
|
||||
const normalizedQuery = query.trim().toLowerCase();
|
||||
if (!normalizedQuery) return approvals;
|
||||
|
||||
return approvals.filter((approval) => [
|
||||
approval.id,
|
||||
approval.action,
|
||||
approval.target,
|
||||
approval.requester,
|
||||
approval.context,
|
||||
approval.reason || "",
|
||||
approval.auditId || "",
|
||||
approval.jobId || "",
|
||||
].some((value) => value.toLowerCase().includes(normalizedQuery)));
|
||||
};
|
||||
|
||||
export const buildApprovalEmptyState = (
|
||||
options: { totalApprovals: number; visibleApprovals: number; query: string },
|
||||
): ApprovalEmptyState => {
|
||||
const query = options.query.trim();
|
||||
|
||||
if (options.totalApprovals === 0) {
|
||||
return {
|
||||
title: "Geen openstaande approvals",
|
||||
message: "Er staan geen approvals klaar. Nieuwe approval-first acties verschijnen hier zodra Command Center, connectors of remediation flows er een aanvragen.",
|
||||
actions: [{ label: "Command Center", href: "/command-center" }],
|
||||
};
|
||||
}
|
||||
|
||||
if (query && options.visibleApprovals === 0) {
|
||||
return {
|
||||
title: "Geen approval voor deze handoff",
|
||||
message: `Er is geen approval gevonden voor "${query}". Controleer Audit of Jobs om te zien of de aanvraag al verwerkt, verlopen of als job doorgezet is.`,
|
||||
actions: [
|
||||
{ label: "Toon alle approvals", href: "/approvals" },
|
||||
{ label: "Zoek in Audit", href: `/audit?query=${encodeURIComponent(query)}` },
|
||||
{ label: "Zoek in Jobs", href: `/jobs?query=${encodeURIComponent(query)}` },
|
||||
{ label: "Command Center", href: "/command-center" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
title: "Geen approvals zichtbaar",
|
||||
message: "De huidige selectie bevat geen approvals. Toon alles of start een nieuwe approval-first actie via Command Center.",
|
||||
actions: [
|
||||
{ label: "Toon alle approvals", href: "/approvals" },
|
||||
{ label: "Command Center", href: "/command-center" },
|
||||
],
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user