diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json index 9a804e6..7f815b5 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json @@ -4320,6 +4320,12 @@ "en": "Widgets", "tr": "Widget'lar" }, + { + "resourceName": "Platform", + "key": "ListForms.ListFormEdit.TabWorkflow", + "en": "Workflow", + "tr": "İş Akışı" + }, { "resourceName": "Platform", "key": "ListForms.ListFormEdit.TabFields", diff --git a/ui/src/proxy/admin/list-form/options.ts b/ui/src/proxy/admin/list-form/options.ts index 33ab54a..5afcf3e 100644 --- a/ui/src/proxy/admin/list-form/options.ts +++ b/ui/src/proxy/admin/list-form/options.ts @@ -101,6 +101,7 @@ export const tabVisibilityConfig: Record = { 'fields', 'editForm', 'widget', + 'workflow', //Chart tabları 'commonSettings', 'series', diff --git a/ui/src/services/workflow.service.ts b/ui/src/services/workflow.service.ts index b1c9097..7362d4f 100644 --- a/ui/src/services/workflow.service.ts +++ b/ui/src/services/workflow.service.ts @@ -66,7 +66,8 @@ export type CreateUpdateWorkflowInput = Partial & { tarih?: string } -export type SaveCriteriaInput = Partial & { +export type SaveCriteriaInput = Omit, 'id'> & { + id?: string | null workflowItemId: string } @@ -149,4 +150,3 @@ export const workflowService = { return response.data }, } - diff --git a/ui/src/utils/workflow/workflowConstants.ts b/ui/src/utils/workflow/workflowConstants.ts index 4f02e3c..5dc25ab 100644 --- a/ui/src/utils/workflow/workflowConstants.ts +++ b/ui/src/utils/workflow/workflowConstants.ts @@ -20,7 +20,7 @@ export const columnOptions = ["Tutar", "Id"].map((value) => ({ label: value, })); -export const kindIcon = { +export const kindIcon: Record = { Start: FiPlay as any, Compare: FiGitBranch as any, Approval: FiCheck as any, diff --git a/ui/src/utils/workflow/workflowHelpers.ts b/ui/src/utils/workflow/workflowHelpers.ts index 3dae4e6..52f2ab2 100644 --- a/ui/src/utils/workflow/workflowHelpers.ts +++ b/ui/src/utils/workflow/workflowHelpers.ts @@ -1,6 +1,53 @@ import { getNodeHeight, nodeSize } from "./workflowConstants"; +import type { + CompareOutcomeDto, + SaveCriteriaInput, + WorkflowConditionDto, + WorkflowCriteriaDto, + WorkflowItemDto, +} from "@/services/workflow.service"; -export function isPendingApproval(item, criteria) { +export type WorkflowCriteriaForm = Partial & { + id?: string | null; + workflowItemId: string; + compareOutcomes: CompareOutcomeDto[]; +}; + +export type WorkflowOutcome = { + field: string; + label: string; + targetId?: string | null; +}; + +export type WorkflowLinkPort = { + field?: string; + index?: number; + count?: number; + sourceSlotIndex?: number; + sourceSlotCount?: number; + targetSlotIndex?: number; + targetSlotCount?: number; + routeIndex?: number; + routeCount?: number; +}; + +export type WorkflowLink = { + key: string; + source: WorkflowCriteriaDto; + target: WorkflowCriteriaDto; + label: string; + sourcePort: WorkflowLinkPort; +}; + +type Endpoint = { + link: WorkflowLink; + role: "source" | "target"; +}; + +export function isPendingApproval( + item: WorkflowItemDto | undefined | null, + criteria: WorkflowCriteriaDto[], +) { if (!item) return false; return criteria.some( @@ -11,14 +58,14 @@ export function isPendingApproval(item, criteria) { ); } -export function buildFitLayout(criteria) { +export function buildFitLayout(criteria: WorkflowCriteriaDto[]) { const links = collectLinks(criteria); const rankById = buildTraversalRanks(criteria, links); - const groups = new Map(); + const groups = new Map(); criteria.forEach((item) => { const column = fitColumn(item); if (!groups.has(column)) groups.set(column, []); - groups.get(column).push(item); + groups.get(column)?.push(item); }); const sortedColumns = [...groups.keys()].sort((a, b) => a - b); @@ -34,12 +81,12 @@ export function buildFitLayout(criteria) { const top = 72; const left = 72; const xGap = 128; - const positions = new Map(); + const positions = new Map(); sortedColumns.forEach((column, columnIndex) => { - const items = groups - .get(column) - .sort((a, b) => compareLayoutNodes(a, b, rankById)); + const items = (groups.get(column) || []).sort((a, b) => + compareLayoutNodes(a, b, rankById), + ); const groupHeight = items.reduce((sum, item) => sum + getNodeHeight(item), 0) + Math.max(0, items.length - 1) * yGap; @@ -57,8 +104,8 @@ export function buildFitLayout(criteria) { return positions; } -function fitColumn(item) { - const priority = { +function fitColumn(item: WorkflowCriteriaDto) { + const priority: Record = { Start: 0, Compare: 1, Approval: 2, @@ -69,16 +116,25 @@ function fitColumn(item) { return priority[item.kind] ?? 2; } -function compareLayoutNodes(a, b, rankById = new Map()) { +function compareLayoutNodes( + a: WorkflowCriteriaDto, + b: WorkflowCriteriaDto, + rankById = new Map(), +) { return ( (rankById.get(a.id) ?? 999) - (rankById.get(b.id) ?? 999) || a.title.localeCompare(b.title, "tr") ); } -function buildTraversalRanks(criteria, links) { - const rankById = new Map(); - const outgoing = new Map(criteria.map((item) => [item.id, []])); +function buildTraversalRanks( + criteria: WorkflowCriteriaDto[], + links: WorkflowLink[], +) { + const rankById = new Map(); + const outgoing = new Map( + criteria.map((item) => [item.id, []]), + ); links.forEach((link) => { outgoing.get(link.source.id)?.push(link.target.id); }); @@ -90,6 +146,7 @@ function buildTraversalRanks(criteria, links) { while (queue.length) { const id = queue.shift(); + if (!id) continue; if (rankById.has(id)) continue; rankById.set(id, rankById.size); @@ -105,8 +162,8 @@ function buildTraversalRanks(criteria, links) { return rankById; } -export function collectLinks(criteria) { - const links = []; +export function collectLinks(criteria: WorkflowCriteriaDto[]) { + const links: WorkflowLink[] = []; criteria.forEach((source) => { if (source.kind === "Compare" && source.compareOutcomes?.length) { source.compareOutcomes.forEach((outcome, index) => { @@ -156,12 +213,15 @@ export function collectLinks(criteria) { return assignLinkSlots(links, criteria); } -export function assignLinkSlots(links, criteria) { - const endpointGroups = new Map(); - const addEndpoint = (nodeId, side, endpoint) => { +export function assignLinkSlots( + links: WorkflowLink[], + criteria: WorkflowCriteriaDto[], +) { + const endpointGroups = new Map(); + const addEndpoint = (nodeId: string, side: string, endpoint: Endpoint) => { const key = `${nodeId}:${side}`; if (!endpointGroups.has(key)) endpointGroups.set(key, []); - endpointGroups.get(key).push(endpoint); + endpointGroups.get(key)?.push(endpoint); }; links.forEach((link) => { @@ -195,7 +255,7 @@ export function assignLinkSlots(links, criteria) { return links; } -function sideToward(from, to) { +function sideToward(from: WorkflowCriteriaDto, to: WorkflowCriteriaDto) { const fromLeft = Number(from.positionX || 0); const fromTop = Number(from.positionY || 0); const fromCenter = { @@ -216,7 +276,7 @@ function sideToward(from, to) { return dy >= 0 ? "bottom" : "top"; } -export function getNodeOutcomes(item) { +export function getNodeOutcomes(item: WorkflowCriteriaDto): WorkflowOutcome[] { if (item.kind === "Compare") { const outcomes = item.compareOutcomes?.length ? item.compareOutcomes @@ -246,26 +306,28 @@ export function getNodeOutcomes(item) { ]; } -export function outcomeLabel(field) { +export function outcomeLabel(field?: string) { if (field?.startsWith("compareOutcomes:")) return "Karşılaştırma durumu"; - return { + const labels: Record = { nextOnStart: "Sonraki", nextOnTrue: "Doğru", nextOnFalse: "Yanlış", nextOnApprove: "Onay", nextOnReject: "Red", - }[field]; + }; + + return field ? labels[field] : undefined; } export function addLink( - links, - criteria, - source, - targetId, - label, - type, - sourcePort = null, + links: WorkflowLink[], + criteria: WorkflowCriteriaDto[], + source: WorkflowCriteriaDto, + targetId: string | null | undefined, + label: string, + type: string, + sourcePort: WorkflowLinkPort = {}, ) { if (!targetId) return; const target = criteria.find((item) => item.id === targetId); @@ -280,7 +342,10 @@ export function addLink( } } -export function emptyCriteria(kind = "Compare", workflowItemId = null) { +export function emptyCriteria( + kind = "Compare", + workflowItemId = "", +): WorkflowCriteriaForm { return { id: "", workflowItemId, @@ -305,7 +370,7 @@ export function emptyCriteria(kind = "Compare", workflowItemId = null) { }; } -export function toCriteriaForm(item) { +export function toCriteriaForm(item: WorkflowCriteriaDto): WorkflowCriteriaForm { const sharedPerson = item.approver || item.informPerson || ""; return { @@ -324,13 +389,13 @@ export function toCriteriaForm(item) { }; } -export function normalizeCriteria(item) { +export function normalizeCriteria(item: WorkflowCriteriaForm): SaveCriteriaInput { const sharedPerson = item.approver || item.informPerson || ""; return { ...item, id: item.id || null, - workflowItemId: item.workflowItemId || null, + workflowItemId: item.workflowItemId || "", compareValue: Number(item.compareValue || 0), approver: sharedPerson, informPerson: sharedPerson, @@ -343,17 +408,17 @@ export function normalizeCriteria(item) { }; } -export function defaultTitle(kind) { +export function defaultTitle(kind: string) { return { Start: "İş Akışı Başlat", Compare: "Tutar > 5000 TL", Approval: "Onaylanacak Kişi", Inform: "Bilgilendirme Yapılacak Personel", End: "Akışı Bitir", - }[kind]; + }[kind] ?? "İş Akışı Adımı"; } -export function emptyCompareOutcome(label = "Durum") { +export function emptyCompareOutcome(label = "Durum"): CompareOutcomeDto { return { label, targetId: "", @@ -361,7 +426,12 @@ export function emptyCompareOutcome(label = "Durum") { }; } -export function toCompareOutcomeForm(outcome) { +export function toCompareOutcomeForm( + outcome: Partial & + Partial & { + conditions?: Partial[]; + }, +): CompareOutcomeDto { const conditions = outcome.conditions?.length ? outcome.conditions : [ @@ -383,7 +453,12 @@ export function toCompareOutcomeForm(outcome) { }; } -export function normalizeCompareOutcome(outcome) { +export function normalizeCompareOutcome( + outcome: Partial & + Partial & { + conditions?: Partial[]; + }, +): CompareOutcomeDto { const conditions = ( outcome.conditions?.length ? outcome.conditions @@ -395,7 +470,10 @@ export function normalizeCompareOutcome(outcome) { }, ] ) - .filter((condition) => condition.operator && condition.compareValue !== "") + .filter( + (condition) => + condition.operator && String(condition.compareValue ?? "") !== "", + ) .map((condition) => ({ column: condition.column || "Tutar", operator: condition.operator || ">", @@ -403,13 +481,15 @@ export function normalizeCompareOutcome(outcome) { })); return { - label: outcome.label.trim(), + label: (outcome.label || "").trim(), targetId: outcome.targetId || null, conditions, }; } -export function compareOutcomeRuleText(outcome) { +export function compareOutcomeRuleText( + outcome: Partial & Partial, +) { const conditions = outcome.conditions?.length ? outcome.conditions : outcome.operator @@ -432,13 +512,13 @@ export function compareOutcomeRuleText(outcome) { : "Kural yok"; } -export function formatCompactValue(value) { +export function formatCompactValue(value: number | string | null | undefined) { return new Intl.NumberFormat("tr-TR", { maximumFractionDigits: 2, }).format(Number(value || 0)); } -export function criteriaSummary(item) { +export function criteriaSummary(item: WorkflowCriteriaDto) { if (item.kind === "Compare") { return ( (item.compareOutcomes || []) @@ -454,22 +534,25 @@ export function criteriaSummary(item) { return item.title; } -export function targetTitle(criteria, id) { +export function targetTitle( + criteria: WorkflowCriteriaDto[], + id?: string | null, +) { if (!id) return "-"; const item = criteria.find((candidate) => candidate.id === id); return item ? `${item.id} - ${item.title}` : id; } -export function statusClass(status) { +export function statusClass(status?: string) { if (status === "Onay Bekliyor") return "pending"; if (status === "Bitti") return "done"; if (status === "Bilgilendirildi") return "info"; return ""; } -export function formatMoney(value) { +export function formatMoney(value?: number | string | null) { return new Intl.NumberFormat("tr-TR", { style: "currency", currency: "TRY", - }).format(value || 0); + }).format(Number(value || 0)); } diff --git a/ui/src/views/admin/listForm/edit/FormEdit.tsx b/ui/src/views/admin/listForm/edit/FormEdit.tsx index 2647d3f..8bff8f9 100644 --- a/ui/src/views/admin/listForm/edit/FormEdit.tsx +++ b/ui/src/views/admin/listForm/edit/FormEdit.tsx @@ -53,6 +53,7 @@ import FormTabRow from './FormTabRow' import FormTabGantt from './FormTabGantt' import FormTabScheduler from './FormTabScheduler' import { APP_NAME } from '@/constants/app.constant' +import { FormTabWorkflow } from './FormTabWorkflow' export interface FormEditProps { onSubmit: ( @@ -271,6 +272,9 @@ const FormEdit = () => { {visibleTabs.includes('widget') && ( {translate('::ListForms.ListFormEdit.TabWidgets')} )} + {visibleTabs.includes('workflow') && ( + {translate('::ListForms.ListFormEdit.TabWorkflow')} + )} {/* Chart Tabs */} {visibleTabs.includes('commonSettings') && ( @@ -387,6 +391,9 @@ const FormEdit = () => { + + + ([]); + const [criteria, setCriteria] = useState([]); + const [selectedWorkflowId, setSelectedWorkflowId] = useState( + null, + ); const [selectedId, setSelectedId] = useState(""); - const [pendingLink, setPendingLink] = useState(null); - const [workflowForm, setWorkflowForm] = useState({ + const [pendingLink, setPendingLink] = useState(null); + const [workflowForm, setWorkflowForm] = useState({ sorumlu: "", amount: 7200, }); - const [editingWorkflowId, setEditingWorkflowId] = useState(null); - const [workflowEditForm, setWorkflowEditForm] = useState({ + const [editingWorkflowId, setEditingWorkflowId] = useState( + null, + ); + const [workflowEditForm, setWorkflowEditForm] = useState({ sorumlu: "", tarih: "", amount: 0, }); - const [criteriaForm, setCriteriaForm] = useState(emptyCriteria()); - const [dragPreview, setDragPreview] = useState(null); + const [criteriaForm, setCriteriaForm] = useState( + emptyCriteria(), + ); + const [dragPreview, setDragPreview] = useState(null); const [canvasZoom, setCanvasZoom] = useState(1); const [designerTab, setDesignerTab] = useState("flow"); const [busy, setBusy] = useState(false); const [approvalDialogWorkflowId, setApprovalDialogWorkflowId] = - useState(null); - const canvasRef = useRef(null); + useState(null); + const canvasRef = useRef(null); const currentCriteria = useMemo( () => criteria.filter((item) => item.workflowItemId === selectedWorkflowId), @@ -74,7 +110,7 @@ export function Dashboard() { }, []); const runAction = useCallback( - async (action) => { + async (action: () => Promise) => { setBusy(true); try { await action(); @@ -120,7 +156,7 @@ export function Dashboard() { } }, [approvalDialogWorkflowId, pendingItems]); - const createWorkflow = (event) => { + const createWorkflow = (event: FormEvent) => { event.preventDefault(); runAction(async () => { const created = await workflowService.createWorkflow({ @@ -134,7 +170,7 @@ export function Dashboard() { }); }; - const beginWorkflowEdit = (item) => { + const beginWorkflowEdit = (item: WorkflowItemDto) => { setSelectedWorkflowId(item.id); setPendingLink(null); setSelectedId(""); @@ -151,7 +187,7 @@ export function Dashboard() { setWorkflowEditForm({ sorumlu: "", tarih: "", amount: 0 }); }; - const saveWorkflowEdit = (id) => { + const saveWorkflowEdit = (id: string) => { runAction(async () => { await workflowService.updateWorkflow(id, { sorumlu: workflowEditForm.sorumlu, @@ -163,7 +199,7 @@ export function Dashboard() { }; const startWorkflow = useCallback( - (id) => { + (id: string) => { runAction(async () => { await workflowService.startWorkflow(id); const data = await loadState(); @@ -179,7 +215,7 @@ export function Dashboard() { [loadState, runAction], ); - const saveCriteria = (event) => { + const saveCriteria = (event: FormEvent) => { event.preventDefault(); runAction(async () => { await workflowService.saveCriteria(normalizeCriteria(criteriaForm)); @@ -187,7 +223,7 @@ export function Dashboard() { }); }; - const addCriteria = (kind) => { + const addCriteria = (kind: string) => { if (!selectedWorkflowId) return; setDesignerTab("flow"); @@ -202,7 +238,7 @@ export function Dashboard() { }; const deleteSelectedCriteria = useCallback( - (criteriaId = selectedId) => { + (criteriaId: string = selectedId) => { if (!criteriaId || busy) return; runAction(async () => { @@ -214,17 +250,17 @@ export function Dashboard() { ); const disconnectLink = useCallback( - (sourceId, outcome) => { + (sourceId: string, outcome: string) => { if (!sourceId || !outcome || busy) return; const source = currentCriteria.find((item) => item.id === sourceId); if (!source) return; - const next = { ...source }; + const next: WorkflowCriteriaForm = toCriteriaForm(source); if (outcome.startsWith("compareOutcomes:")) { const outcomeIndex = Number(outcome.split(":")[1]); next.compareOutcomes = [...(source.compareOutcomes || [])]; - if (next.compareOutcomes[outcomeIndex]) { + if (next.compareOutcomes?.[outcomeIndex]) { next.compareOutcomes[outcomeIndex] = { ...next.compareOutcomes[outcomeIndex], targetId: null, @@ -233,7 +269,7 @@ export function Dashboard() { if (outcomeIndex === 0) next.nextOnTrue = null; if (outcomeIndex === 1) next.nextOnFalse = null; } else { - next[outcome] = null; + (next as Record)[outcome] = null; } runAction(async () => { @@ -246,10 +282,10 @@ export function Dashboard() { ); useEffect(() => { - const deleteWithKeyboard = (event) => { + const deleteWithKeyboard = (event: globalThis.KeyboardEvent) => { const activeTag = document.activeElement?.tagName?.toLowerCase(); const isEditing = - ["input", "textarea", "select"].includes(activeTag) || + Boolean(activeTag && ["input", "textarea", "select"].includes(activeTag)) || (document.activeElement instanceof HTMLElement && document.activeElement.isContentEditable); @@ -267,7 +303,7 @@ export function Dashboard() { return () => window.removeEventListener("keydown", deleteWithKeyboard); }, [deleteSelectedCriteria, disconnectLink, pendingLink]); - const updateNodePosition = ({ active, delta }) => { + const updateNodePosition = ({ active, delta }: DragEndEvent) => { setDragPreview(null); const item = currentCriteria.find( @@ -290,11 +326,11 @@ export function Dashboard() { }); }; - const connectNodes = (sourceId, outcome, targetId) => { + const connectNodes = (sourceId: string, outcome: string, targetId: string) => { const source = currentCriteria.find((item) => item.id === sourceId); if (!source || source.id === targetId) return; - const next = { ...source }; + const next: WorkflowCriteriaForm = toCriteriaForm(source); if (outcome.startsWith("compareOutcomes:")) { const outcomeIndex = Number(outcome.split(":")[1]); next.compareOutcomes = [...(source.compareOutcomes || [])]; @@ -305,7 +341,7 @@ export function Dashboard() { if (outcomeIndex === 0) next.nextOnTrue = targetId; if (outcomeIndex === 1) next.nextOnFalse = targetId; } else { - next[outcome] = targetId; + (next as Record)[outcome] = targetId; } setPendingLink(null); @@ -340,13 +376,13 @@ export function Dashboard() { }); }; - const selectWorkflow = (item) => { + const selectWorkflow = (item: WorkflowItemDto) => { setSelectedWorkflowId(item.id); setPendingLink(null); setSelectedId(""); }; - const openCriteriaDetails = (id) => { + const openCriteriaDetails = (id: string) => { setSelectedId(id); setPendingLink(null); setDesignerTab("criteria"); @@ -357,7 +393,7 @@ export function Dashboard() { setSelectedId(""); }; - const beginLink = (sourceId, outcome) => { + const beginLink = (sourceId: string, outcome: string) => { setPendingLink({ sourceId, outcome }); setSelectedId(sourceId); }; @@ -391,12 +427,12 @@ export function Dashboard() { onCloseApprovalDialog={() => setApprovalDialogWorkflowId(null)} onConnectNodes={connectNodes} onCreateWorkflow={createWorkflow} - onDecision={(id, approved, note) => + onDecision={(id: string, approved: boolean, note: string) => runAction(() => workflowService.decideWorkflow(id, { approved, note })) } onDeleteSelectedCriteria={deleteSelectedCriteria} onDisconnectLink={disconnectLink} - onDragMove={(event) => + onDragMove={(event: DragEndEvent | null) => setDragPreview( event ? { id: event.active.id, delta: event.delta } : null, ) diff --git a/ui/src/views/admin/listForm/workflow/ApprovalDialog.tsx b/ui/src/views/admin/listForm/workflow/ApprovalDialog.tsx index 42cce66..32a3dc8 100644 --- a/ui/src/views/admin/listForm/workflow/ApprovalDialog.tsx +++ b/ui/src/views/admin/listForm/workflow/ApprovalDialog.tsx @@ -1,12 +1,30 @@ import { formatMoney } from "@/utils/workflow/workflowHelpers"; import { useState } from "react"; import { FiCheck, FiSlash, FiX } from "react-icons/fi"; +import type { + WorkflowCriteriaDto, + WorkflowItemDto, +} from "@/services/workflow.service"; const CloseIcon = FiX as any; const CheckIcon = FiCheck as any; const SlashIcon = FiSlash as any; -export function ApprovalDialog({ busy, criteria, items, onClose, onDecision }) { +type ApprovalDialogProps = { + busy: boolean; + criteria: WorkflowCriteriaDto[]; + items: WorkflowItemDto[]; + onClose: () => void; + onDecision: (id: string, approved: boolean, note: string) => void; +}; + +export function ApprovalDialog({ + busy, + criteria, + items, + onClose, + onDecision, +}: ApprovalDialogProps) { if (!items.length) return null; return ( @@ -59,8 +77,8 @@ function PendingApprovals({ busy, onDecision, showChrome = true, -}) { - const [notes, setNotes] = useState({}); +}: Omit & { showChrome?: boolean }) { + const [notes, setNotes] = useState>({}); const content = ( <> diff --git a/ui/src/views/admin/listForm/workflow/CriteriaTable.tsx b/ui/src/views/admin/listForm/workflow/CriteriaTable.tsx index 0c489ab..a25262b 100644 --- a/ui/src/views/admin/listForm/workflow/CriteriaTable.tsx +++ b/ui/src/views/admin/listForm/workflow/CriteriaTable.tsx @@ -13,10 +13,29 @@ import { emptyCompareOutcome, targetTitle, } from "@/utils/workflow/workflowHelpers"; +import type { + CompareOutcomeDto, + WorkflowCriteriaDto, + WorkflowItemDto, +} from "@/services/workflow.service"; const SaveIcon = FiSave as any; const TrashIcon = FiTrash2 as any; +type CriteriaTableProps = { + criteria: WorkflowCriteriaDto[]; + selectedWorkflow?: WorkflowItemDto | null; + selectedId: string; + activeNodeId?: string; + form: any; + busy: boolean; + onSelect: (id: string) => void; + onChange: (form: any) => void; + onSubmit: (event: React.FormEvent) => void; + onDelete: (id: string) => void; + onAddCriteria?: (kind: string) => void; +}; + export function CriteriaTable({ criteria, selectedWorkflow, @@ -29,27 +48,32 @@ export function CriteriaTable({ onSubmit, onDelete, onAddCriteria, -}) { - const setField = (name, value) => onChange({ ...form, [name]: value }); +}: CriteriaTableProps) { + const setField = (name: string, value: unknown) => + onChange({ ...form, [name]: value }); const targetOptions = [ { value: "", label: "Bağlantı yok" }, ...criteria .filter((item) => item.id !== form.id) .map((item) => ({ value: item.id, label: `${item.id} - ${item.title}` })), ]; - const updateCompareOutcome = (index, patch) => { + const updateCompareOutcome = (index: number, patch: Partial) => { const next = [...(form.compareOutcomes || [])]; next[index] = { ...next[index], ...patch }; setField("compareOutcomes", next); }; - const updateCompareCondition = (outcomeIndex, conditionIndex, patch) => { + const updateCompareCondition = ( + outcomeIndex: number, + conditionIndex: number, + patch: Record, + ) => { const next = [...(form.compareOutcomes || [])]; const conditions = [...(next[outcomeIndex]?.conditions || [])]; conditions[conditionIndex] = { ...conditions[conditionIndex], ...patch }; next[outcomeIndex] = { ...next[outcomeIndex], conditions }; setField("compareOutcomes", next); }; - const addCompareCondition = (outcomeIndex) => { + const addCompareCondition = (outcomeIndex: number) => { const next = [...(form.compareOutcomes || [])]; next[outcomeIndex] = { ...next[outcomeIndex], @@ -60,23 +84,27 @@ export function CriteriaTable({ }; setField("compareOutcomes", next); }; - const removeCompareCondition = (outcomeIndex, conditionIndex) => { + const removeCompareCondition = (outcomeIndex: number, conditionIndex: number) => { const next = [...(form.compareOutcomes || [])]; const conditions = (next[outcomeIndex]?.conditions || []).filter( - (_, index) => index !== conditionIndex, + (_: unknown, index: number) => index !== conditionIndex, ); next[outcomeIndex] = { ...next[outcomeIndex], conditions }; setField("compareOutcomes", next); }; - const removeCompareOutcome = (index) => { + const removeCompareOutcome = (index: number) => { setField( "compareOutcomes", (form.compareOutcomes || []).filter( - (_, itemIndex) => itemIndex !== index, + (_: unknown, itemIndex: number) => itemIndex !== index, ), ); }; - const targetSelect = (value, onSelectTarget, required = false) => ( + const targetSelect = ( + value: string | null | undefined, + onSelectTarget: (value: string) => void, + required = false, + ) => (