68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
import { Link } from "react-router";
|
|
|
|
import type { AuthInfo, WizardDirectoryEntry } from "../types";
|
|
import { usePlatformLanguage } from "../i18n/LanguageContext";
|
|
import { hasAnyScope, hasScope } from "../utils/permissions";
|
|
import Card from "./Card";
|
|
|
|
export type WizardDirectoryProps = {
|
|
entries: WizardDirectoryEntry[];
|
|
auth: AuthInfo;
|
|
emptyText?: string;
|
|
};
|
|
|
|
function canOpenWizard(auth: AuthInfo, entry: WizardDirectoryEntry): boolean {
|
|
if (entry.available === false) return false;
|
|
if (entry.allOf?.some((scope) => !hasScope(auth, scope))) return false;
|
|
if (entry.anyOf?.length && !hasAnyScope(auth, entry.anyOf)) return false;
|
|
return true;
|
|
}
|
|
|
|
export default function WizardDirectory({
|
|
entries,
|
|
auth,
|
|
emptyText = "No guided workflows are available."
|
|
}: WizardDirectoryProps) {
|
|
const { translateText } = usePlatformLanguage();
|
|
const visibleEntries = entries.filter(
|
|
(entry) =>
|
|
!entry.anyOf?.length
|
|
|| hasAnyScope(auth, entry.anyOf)
|
|
|| entry.available === false
|
|
);
|
|
|
|
if (!visibleEntries.length) {
|
|
return <p className="muted">{translateText(emptyText)}</p>;
|
|
}
|
|
|
|
return (
|
|
<div className="wizard-directory-grid">
|
|
{visibleEntries.map((entry) => {
|
|
const available = canOpenWizard(auth, entry);
|
|
return (
|
|
<Card
|
|
key={`${entry.moduleId}:${entry.id}`}
|
|
title={entry.label}
|
|
actions={
|
|
available
|
|
? (
|
|
<Link className="btn btn-primary" to={entry.to}>
|
|
{translateText(entry.actionLabel ?? "Open")}
|
|
</Link>
|
|
)
|
|
: null
|
|
}
|
|
>
|
|
{entry.description && <p>{translateText(entry.description)}</p>}
|
|
{!available && entry.unavailableReason && (
|
|
<p className="muted small-note">
|
|
{translateText(entry.unavailableReason)}
|
|
</p>
|
|
)}
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|