Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:35:57 +02:00
commit 1dfbca08a1
528 files changed
+131054

No files matched your search

+142
View File
@@ -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" },
],
};
};