155 lines
5.5 KiB
TypeScript
155 lines
5.5 KiB
TypeScript
/**
|
|
* TemplatePicker - modal to browse and select a workflow template for creating a new workflow.
|
|
*/
|
|
|
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
import { FaSpinner } from 'react-icons/fa';
|
|
import {
|
|
fetchTemplates,
|
|
type AutoWorkflowTemplate,
|
|
type AutoTemplateScope,
|
|
type ApiRequestFunction,
|
|
} from '../../../api/workflowApi';
|
|
import styles from './Automation2FlowEditor.module.css';
|
|
import { useLanguage } from '../../../providers/language/LanguageContext';
|
|
|
|
interface TemplatePickerProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
onSelect: (templateId: string) => void;
|
|
instanceId: string;
|
|
request: ApiRequestFunction;
|
|
}
|
|
|
|
export const TemplatePicker: React.FC<TemplatePickerProps> = ({
|
|
open,
|
|
onClose,
|
|
onSelect,
|
|
instanceId,
|
|
request,
|
|
}) => {
|
|
const { t } = useLanguage();
|
|
const scopeLabels = useMemo(
|
|
() =>
|
|
({
|
|
all: t('Alle'),
|
|
user: t('Meine'),
|
|
instance: t('Instanz'),
|
|
mandate: t('Mandant'),
|
|
system: t('System'),
|
|
}) as Record<AutoTemplateScope | 'all', string>,
|
|
[t]
|
|
);
|
|
const [templates, setTemplates] = useState<AutoWorkflowTemplate[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [activeScope, setActiveScope] = useState<AutoTemplateScope | 'all'>('all');
|
|
const [copying, setCopying] = useState<string | null>(null);
|
|
|
|
const _load = useCallback(async () => {
|
|
if (!instanceId || !open) return;
|
|
setLoading(true);
|
|
try {
|
|
const scope = activeScope === 'all' ? undefined : activeScope;
|
|
const result = await fetchTemplates(request, instanceId, scope);
|
|
setTemplates(Array.isArray(result) ? result : result.items);
|
|
} catch {
|
|
setTemplates([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [instanceId, request, open, activeScope]);
|
|
|
|
useEffect(() => {
|
|
_load();
|
|
}, [_load]);
|
|
|
|
const _handleSelect = useCallback(
|
|
async (templateId: string) => {
|
|
setCopying(templateId);
|
|
try {
|
|
await onSelect(templateId);
|
|
} finally {
|
|
setCopying(null);
|
|
}
|
|
},
|
|
[onSelect]
|
|
);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<div className={styles.workflowModalBackdrop} role="dialog" aria-modal="true" aria-labelledby="tpl-picker-title">
|
|
<div className={styles.workflowModal} style={{ maxWidth: 600, maxHeight: '80vh', display: 'flex', flexDirection: 'column' }}>
|
|
<h3 id="tpl-picker-title" className={styles.workflowModalTitle}>
|
|
{t('Neu aus Vorlage')}
|
|
</h3>
|
|
<p className={styles.workflowModalHint}>
|
|
{t('Wählen Sie eine Vorlage, um einen neuen Workflow zu erstellen.')}
|
|
</p>
|
|
|
|
<div style={{ display: 'flex', gap: 6, marginBottom: 12, flexWrap: 'wrap' }}>
|
|
{(['all', 'user', 'instance', 'mandate', 'system'] as const).map((s) => (
|
|
<button
|
|
key={s}
|
|
type="button"
|
|
className={activeScope === s ? styles.workflowModalBtnPrimary : styles.workflowModalBtnSecondary}
|
|
onClick={() => setActiveScope(s)}
|
|
style={{ fontSize: '0.8rem', padding: '4px 10px' }}
|
|
>
|
|
{scopeLabels[s]}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<div style={{ flex: 1, overflowY: 'auto', minHeight: 120 }}>
|
|
{loading ? (
|
|
<div style={{ textAlign: 'center', padding: 24 }}>
|
|
<FaSpinner className={styles.spinner} />
|
|
</div>
|
|
) : templates.length === 0 ? (
|
|
<div style={{ textAlign: 'center', padding: 24, color: 'var(--text-secondary, #888)' }}>
|
|
{t('Keine Vorlagen gefunden.')}
|
|
</div>
|
|
) : (
|
|
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.85rem' }}>
|
|
<thead>
|
|
<tr style={{ borderBottom: '2px solid var(--border-color, #e0e0e0)', textAlign: 'left' }}>
|
|
<th style={{ padding: '6px 8px' }}>{t('Name')}</th>
|
|
<th style={{ padding: '6px 8px', width: 80 }}>{t('Scope')}</th>
|
|
<th style={{ padding: '6px 8px', width: 100 }}></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{templates.map((tpl) => (
|
|
<tr key={tpl.id} style={{ borderBottom: '1px solid var(--border-color, #eee)' }}>
|
|
<td style={{ padding: '8px' }}>{tpl.label}</td>
|
|
<td style={{ padding: '8px', fontSize: '0.8rem', color: 'var(--text-secondary, #888)' }}>
|
|
{scopeLabels[(tpl.templateScope as AutoTemplateScope) || 'user']}
|
|
</td>
|
|
<td style={{ padding: '8px', textAlign: 'right' }}>
|
|
<button
|
|
type="button"
|
|
className={styles.workflowModalBtnPrimary}
|
|
style={{ fontSize: '0.8rem', padding: '4px 10px' }}
|
|
onClick={() => _handleSelect(tpl.id)}
|
|
disabled={copying !== null}
|
|
>
|
|
{copying === tpl.id ? <FaSpinner className={styles.spinner} /> : t('Übernehmen')}
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</div>
|
|
|
|
<div className={styles.workflowModalActions} style={{ marginTop: 12 }}>
|
|
<button type="button" className={styles.workflowModalBtnSecondary} onClick={onClose}>
|
|
{t('Abbrechen')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|